Compare commits

..

74 Commits

Author SHA1 Message Date
zsyg
a777991b8c 添加ClamAV图标 2025-07-05 19:34:02 +08:00
zsyg
e2c6c52b32 Add files via upload 2025-07-05 19:33:25 +08:00
zsyg
33089c39b6 Update features.html 2025-07-05 19:15:03 +08:00
zsyg
45805178cc 修改版本号 2025-07-05 19:09:08 +08:00
zsyg
08a11f025a 修复bug和扩展功能 2025-07-05 19:08:44 +08:00
zsyg
5bb3886bc5 添加乌班图的图标 2025-07-05 17:58:10 +08:00
zsyg
ce5f964776 添加应用卡片 2025-07-05 17:57:27 +08:00
zsyg
f52c7908d7 添加应用卡片图标 2025-07-05 17:32:23 +08:00
zsyg
abcbf06493 添加应用卡片图标 2025-07-05 17:32:04 +08:00
zsyg
f56bcb3627 添加应用卡片图标 2025-07-05 17:31:40 +08:00
zsyg
f789c7904a 修改版本号 2025-07-05 17:30:57 +08:00
zsyg
e04709637c 修改版本号 2025-07-05 17:30:38 +08:00
zsyg
e39f976607 添加更多应用卡片 2025-07-05 17:30:18 +08:00
zsyg
0d9ec0ad44 修改版本号 2025-07-05 16:44:38 +08:00
zsyg
122ada92d9 修改版本号 2025-07-05 16:44:11 +08:00
zsyg
d571729c89 修改版本号 2025-07-05 16:43:38 +08:00
zsyg
351e1e97c3 修改布局 2025-07-05 16:43:14 +08:00
zsyg
cff4c39e8c 添加版本号 2025-07-05 14:42:42 +08:00
zsyg
5f41f57e8d Add files via upload 2025-07-05 14:41:38 +08:00
zsyg
f2756ddbd8 添加图标 2025-07-05 14:40:40 +08:00
zsyg
d7a90ca422 添加notepad--图标 2025-07-05 14:30:46 +08:00
zsyg
7404bdfb5d 添加notepad-- 2025-07-05 14:28:40 +08:00
zsyg
5e8de310df 添加图标提取器图标 2025-07-05 14:17:32 +08:00
zsyg
f3cca9b3a1 添加图标提取器代码 2025-07-05 14:16:38 +08:00
zsyg
2154f465b7 Add files via upload 2025-07-05 14:16:00 +08:00
zsyg
99bbda4668 添加版本号 2025-07-05 10:24:08 +08:00
zsyg
d1d69da3e3 Add files via upload 2025-07-05 10:22:19 +08:00
zsyg
4c8cb807d9 添加ollama 2025-07-05 10:20:04 +08:00
zsyg
1ad64feab9 添加ollama 2025-07-05 10:19:19 +08:00
zsyg
ef7c582c50 删除视频压缩工具 2025-07-02 20:14:29 +08:00
zsyg
ee65689048 Add files via upload 2025-07-02 18:01:12 +08:00
zsyg
4ef8099054 添加自启动工具 2025-07-02 18:00:22 +08:00
zsyg
6dd8819f22 Add files via upload 2025-07-02 17:59:18 +08:00
zsyg
59900081da Create dotnet.yml 2025-07-02 17:19:33 +08:00
zsyg
2c60d0b970 Create dotnet-desktop.yml 2025-07-02 17:17:38 +08:00
zsyg
815ba41bdc 提高代码质量 2025-07-02 16:53:47 +08:00
zsyg
459c0bc9d7 Add files via upload 2025-07-02 16:05:28 +08:00
zsyg
7c78a118a9 解决非win环境下编译问题 2025-07-02 16:04:10 +08:00
zsyg
d5f944280e Add files via upload 2025-07-02 11:11:16 +08:00
zsyg
1cd722bf89 Add files via upload 2025-07-02 11:10:09 +08:00
zsyg
e34a954777 添加艺术字 2025-07-02 11:09:21 +08:00
zsyg
125bf6b0d4 移动位置 2025-07-02 10:29:36 +08:00
zsyg
983a0d5bf4 修改位置 2025-07-02 10:28:33 +08:00
zsyg
259b075541 允许自定义下载和修改编译路径 2025-07-02 10:25:17 +08:00
zsyg
d454ac0fdc 允许自定义下载路径 2025-07-02 10:10:02 +08:00
zsyg
d5c83d854c 删除没有必要的文件 2025-07-02 09:17:44 +08:00
zsyg
1c2bc713be 提高代码质量 2025-07-02 09:16:18 +08:00
zsyg
1539c665f0 提供代码质量 2025-07-02 09:15:43 +08:00
zsyg
074e55fbbc Add files via upload 2025-07-02 09:14:43 +08:00
zsyg
334fa56070 修复代码潜在问题 2025-07-02 09:12:49 +08:00
zsyg
0268e13b56 Add files via upload 2025-07-01 19:02:06 +08:00
zsyg
cba6c9eeca Update README.md 2025-07-01 11:31:02 +08:00
zsyg
70a776125a 修改密码生成器代码位置 2025-07-01 10:34:32 +08:00
zsyg
f7250dae08 修复主题系统带来的颜色 2025-07-01 10:26:26 +08:00
zsyg
ac93c8418f Add files via upload 2025-06-30 20:25:25 +08:00
zsyg
5a49714ed7 Add files via upload 2025-06-30 20:22:07 +08:00
zsyg
19056a1a8c Add files via upload 2025-06-30 20:21:19 +08:00
zsyg
7c4250f912 Add files via upload 2025-06-30 20:20:48 +08:00
zsyg
d056c24a1b Add files via upload 2025-06-30 20:20:21 +08:00
zsyg
5651e944f9 Add files via upload 2025-06-30 15:46:54 +08:00
zsyg
1dbd9968c9 Add files via upload 2025-06-30 15:46:31 +08:00
zsyg
e387d22fee Add files via upload 2025-06-30 15:44:21 +08:00
zsyg
68bd471bd2 Add files via upload 2025-06-30 15:43:54 +08:00
zsyg
53392a2ce8 Add files via upload 2025-06-29 20:23:29 +08:00
zsyg
5ecfe2da2a Add files via upload 2025-06-29 13:04:34 +08:00
zsyg
5b9532acfe Add files via upload 2025-06-29 13:03:57 +08:00
zsyg
c87d071ee0 Update README.md 2025-06-29 09:58:30 +08:00
zsyg
d54bd4c353 Add files via upload 2025-06-29 09:47:07 +08:00
zsyg
24198b2e09 Add files via upload 2025-06-29 09:46:39 +08:00
zsyg
0a0ef24497 Add files via upload 2025-06-29 09:46:05 +08:00
zsyg
96ab4bc726 添加innosetup脚本 2025-06-29 08:01:13 +08:00
zsyg
be87aaa0e6 Update README.md 2025-06-28 18:27:22 +08:00
zsyg
e8bef7e396 Update README.md 2025-06-28 18:23:41 +08:00
zsyg
7f06c9b6b1 Update README.md 2025-06-28 18:22:59 +08:00
97 changed files with 5511 additions and 2223 deletions

115
.github/workflows/dotnet-desktop.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build, test, sign and package a WPF or Windows Forms desktop application
# built on .NET Core.
# To learn how to migrate your existing application to .NET Core,
# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework
#
# To configure this workflow:
#
# 1. Configure environment variables
# GitHub sets default environment variables for every workflow run.
# Replace the variables relative to your project in the "env" section below.
#
# 2. Signing
# Generate a signing certificate in the Windows Application
# Packaging Project or add an existing signing certificate to the project.
# Next, use PowerShell to encode the .pfx file using Base64 encoding
# by running the following Powershell script to generate the output string:
#
# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte
# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt'
#
# Open the output file, SigningCertificate_Encoded.txt, and copy the
# string inside. Then, add the string to the repo as a GitHub secret
# and name it "Base64_Encoded_Pfx."
# For more information on how to configure your signing certificate for
# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing
#
# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key".
# See "Build the Windows Application Packaging project" below to see how the secret is used.
#
# For more information on GitHub Actions, refer to https://github.com/features/actions
# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications,
# refer to https://github.com/microsoft/github-actions-for-desktop-apps
name: .NET Core Desktop
on:
push:
分支: [ "main" ]
pull_request:
分支: [ "main" ]
jobs:
build:
strategy:
matrix:
configuration: [Debug, Release]
runs-on: windows-latest # For a list of available runner types, refer to
# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
env:
Solution_Name: your-solution-name # Replace with your solution name, i.e. MyWpfApp.sln.
Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj.
Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package.
Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj.
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Install the .NET Core workload
- name: Install .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
# Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# Execute all unit tests in the solution
- name: Execute unit tests
run: dotnet test
# Restore the application to populate the obj folder with RuntimeIdentifiers
- name: Restore the application
run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
env:
Configuration: ${{ matrix.configuration }}
# Decode the base 64 encoded pfx and save the Signing_Certificate
- name: Decode the pfx
run: |
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}")
$certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx
[IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte)
# Create the app package by building and packaging the Windows Application Packaging project
- name: Create the app package
run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }}
env:
Appx_Bundle: Always
Appx_Bundle_Platforms: x86|x64
Appx_Package_Build_Mode: StoreUpload
Configuration: ${{ matrix.configuration }}
# Remove the pfx
- name: Remove the pfx
run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx
# Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: MSIX Package
path: ${{ env.Wap_Project_Directory }}\AppPackages

28
.github/workflows/dotnet.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET
on:
push:
分支: [ "main" ]
pull_request:
分支: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -17,12 +23,13 @@ namespace AppStore
private void InitializeComponent() private void InitializeComponent()
{ {
this.Dock = DockStyle.Fill; this.Dock = DockStyle.Fill;
this.BackColor = Color.White; this.BackColor = ThemeManager.BackgroundColor;
this.Padding = new Padding(20); this.Padding = new Padding(20);
// 创建主布局面板 // 创建主布局面板
TableLayoutPanel mainLayout = new TableLayoutPanel(); TableLayoutPanel mainLayout = new TableLayoutPanel();
mainLayout.Dock = DockStyle.Fill; mainLayout.Dock = DockStyle.Fill;
mainLayout.BackColor = ThemeManager.BackgroundColor;
mainLayout.ColumnCount = 1; mainLayout.ColumnCount = 1;
mainLayout.RowCount = 2; mainLayout.RowCount = 2;
mainLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); mainLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
@@ -50,7 +57,7 @@ namespace AppStore
// 初始化并添加应用信息 // 初始化并添加应用信息
infoLabel = new Label(); infoLabel = new Label();
infoLabel.Text = "kortapp-z\n版本: 1.0.7\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件"; infoLabel.Text = "kortapp-z\n版本: 1.2.4\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件";
infoLabel.Font = new Font("Microsoft YaHei", 12); infoLabel.Font = new Font("Microsoft YaHei", 12);
infoLabel.AutoSize = false; infoLabel.AutoSize = false;
infoLabel.Width = 300; infoLabel.Width = 300;
@@ -68,6 +75,7 @@ namespace AppStore
// 在底部添加GitHub链接区域 // 在底部添加GitHub链接区域
TableLayoutPanel githubPanel = new TableLayoutPanel(); TableLayoutPanel githubPanel = new TableLayoutPanel();
githubPanel.Dock = DockStyle.Bottom; githubPanel.Dock = DockStyle.Bottom;
githubPanel.BackColor = ThemeManager.BackgroundColor;
githubPanel.Height = 60; githubPanel.Height = 60;
githubPanel.ColumnCount = 3; githubPanel.ColumnCount = 3;
githubPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); githubPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
@@ -109,6 +117,7 @@ namespace AppStore
// 创建包含图标和文字的面板 // 创建包含图标和文字的面板
Panel linkPanel = new Panel(); Panel linkPanel = new Panel();
linkPanel.AutoSize = true; linkPanel.AutoSize = true;
linkPanel.BackColor = ThemeManager.BackgroundColor;
linkPanel.Controls.Add(githubIcon); linkPanel.Controls.Add(githubIcon);
linkPanel.Controls.Add(githubLabel); linkPanel.Controls.Add(githubLabel);
githubIcon.Location = new Point(0, 0); githubIcon.Location = new Point(0, 0);

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -13,7 +19,9 @@ namespace AppStore
{ {
private PictureBox iconBox; private PictureBox iconBox;
private Label nameLabel; private Label nameLabel;
private Panel namePanel;
private Button downloadBtn; private Button downloadBtn;
private Color borderColor = SystemColors.ControlDark;
private static readonly ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath> PathCache = private static readonly ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath> PathCache =
new ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath>(); new ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath>();
@@ -27,6 +35,7 @@ namespace AppStore
// 确保关键对象不为null // 确保关键对象不为null
iconBox = new PictureBox() { SizeMode = PictureBoxSizeMode.StretchImage }; iconBox = new PictureBox() { SizeMode = PictureBoxSizeMode.StretchImage };
nameLabel = new Label() { Text = string.Empty }; nameLabel = new Label() { Text = string.Empty };
namePanel = new Panel();
downloadBtn = new Button() { Text = "下载" }; downloadBtn = new Button() { Text = "下载" };
// 确保DownloadManager已初始化 // 确保DownloadManager已初始化
@@ -45,51 +54,142 @@ namespace AppStore
this.Padding = new Padding(10); this.Padding = new Padding(10);
// 异步初始化卡片路径和边框 // 异步初始化卡片路径和边框
// 预加载边框路径
Task.Run(() => { Task.Run(() => {
InitializeCardPath(); InitializeCardPath();
InitializeBorder(); InitializeBorder();
// 确保在主线程注册事件
this.Invoke((MethodInvoker)(() => {
this.Paint += (sender, e) => {
if (BorderCache.IsEmpty)
{
Task.Run(() => {
InitializeBorder();
this.Invoke((MethodInvoker)(() => this.Invalidate()));
});
}
};
}));
}); });
// 应用图标 // 应用图标 - 添加null检查
iconBox = new PictureBox(); if (iconBox != null && this != null && this.Controls != null)
iconBox.Size = new Size(80, 80); {
iconBox.Location = new Point((Width - 80) / 2, 15); iconBox.Size = new Size(80, 80);
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; iconBox.Location = new Point((Width - 80) / 2, 15);
this.Controls.Add(iconBox); iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(iconBox);
}
else
{
Logger.LogWarning("iconBox或Controls为null");
}
// 应用名称 // 应用名称 - 使用Panel包裹Label实现边框颜色
if (namePanel != null)
{
namePanel.Size = new Size(Width - 20, 40);
namePanel.Location = new Point(10, 100);
namePanel.Paint += (sender, e) => {
try
{
if (e != null && e.Graphics != null && namePanel != null)
{
var rect = namePanel.ClientRectangle;
if (rect.Width > 0 && rect.Height > 0)
{
ControlPaint.DrawBorder(e.Graphics, rect,
borderColor, ButtonBorderStyle.Solid);
}
}
}
catch (Exception ex)
{
Logger.LogWarning($"绘制namePanel边框失败: {ex.Message}");
}
};
}
nameLabel = new Label(); nameLabel = new Label();
nameLabel.AutoSize = false; nameLabel.Dock = DockStyle.Fill;
nameLabel.Size = new Size(Width - 20, 40);
nameLabel.Location = new Point(10, 100);
nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold); nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold);
nameLabel.TextAlign = ContentAlignment.MiddleCenter; nameLabel.TextAlign = ContentAlignment.MiddleCenter;
this.Controls.Add(nameLabel);
if (namePanel != null && nameLabel != null)
{
namePanel.Controls.Add(nameLabel);
}
// 初始主题设置
UpdateLabelTheme();
// 订阅主题变化事件
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
if (this != null && this.Controls != null && namePanel != null)
{
this.Controls.Add(namePanel);
}
// 下载按钮 // 下载按钮 - 添加null检查
downloadBtn = new Button(); if (downloadBtn != null)
downloadBtn.Text = "下载"; {
downloadBtn.Size = new Size(100, 32); downloadBtn.Text = "下载";
downloadBtn.Location = new Point((Width - 100) / 2, 150); downloadBtn.Size = new Size(100, 32);
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); downloadBtn.Location = new Point((Width - 100) / 2, 150);
downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat;
downloadBtn.FlatAppearance.BorderSize = 0;
downloadBtn.Cursor = Cursors.Hand;
downloadBtn.Font = new Font("Microsoft YaHei", 9);
// 按钮悬停效果
downloadBtn.MouseEnter += (s, e) => {
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
};
downloadBtn.MouseLeave += (s, e) => {
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
}; downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat;
downloadBtn.Click += DownloadBtn_Click; downloadBtn.FlatAppearance.BorderSize = 0;
this.Controls.Add(downloadBtn); downloadBtn.Cursor = Cursors.Hand;
downloadBtn.Visible = ShowDownloadButton; downloadBtn.Font = new Font("Microsoft YaHei", 9);
// 按钮悬停效果 - 添加null检查
downloadBtn.MouseEnter += (s, e) => {
if (downloadBtn != null)
{
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
}
};
downloadBtn.MouseLeave += (s, e) => {
if (downloadBtn != null)
{
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
}
};
downloadBtn.Click += DownloadBtn_Click;
this.Controls.Add(downloadBtn);
downloadBtn.Visible = ShowDownloadButton;
}
}
private void UpdateLabelTheme()
{
if (ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Dark)
{
nameLabel.BackColor = Color.Black;
nameLabel.ForeColor = Color.White;
namePanel.BackColor = Color.Black;
borderColor = Color.White;
}
else
{
nameLabel.BackColor = Color.White;
nameLabel.ForeColor = Color.Black;
namePanel.BackColor = Color.White;
borderColor = SystemColors.ControlDark;
}
if (namePanel != null && !namePanel.IsDisposed)
{
namePanel.Invalidate(); // 触发重绘
}
else
{
Logger.LogWarning("namePanel为null或已释放");
}
} }
/// <summary> /// <summary>
@@ -101,52 +201,58 @@ namespace AppStore
// 使用卡片尺寸作为缓存键 // 使用卡片尺寸作为缓存键
string cacheKey = $"{Width}_{Height}_10"; string cacheKey = $"{Width}_{Height}_10";
// 检查缓存中是否已有路径 // 双重检查锁模式确保线程安全
if (!BorderCache.TryGetValue(cacheKey, out var borderPath)) if (!BorderCache.TryGetValue(cacheKey, out var borderPath))
{ {
// 创建临时文件存储路径数据 lock (BorderCache)
string tempFile = Path.GetTempFileName();
try
{ {
// 配置C++程序启动参数 if (!BorderCache.TryGetValue(cacheKey, out borderPath))
ProcessStartInfo startInfo = new ProcessStartInfo
{ {
FileName = Path.Combine(Application.StartupPath, "resource", "border_renderer.exe"), // 创建临时文件存储路径数据
Arguments = $"{Width} {Height} 10 \"{tempFile}\"", // 传递宽高和圆角半径 string tempFile = Path.GetTempFileName();
UseShellExecute = false, // 不显示命令行窗口 try
CreateNoWindow = true // 静默运行
};
// 启动C++程序计算路径
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
// 检查计算结果
if (process.ExitCode == 0 && File.Exists(tempFile))
{ {
// 读取C++程序生成的路径点 // 配置C++程序启动参数
var lines = File.ReadAllLines(tempFile); ProcessStartInfo startInfo = new ProcessStartInfo
PointF[] points = lines.Select(line => { {
var parts = line.Split(','); // 解析坐标点 FileName = Path.Combine(Application.StartupPath, "resource", "border_renderer.exe"),
return new PointF(float.Parse(parts[0]), float.Parse(parts[1])); Arguments = $"{Width} {Height} 10 \"{tempFile}\"", // 传递宽高和圆角半径
}).ToArray(); UseShellExecute = false, // 不显示命令行窗口
CreateNoWindow = true // 静默运行
// 创建GraphicsPath对象 };
borderPath = new System.Drawing.Drawing2D.GraphicsPath();
borderPath.AddLines(points); // 添加路径 // 启动C++程序计算路径
using (var process = Process.Start(startInfo))
// 缓存路径对象 {
BorderCache.TryAdd(cacheKey, borderPath); process.WaitForExit();
// 检查计算结果
if (process.ExitCode == 0 && File.Exists(tempFile))
{
// 读取C++程序生成的路径点
var lines = File.ReadAllLines(tempFile);
PointF[] points = lines.Select(line => {
var parts = line.Split(','); // 解析坐标点
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
}).ToArray();
// 创建GraphicsPath对象
borderPath = new System.Drawing.Drawing2D.GraphicsPath();
borderPath.AddLines(points); // 添加路径点
// 缓存路径对象
BorderCache.TryAdd(cacheKey, borderPath);
}
}
}
finally
{
// 确保临时文件被删除
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
} }
}
}
finally
{
// 确保临时文件被删除
if (File.Exists(tempFile))
{
File.Delete(tempFile);
} }
} }
} }
@@ -157,6 +263,12 @@ namespace AppStore
{ {
base.OnPaint(e); base.OnPaint(e);
// 确保边框已初始化
if (BorderCache.IsEmpty)
{
InitializeBorder();
}
// 绘制背景 // 绘制背景
using (var brush = new SolidBrush(this.BackColor)) { using (var brush = new SolidBrush(this.BackColor)) {
e.Graphics.FillRectangle(brush, this.ClientRectangle); e.Graphics.FillRectangle(brush, this.ClientRectangle);
@@ -219,22 +331,37 @@ namespace AppStore
}; };
// 启动C++程序计算路径 // 启动C++程序计算路径
using (var process = Process.Start(startInfo)) { if (startInfo != null)
process.WaitForExit(); {
using (var process = Process.Start(startInfo))
// 检查计算结果 {
if (process.ExitCode == 0 && File.Exists(tempFile)) { if (process != null)
// 读取生成的路径点 {
var lines = File.ReadAllLines(tempFile); process.WaitForExit();
PointF[] points = lines.Select(line => {
var parts = line.Split(','); // 解析坐标 // 检查计算结果
return new PointF(float.Parse(parts[0]), float.Parse(parts[1])); if (process.ExitCode == 0 && File.Exists(tempFile))
}).ToArray(); {
try
// 创建并缓存路径对象 {
path = new System.Drawing.Drawing2D.GraphicsPath(); // 读取生成的路径点
path.AddLines(points); var lines = File.ReadAllLines(tempFile);
PathCache.TryAdd(cacheKey, path); PointF[] points = lines.Select(line => {
var parts = line.Split(','); // 解析坐标
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
}).ToArray();
// 创建并缓存路径对象
path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddLines(points);
PathCache.TryAdd(cacheKey, path);
}
catch (Exception ex)
{
Logger.LogWarning($"读取路径点失败: {ex.Message}");
}
}
}
} }
} }
} catch { } catch {
@@ -310,8 +437,18 @@ namespace AppStore
public void UpdateDisplay() public void UpdateDisplay()
{ {
nameLabel.Text = AppName; if (nameLabel != null && AppName != null)
iconBox.Image = AppIcon; {
nameLabel.Text = AppName;
}
else
{
Logger.LogWarning("nameLabel或AppName为null");
}
if (iconBox != null && AppIcon != null)
{
iconBox.Image = AppIcon;
}
} }
private void DownloadBtn_Click(object sender, EventArgs e) private void DownloadBtn_Click(object sender, EventArgs e)
@@ -321,14 +458,15 @@ namespace AppStore
// 更严格的null检查 // 更严格的null检查
// 更严格的null检查包括DownloadManager.Instance和其方法 // 更严格的null检查包括DownloadManager.Instance和其方法
// 全面的null和状态检查 // 全面的null和状态检查
var downloadManager = DownloadManager.Instance;
if (sender == null || e == null || if (sender == null || e == null ||
string.IsNullOrWhiteSpace(DownloadUrl) || string.IsNullOrWhiteSpace(DownloadUrl) ||
string.IsNullOrWhiteSpace(AppName) || string.IsNullOrWhiteSpace(AppName) ||
!this.IsHandleCreated || !this.IsHandleCreated ||
this.IsDisposed || this.IsDisposed ||
DownloadManager.Instance == null || downloadManager == null ||
DownloadManager.Instance.DownloadItems == null || downloadManager.DownloadItems == null ||
DownloadManager.Instance.StartDownload == null) downloadManager.StartDownload == null)
{ {
return; return;
} }
@@ -336,7 +474,7 @@ namespace AppStore
string safeAppName = AppName ?? "未知应用"; string safeAppName = AppName ?? "未知应用";
string fileName = $"{safeAppName.Replace(" ", "_")}.exe"; string fileName = $"{safeAppName.Replace(" ", "_")}.exe";
DownloadManager.Instance.StartDownload(fileName, DownloadUrl); downloadManager.StartDownload(fileName, DownloadUrl);
string message = $"已开始下载: {safeAppName}"; string message = $"已开始下载: {safeAppName}";
this.Invoke((MethodInvoker)delegate { this.Invoke((MethodInvoker)delegate {

63
AppSearch.cs Normal file
View File

@@ -0,0 +1,63 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace AppStore
{
public static class AppSearch
{
/// <summary>
/// 搜索应用卡片
/// </summary>
/// <param name="flowPanel">包含应用卡片的FlowLayoutPanel</param>
/// <param name="searchText">搜索文本</param>
public static void SearchApps(FlowLayoutPanel flowPanel, string searchText)
{
if (flowPanel == null || string.IsNullOrWhiteSpace(searchText))
{
ShowAllApps(flowPanel);
return;
}
foreach (Control control in flowPanel.Controls)
{
if (control is AppCard appCard)
{
bool isMatch = IsMatchSearch(appCard.AppName, searchText);
control.Visible = isMatch;
}
}
}
/// <summary>
/// 显示所有应用卡片
/// </summary>
public static void ShowAllApps(FlowLayoutPanel? flowPanel)
{
if (flowPanel == null) return;
foreach (Control control in flowPanel.Controls)
{
control.Visible = true;
}
}
/// <summary>
/// 检查应用名称是否匹配搜索文本
/// </summary>
private static bool IsMatchSearch(string appName, string searchText)
{
if (string.IsNullOrEmpty(appName)) return false;
// 不区分大小写比较
return appName.Contains(searchText, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -1,36 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<NoWarn>CS8622</NoWarn> <NoWarn>CS8622</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<WarningsAsErrors>CS8618</WarningsAsErrors> <WarningsAsErrors>CS8618</WarningsAsErrors>
<ApplicationIcon>img\ico\icon.ico</ApplicationIcon> <ApplicationIcon>img\ico\icon.ico</ApplicationIcon>
<Platforms>x86;x64</Platforms> <Platforms>x86;x64</Platforms>
</PropertyGroup> <EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<ItemGroup>
<None Include="help.txt" /> <ItemGroup>
<None Include="resource\*.exe"> <None Include="help.txt" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="resource\*.exe">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<None Include="img\png\*.png"> </None>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="img\png\*.png">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<None Include="img\jpg\*.jpg"> </None>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="img\jpg\*.jpg">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<None Include="img\resource\png\*.png"> </None>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="img\resource\png\*.png">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<None Include="img\ico\*.ico"> </None>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="img\ico\*.ico">
</None> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ItemGroup> </None>
</ItemGroup>
</Project>
<ItemGroup>
<PackageReference Include="ZXing.Net" Version="0.16.9" />
</ItemGroup>
</Project>

View File

@@ -1,92 +1,131 @@
using System; // _ _
using System.Drawing; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.Windows.Forms; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
namespace AppStore //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
{ // |_| |_|
public class DownloadItem : UserControl using System;
{ using System.Drawing;
private Label nameLabel; using System.Windows.Forms;
private ProgressBar progressBar;
private Label statusLabel; namespace AppStore
private Button cancelBtn; {
public class DownloadItem : UserControl
public string FileName { get; set; } = string.Empty; {
public int Progress { get; set; } private Label nameLabel;
public string Status { get; set; } = string.Empty; private ProgressBar progressBar;
private Label statusLabel;
public DownloadItem() private Button cancelBtn;
{
nameLabel = new Label(); public string FileName { get; set; } = string.Empty;
progressBar = new ProgressBar(); public int Progress { get; set; }
statusLabel = new Label(); public string Status { get; set; } = string.Empty;
cancelBtn = new Button();
public DownloadItem()
InitializeComponent(); {
} nameLabel = new Label();
progressBar = new ProgressBar();
private void InitializeComponent() statusLabel = new Label();
{ cancelBtn = new Button();
this.Size = new Size(400, 60);
this.BackColor = Color.White; InitializeComponent();
this.BorderStyle = BorderStyle.FixedSingle;
// 监听主题变化
// 文件名标签 ThemeManager.ThemeChanged += (theme) => {
nameLabel = new Label(); this.Invoke((MethodInvoker)delegate {
nameLabel.AutoSize = true; ApplyTheme();
nameLabel.Location = new Point(10, 10); });
nameLabel.Font = new Font("Microsoft YaHei", 9, FontStyle.Bold); };
this.Controls.Add(nameLabel); }
// 进度条 private void ApplyTheme()
progressBar = new ProgressBar(); {
progressBar.Size = new Size(200, 20); this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
progressBar.Location = new Point(10, 30); ? Color.White
this.Controls.Add(progressBar); : Color.Black;
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
// 状态标签 ? Color.Black
statusLabel = new Label(); : Color.White;
statusLabel.AutoSize = true;
statusLabel.Location = new Point(220, 30); cancelBtn.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
statusLabel.Font = new Font("Microsoft YaHei", 8); ? SystemColors.Control
this.Controls.Add(statusLabel); : Color.FromArgb(70, 70, 70);
cancelBtn.ForeColor = ThemeManager.TextColor;
// 取消按钮 }
cancelBtn = new Button();
cancelBtn.Text = "取消"; private void InitializeComponent()
cancelBtn.Size = new Size(60, 25); {
cancelBtn.Location = new Point(320, 30); this.Size = new Size(400, 60);
cancelBtn.Click += CancelBtn_Click; this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
this.Controls.Add(cancelBtn); ? Color.White
} : Color.Black;
this.BorderStyle = BorderStyle.FixedSingle;
public void UpdateDisplay() this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
{ ? Color.Black
nameLabel.Text = FileName; : Color.White;
progressBar.Value = Progress;
statusLabel.Text = Status; // 文件名标签
} nameLabel = new Label();
nameLabel.AutoSize = true;
private void CancelBtn_Click(object sender, EventArgs e) nameLabel.Location = new Point(10, 10);
{ nameLabel.Font = new Font("Microsoft YaHei", 9, FontStyle.Bold);
if (sender == null || e == null) return; this.Controls.Add(nameLabel);
if (InvokeRequired)
{ // 进度条
Invoke(new EventHandler(CancelBtn_Click), sender, e); progressBar = new ProgressBar();
return; progressBar.Size = new Size(200, 20);
} progressBar.Location = new Point(10, 30);
this.Controls.Add(progressBar);
try
{ // 状态标签
DownloadManager.Instance.CancelDownload(this); statusLabel = new Label();
Status = "已取消"; statusLabel.AutoSize = true;
UpdateDisplay(); statusLabel.Location = new Point(220, 30);
} statusLabel.Font = new Font("Microsoft YaHei", 8);
catch (Exception ex) this.Controls.Add(statusLabel);
{
Status = $"取消失败: {ex.Message}"; // 取消按钮
UpdateDisplay(); cancelBtn = new Button();
} cancelBtn.Text = "取消";
} cancelBtn.Size = new Size(60, 25);
} cancelBtn.Location = new Point(320, 30);
} cancelBtn.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? SystemColors.Control
: Color.FromArgb(70, 70, 70);
cancelBtn.ForeColor = ThemeManager.TextColor;
cancelBtn.FlatStyle = FlatStyle.Flat;
cancelBtn.FlatAppearance.BorderSize = 0;
cancelBtn.Click += CancelBtn_Click;
this.Controls.Add(cancelBtn);
}
public void UpdateDisplay()
{
nameLabel.Text = FileName;
progressBar.Value = Progress;
statusLabel.Text = Status;
}
private void CancelBtn_Click(object sender, EventArgs e)
{
if (sender == null || e == null) return;
if (InvokeRequired)
{
Invoke(new EventHandler(CancelBtn_Click), sender, e);
return;
}
try
{
DownloadManager.Instance.CancelDownload(this);
Status = "已取消";
UpdateDisplay();
}
catch (Exception ex)
{
Status = $"取消失败: {ex.Message}";
UpdateDisplay();
}
}
}
}

View File

@@ -1,346 +1,514 @@
using System; // _ _
using System.Collections.Generic; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.Diagnostics; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
using System.IO; //| | (_) | | | || (_| | |_) | |_) |_____/ /
using System.Runtime.InteropServices; //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
using System.Threading.Tasks; // |_| |_|
using System.Windows.Forms; using System;
using System.Collections.Generic;
namespace AppStore using System.Diagnostics;
{ using System.IO;
public class DownloadManager using System.Runtime.InteropServices;
{ using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Forms;
[DllImport("shell32.dll")]
private static extern int SHGetKnownFolderPath( namespace AppStore
[MarshalAs(UnmanagedType.LPStruct)] Guid rfid, {
uint dwFlags, public class DownloadManager
IntPtr hToken, {
out IntPtr ppszPath);
[DllImport("shell32.dll")]
private static DownloadManager instance = null!; private static extern int SHGetKnownFolderPath(
public static DownloadManager Instance => instance ??= new DownloadManager(); [MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
private Process? currentProcess; IntPtr hToken,
public List<DownloadItem> DownloadItems { get; } = new List<DownloadItem>(); out IntPtr ppszPath);
public event Action<DownloadItem> DownloadAdded = delegate { };
public event Action<DownloadItem> DownloadProgressChanged = delegate { }; private static DownloadManager instance = null!;
public event Action<DownloadItem> DownloadCompleted = delegate { }; public static DownloadManager Instance => instance ??= new DownloadManager();
// 内部类封装进程结果 private Process? currentProcess;
private class ProcessResult public List<DownloadItem> DownloadItems { get; } = new List<DownloadItem>();
{
public int ExitCode { get; set; } = -1; public event Action<DownloadItem> DownloadAdded = delegate { };
public bool HasExited { get; set; } public event Action<DownloadItem> DownloadProgressChanged = delegate { };
} public event Action<DownloadItem> DownloadCompleted = delegate { };
// 安全获取进程结果 // 内部类封装进程结果
private ProcessResult GetProcessResult(Process? process) private class ProcessResult
{ {
var result = new ProcessResult(); public int ExitCode { get; set; } = -1;
if (process == null) return result; public bool HasExited { get; set; }
}
try
{ // 安全获取进程结果
if (!process.HasExited) private ProcessResult GetProcessResult(Process? process)
{ {
process.WaitForExit(5000); var result = new ProcessResult();
} if (process == null || process.StartInfo == null) return result;
result.HasExited = process.HasExited; try
if (result.HasExited) {
{ if (!process.HasExited)
result.ExitCode = process.ExitCode; {
} process.WaitForExit(5000);
} }
catch
{ result.HasExited = process.HasExited;
// 忽略所有异常,使用默认值 if (result.HasExited)
} {
result.ExitCode = process.ExitCode;
return result; }
} }
catch
public void StartDownload(string fileName, string url) {
{ // 忽略所有异常,使用默认值
// 从URL获取原始文件名用于显示 }
var uri = new Uri(url);
var originalFileName = Path.GetFileName(uri.LocalPath); return result;
}
var downloadItem = new DownloadItem
{ public void StartDownload(string fileName, string url)
FileName = originalFileName, // 显示原始文件名 {
Progress = 0, // 从URL获取原始文件名用于显示
Status = "准备下载" var uri = new Uri(url);
}; var originalFileName = Path.GetFileName(uri.LocalPath);
DownloadItems.Add(downloadItem); var downloadItem = new DownloadItem
DownloadAdded?.Invoke(downloadItem); {
FileName = originalFileName, // 显示原始文件名
Task.Run(() => DownloadFile(downloadItem, fileName, url)); Progress = 0,
} Status = "准备下载"
};
private void DownloadFile(DownloadItem downloadItem, string fileName, string url)
{ DownloadItems.Add(downloadItem);
try DownloadAdded?.Invoke(downloadItem);
{
// 设置下载目录为用户文件夹中的Downloads Task.Run(() => DownloadFile(downloadItem, fileName, url));
// 获取系统下载文件夹路径 }
// 获取系统下载文件夹路径
string downloadsDir; private void DownloadFile(DownloadItem downloadItem, string fileName, string url)
IntPtr pathPtr = IntPtr.Zero; {
try string downloadsDir = string.Empty;
{ try
// 使用SHGetKnownFolderPath API获取下载文件夹 {
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B"); // 获取并验证下载路径
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) != 0) downloadsDir = GetDownloadPath();
{
throw new Exception("无法获取下载文件夹路径"); try
} {
// 检查路径是否有效
downloadsDir = Marshal.PtrToStringUni(pathPtr); if (string.IsNullOrWhiteSpace(downloadsDir))
} {
catch throw new Exception("下载路径为空");
{ }
throw new Exception("无法确定下载文件夹位置,请手动指定下载路径");
} // 尝试创建目录(如果不存在)
finally Directory.CreateDirectory(downloadsDir);
{
if (pathPtr != IntPtr.Zero) // 验证目录是否可写
{ string testFile = Path.Combine(downloadsDir, "write_test.tmp");
Marshal.FreeCoTaskMem(pathPtr); File.WriteAllText(testFile, "test");
} File.Delete(testFile);
} }
Directory.CreateDirectory(downloadsDir); catch (Exception ex)
{
// 回退到默认下载路径
string defaultPath = Path.Combine(
// 构建aria2c路径 Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
var aria2cPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "resource", "aria2c.exe"); "Downloads");
if (!File.Exists(aria2cPath)) Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
{ downloadsDir = defaultPath;
throw new FileNotFoundException($"找不到aria2c.exe: {aria2cPath}"); Directory.CreateDirectory(downloadsDir);
} }
// 设置线程数为16并添加详细日志
// 从URL获取原始文件名
var uri = new Uri(url); // 构建aria2c路径
var originalFileName = Path.GetFileName(uri.LocalPath); var aria2cPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "resource", "aria2c.exe");
var arguments = $"--out=\"{originalFileName}\" --dir=\"{downloadsDir}\" --split=16 --max-connection-per-server=16 {url}";
if (!File.Exists(aria2cPath))
{
currentProcess = new Process throw new FileNotFoundException($"找不到aria2c.exe: {aria2cPath}");
{ }
StartInfo = new ProcessStartInfo
{ // 设置线程数为16并添加详细日志
FileName = aria2cPath, // 从URL获取原始文件名
Arguments = arguments, var uri = new Uri(url);
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory, var originalFileName = Path.GetFileName(uri.LocalPath);
UseShellExecute = false, var arguments = $"--out=\"{originalFileName}\" --dir=\"{downloadsDir}\" --split=16 --max-connection-per-server=16 {url}";
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true currentProcess = new Process
} {
}; StartInfo = new ProcessStartInfo
{
// 获取目标文件路径 FileName = aria2cPath,
string downloadPath = Path.Combine( Arguments = arguments,
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
"Downloads", UseShellExecute = false,
fileName); CreateNoWindow = true,
RedirectStandardOutput = true,
long totalSize = 0; RedirectStandardError = true
}
// 添加进度检测超时机制 };
var progressTimer = new System.Timers.Timer(5000); // 5秒无更新视为完成
progressTimer.Elapsed += (s, e) => { // 获取目标文件路径
progressTimer.Stop(); string downloadPath = Path.Combine(
if (downloadItem.Progress < 100) { Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
downloadItem.Progress = 100; "Downloads",
downloadItem.Status = "下载完成 (100%)"; fileName);
DownloadProgressChanged?.Invoke(downloadItem);
} long totalSize = 0;
};
// 添加进度检测超时机制
currentProcess.OutputDataReceived += (sender, e) => var progressTimer = new System.Timers.Timer(5000); // 5秒无更新视为完成
{ progressTimer.Elapsed += (s, e) => {
if (!string.IsNullOrEmpty(e.Data)) progressTimer.Stop();
{ if (downloadItem.Progress < 100) {
downloadItem.Progress = 100;
downloadItem.Status = "下载完成 (100%)";
// 重置超时计时器 DownloadProgressChanged?.Invoke(downloadItem);
progressTimer.Stop(); }
progressTimer.Start(); };
// 解析总大小 currentProcess.OutputDataReceived += (sender, e) =>
if (e.Data.Contains("Length:")) {
{ if (!string.IsNullOrEmpty(e.Data))
var sizeStr = e.Data.Split(new[]{"Length:"}, StringSplitOptions.RemoveEmptyEntries)[1] {
.Split('(')[0].Trim();
if (long.TryParse(sizeStr, out totalSize))
{ // 重置超时计时器
progressTimer.Stop();
} progressTimer.Start();
}
// 解析总大小
// 解析进度百分比 if (e.Data.Contains("Length:"))
if (e.Data.Contains("%)")) {
{ var sizeStr = e.Data.Split(new[]{"Length:"}, StringSplitOptions.RemoveEmptyEntries)[1]
var start = e.Data.IndexOf("(") + 1; .Split('(')[0].Trim();
var end = e.Data.IndexOf("%)"); if (long.TryParse(sizeStr, out totalSize))
if (start > 0 && end > start) {
{
var progressStr = e.Data.Substring(start, end - start); }
if (int.TryParse(progressStr, out int progress)) }
{
progress = Math.Min(progress, 100); // 解析进度百分比
downloadItem.Progress = progress; if (e.Data.Contains("%)"))
downloadItem.Status = $"下载中({progress}%)"; {
DownloadProgressChanged?.Invoke(downloadItem); var start = e.Data.IndexOf("(") + 1;
} var end = e.Data.IndexOf("%)");
} if (start > 0 && end > start)
} {
} var progressStr = e.Data.Substring(start, end - start);
}; if (int.TryParse(progressStr, out int progress))
{
currentProcess.ErrorDataReceived += (sender, e) => progress = Math.Min(progress, 100);
{ downloadItem.Progress = progress;
if (!string.IsNullOrEmpty(e.Data)) downloadItem.Status = $"下载中({progress}%)";
{ DownloadProgressChanged?.Invoke(downloadItem);
}
downloadItem.Status = $"错误: {e.Data}"; }
DownloadProgressChanged?.Invoke(downloadItem); }
} }
}; };
currentProcess.Exited += (sender, e) => currentProcess.ErrorDataReceived += (sender, e) =>
{ {
var process = currentProcess; if (!string.IsNullOrEmpty(e.Data))
if (process == null) return; {
var result = GetProcessResult(process); downloadItem.Status = $"错误: {e.Data}";
DownloadProgressChanged?.Invoke(downloadItem);
if (result.ExitCode == 0) }
{ };
// 最终状态强制更新
downloadItem.Progress = 100; currentProcess.Exited += (sender, e) =>
downloadItem.Status = "下载完成 (100%)"; {
var process = currentProcess;
// 验证文件完整性 if (process == null) return;
string downloadPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), var result = GetProcessResult(process);
"Downloads",
downloadItem.FileName); if (result.ExitCode == 0)
{
if (File.Exists(downloadPath)) // 最终状态强制更新
{ downloadItem.Progress = 100;
downloadItem.Status = "下载完成 (100%)";
}
else // 验证文件完整性
{ string downloadPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
} "Downloads",
downloadItem.FileName);
// 触发界面更新
DownloadProgressChanged?.Invoke(downloadItem); if (File.Exists(downloadPath))
DownloadCompleted?.Invoke(downloadItem); {
downloadItem.UpdateDisplay();
}
try else
{ {
// 双重确保在主线程显示提示
if (Application.OpenForms.Count > 0) }
{
var mainForm = Application.OpenForms[0]; // 触发界面更新
mainForm.Invoke((MethodInvoker)delegate { DownloadProgressChanged?.Invoke(downloadItem);
MessageBox.Show(mainForm, DownloadCompleted?.Invoke(downloadItem);
$"文件 {downloadItem.FileName} 已成功下载到:\n{Path.Combine(downloadsDir, downloadItem.FileName)}", downloadItem.UpdateDisplay();
"下载完成",
MessageBoxButtons.OK, try
MessageBoxIcon.Information); {
}); // 双重确保在主线程显示提示
} if (Application.OpenForms.Count > 0)
else {
{ var mainForm = Application.OpenForms[0];
mainForm.Invoke((MethodInvoker)delegate {
} MessageBox.Show(mainForm,
} $"文件 {downloadItem.FileName} 已成功下载到:\n{Path.Combine(downloadsDir, downloadItem.FileName)}",
catch "下载完成",
{ MessageBoxButtons.OK,
} MessageBoxIcon.Information);
} });
else }
{ else
downloadItem.Status = $"下载失败 (代码: {result.ExitCode})"; {
MessageBox.Show($"文件 {downloadItem.FileName} 下载失败", "下载失败",
MessageBoxButtons.OK, MessageBoxIcon.Error); }
} }
catch
DownloadCompleted?.Invoke(downloadItem); {
}
try }
{ else
process?.Dispose(); {
} downloadItem.Status = $"下载失败 (代码: {result.ExitCode})";
finally MessageBox.Show($"文件 {downloadItem.FileName} 下载失败", "下载失败",
{ MessageBoxButtons.OK, MessageBoxIcon.Error);
if (process != null) }
{
currentProcess = null; DownloadCompleted?.Invoke(downloadItem);
}
} try
{
// 强制更新显示 process?.Dispose();
downloadItem.UpdateDisplay(); }
}; finally
{
if (process != null)
{
if (!currentProcess.Start()) currentProcess = null;
{ }
throw new Exception("进程启动失败"); }
}
// 强制更新显示
currentProcess.BeginOutputReadLine(); downloadItem.UpdateDisplay();
currentProcess.BeginErrorReadLine(); };
progressTimer.Start();
}
catch (Exception ex)
{ if (!currentProcess.Start())
downloadItem.Status = $"下载错误: {ex.Message}"; {
DownloadCompleted?.Invoke(downloadItem); throw new Exception("进程启动失败");
}
}
} currentProcess.BeginOutputReadLine();
currentProcess.BeginErrorReadLine();
public void CancelDownload(DownloadItem item) progressTimer.Start();
{ }
try catch (Exception ex)
{ {
var process = currentProcess; string errorDetails = $"下载错误: {ex.Message}\n";
if (process == null || process.HasExited || process.StartInfo == null) errorDetails += $"目标路径: {downloadsDir}\n";
{ errorDetails += $"URL: {url}";
item.Status = "已取消";
DownloadProgressChanged?.Invoke(item); downloadItem.Status = $"下载失败: {ex.Message}";
return; Logger.LogError(errorDetails, ex);
}
MessageBox.Show($"下载失败:\n{errorDetails}", "错误",
process.Kill(); MessageBoxButtons.OK, MessageBoxIcon.Error);
process.Dispose();
currentProcess = null; DownloadCompleted?.Invoke(downloadItem);
}
item.Status = "已取消"; }
DownloadProgressChanged?.Invoke(item);
} public void CancelDownload(DownloadItem item)
catch (Exception ex) {
{ try
item.Status = $"取消失败: {ex.Message}"; {
DownloadProgressChanged?.Invoke(item); var process = currentProcess;
} if (process?.StartInfo == null || process.HasExited)
} {
} item.Status = "已取消";
} DownloadProgressChanged?.Invoke(item);
return;
}
process.Kill();
process.Dispose();
currentProcess = null;
item.Status = "已取消";
DownloadProgressChanged?.Invoke(item);
}
catch (Exception ex)
{
item.Status = $"取消失败: {ex.Message}";
DownloadProgressChanged?.Invoke(item);
}
}
private string GetDownloadPath()
{
string fallbackPath = string.Empty;
// 1. 优先读取用户设置的下载路径
try
{
string jsonPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path", "download_path.json");
Logger.Log($"尝试读取下载路径配置文件: {jsonPath}");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
Logger.Log($"配置文件内容: {jsonString}");
var jsonData = JsonSerializer.Deserialize<JsonElement>(jsonString);
string customPath = jsonData.GetProperty("DownloadPath").GetString()?.Trim();
if (!string.IsNullOrWhiteSpace(customPath))
{
Logger.Log($"读取到自定义路径: {customPath}");
// 处理路径格式
customPath = customPath.Replace(@"\\", @"\");
try
{
// 处理路径中的环境变量和特殊字符
customPath = Environment.ExpandEnvironmentVariables(customPath);
customPath = Path.GetFullPath(customPath);
Logger.Log($"标准化后的路径: {customPath}");
// 确保路径以目录分隔符结尾
if (!customPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
customPath += Path.DirectorySeparatorChar;
}
// 验证驱动器是否存在
string drive = Path.GetPathRoot(customPath);
if (!Directory.Exists(drive))
{
Logger.LogError($"驱动器不存在: {drive}");
throw new Exception($"驱动器 {drive} 不存在");
}
// 验证路径
if (!Directory.Exists(customPath))
{
Logger.Log($"创建目录: {customPath}");
Directory.CreateDirectory(customPath);
}
// 更严格的路径可写性测试
string testFile = Path.Combine(customPath, $"write_test_{Guid.NewGuid()}.tmp");
Logger.Log($"测试路径可写性: {testFile}");
try
{
File.WriteAllText(testFile, DateTime.Now.ToString());
string content = File.ReadAllText(testFile);
File.Delete(testFile);
Logger.Log($"路径验证成功: {customPath}");
return customPath.TrimEnd(Path.DirectorySeparatorChar);
}
catch (Exception ex)
{
Logger.LogError($"路径不可写: {customPath}", ex);
throw new Exception($"路径不可写: {customPath}");
}
}
catch (Exception ex)
{
Logger.LogError($"路径处理失败: {customPath}", ex);
throw;
}
}
}
else
{
Logger.Log("未找到下载路径配置文件");
}
}
catch (Exception ex)
{
Logger.LogError("读取自定义下载路径失败", ex);
}
// 2. 回退到系统默认下载路径
IntPtr pathPtr = IntPtr.Zero;
try
{
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B");
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) == 0)
{
string? defaultPath = Marshal.PtrToStringUni(pathPtr);
if (!string.IsNullOrEmpty(defaultPath))
{
Directory.CreateDirectory(defaultPath);
return defaultPath;
}
else
{
Logger.LogWarning("获取到的系统下载路径为空");
}
}
}
catch (Exception ex)
{
Logger.LogError("获取系统下载路径失败", ex);
}
finally
{
if (pathPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
// 3. 最终回退到相对路径 ~/Downloads
string relativePath = "~/Downloads";
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ??
Environment.GetFolderPath(Environment.SpecialFolder.Desktop) ??
AppDomain.CurrentDomain.BaseDirectory;
if (!string.IsNullOrEmpty(userProfile))
{
fallbackPath = relativePath.Replace("~", userProfile);
if (!string.IsNullOrEmpty(fallbackPath))
{
fallbackPath = Path.GetFullPath(fallbackPath);
}
}
try {
Directory.CreateDirectory(fallbackPath);
// 测试路径可写性
string testFile = Path.Combine(fallbackPath, "write_test.tmp");
if (!string.IsNullOrEmpty(testFile))
{
File.WriteAllText(testFile, "test");
File.Delete(testFile);
}
return fallbackPath;
}
catch {
throw new Exception($"无法使用默认下载路径: {fallbackPath}");
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 KiB

View File

@@ -1,190 +1,196 @@
using System; // _ _
using System.Diagnostics; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.Drawing; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
using System.IO; //| | (_) | | | || (_| | |_) | |_) |_____/ /
using System.Windows.Forms; //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
namespace AppStore using System;
{ using System.Diagnostics;
public class ImageCompressorForm : Form using System.Drawing;
{ using System.IO;
private Button btnSelectInput = new Button(); using System.Windows.Forms;
private Button btnSelectOutput = new Button();
private Button btnCompress = new Button(); namespace AppStore
private TextBox txtInput = new TextBox(); {
private TextBox txtOutput = new TextBox(); public class ImageCompressorForm : Form
private RadioButton rbLossy = new RadioButton(); {
private RadioButton rbLossless = new RadioButton(); private Button btnSelectInput = new Button();
private TrackBar tbQuality = new TrackBar(); private Button btnSelectOutput = new Button();
private Label lblQuality = new Label(); private Button btnCompress = new Button();
private CheckBox cbKeepExif = new CheckBox(); private TextBox txtInput = new TextBox();
private ProgressBar progressBar = new ProgressBar(); private TextBox txtOutput = new TextBox();
private RadioButton rbLossy = new RadioButton();
public ImageCompressorForm() private RadioButton rbLossless = new RadioButton();
{ private TrackBar tbQuality = new TrackBar();
InitializeComponent(); private Label lblQuality = new Label();
} private CheckBox cbKeepExif = new CheckBox();
private ProgressBar progressBar = new ProgressBar();
private void InitializeComponent()
{ public ImageCompressorForm()
this.Text = "图片压缩工具"; {
this.Size = new Size(500, 350); InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen; }
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false; private void InitializeComponent()
{
// 输入文件选择 this.Text = "图片压缩工具";
btnSelectInput.Text = "选择..."; this.Size = new Size(500, 350);
btnSelectInput.Location = new Point(400, 20); this.StartPosition = FormStartPosition.CenterScreen;
btnSelectInput.Click += (s, e) => SelectFile(txtInput); this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.Controls.Add(btnSelectInput); this.MaximizeBox = false;
txtInput.Location = new Point(20, 20); // 输入文件选择
txtInput.Size = new Size(370, 20); btnSelectInput.Text = "选择...";
txtInput.ReadOnly = true; btnSelectInput.Location = new Point(400, 20);
this.Controls.Add(txtInput); btnSelectInput.Click += (s, e) => SelectFile(txtInput);
this.Controls.Add(btnSelectInput);
Label lblInput = new Label();
lblInput.Text = "输入文件:"; txtInput.Location = new Point(20, 20);
lblInput.Location = new Point(20, 0); txtInput.Size = new Size(370, 20);
this.Controls.Add(lblInput); txtInput.ReadOnly = true;
this.Controls.Add(txtInput);
// 输出文件选择
btnSelectOutput.Text = "选择..."; Label lblInput = new Label();
btnSelectOutput.Location = new Point(400, 70); lblInput.Text = "输入文件:";
btnSelectOutput.Click += (s, e) => SelectFile(txtOutput, true); lblInput.Location = new Point(20, 0);
this.Controls.Add(btnSelectOutput); this.Controls.Add(lblInput);
txtOutput.Location = new Point(20, 70); // 输出文件选择
txtOutput.Size = new Size(370, 20); btnSelectOutput.Text = "选择...";
this.Controls.Add(txtOutput); btnSelectOutput.Location = new Point(400, 70);
btnSelectOutput.Click += (s, e) => SelectFile(txtOutput, true);
Label lblOutput = new Label(); this.Controls.Add(btnSelectOutput);
lblOutput.Text = "输出文件:";
lblOutput.Location = new Point(20, 50); txtOutput.Location = new Point(20, 70);
this.Controls.Add(lblOutput); txtOutput.Size = new Size(370, 20);
this.Controls.Add(txtOutput);
// 压缩类型
rbLossy.Text = "有损压缩 (JPEG)"; Label lblOutput = new Label();
rbLossy.Location = new Point(20, 110); lblOutput.Text = "输出文件:";
rbLossy.Checked = true; lblOutput.Location = new Point(20, 50);
this.Controls.Add(rbLossy); this.Controls.Add(lblOutput);
rbLossless.Text = "无损压缩 (PNG)"; // 压缩类型
rbLossless.Location = new Point(20, 135); rbLossy.Text = "有损压缩 (JPEG)";
this.Controls.Add(rbLossless); rbLossy.Location = new Point(20, 110);
rbLossy.Checked = true;
// 质量设置 this.Controls.Add(rbLossy);
tbQuality.Minimum = 1;
tbQuality.Maximum = 1000; rbLossless.Text = "无损压缩 (PNG)";
tbQuality.Value = 800; rbLossless.Location = new Point(20, 135);
tbQuality.Location = new Point(20, 190); this.Controls.Add(rbLossless);
tbQuality.Size = new Size(300, 50);
tbQuality.Scroll += (s, e) => lblQuality.Text = $"压缩质量: {tbQuality.Value}"; // 质量设置
this.Controls.Add(tbQuality); tbQuality.Minimum = 1;
tbQuality.Maximum = 1000;
lblQuality.Text = $"压缩质量: {tbQuality.Value}"; tbQuality.Value = 800;
lblQuality.Location = new Point(20, 170); tbQuality.Location = new Point(20, 190);
this.Controls.Add(lblQuality); tbQuality.Size = new Size(300, 50);
tbQuality.Scroll += (s, e) => lblQuality.Text = $"压缩质量: {tbQuality.Value}";
// EXIF选项 this.Controls.Add(tbQuality);
cbKeepExif.Text = "保留EXIF信息";
cbKeepExif.Location = new Point(20, 240); lblQuality.Text = $"压缩质量: {tbQuality.Value}";
this.Controls.Add(cbKeepExif); lblQuality.Location = new Point(20, 170);
this.Controls.Add(lblQuality);
// 压缩按钮
btnCompress.Text = "开始压缩"; // EXIF选项
btnCompress.Location = new Point(20, 280); cbKeepExif.Text = "保留EXIF信息";
btnCompress.Size = new Size(460, 30); cbKeepExif.Location = new Point(20, 240);
btnCompress.Click += BtnCompress_Click; this.Controls.Add(cbKeepExif);
this.Controls.Add(btnCompress);
// 压缩按钮
// 调整窗体大小 btnCompress.Text = "开始压缩";
this.Size = new Size(500, 370); btnCompress.Location = new Point(20, 280);
} btnCompress.Size = new Size(460, 30);
btnCompress.Click += BtnCompress_Click;
private void SelectFile(TextBox target, bool isSave = false) this.Controls.Add(btnCompress);
{
var dialog = isSave ? new SaveFileDialog() : new OpenFileDialog() as FileDialog; // 调整窗体大小
dialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*"; this.Size = new Size(500, 370);
}
if (dialog.ShowDialog() == DialogResult.OK)
{ private void SelectFile(TextBox target, bool isSave = false)
target.Text = dialog.FileName; {
} var dialog = isSave ? new SaveFileDialog() : new OpenFileDialog() as FileDialog;
} dialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*";
private void BtnCompress_Click(object sender, EventArgs e) if (dialog.ShowDialog() == DialogResult.OK)
{ {
if (string.IsNullOrEmpty(txtInput.Text) || !File.Exists(txtInput.Text)) target.Text = dialog.FileName;
{ }
MessageBox.Show("请选择有效的输入文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); }
return;
} private void BtnCompress_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtOutput.Text)) if (string.IsNullOrEmpty(txtInput.Text) || !File.Exists(txtInput.Text))
{ {
MessageBox.Show("请指定输出文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("请选择有效的输入文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
btnCompress.Enabled = false; if (string.IsNullOrEmpty(txtOutput.Text))
{
try MessageBox.Show("请指定输出文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
{ return;
string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe"); }
if (!File.Exists(toolPath))
{ btnCompress.Enabled = false;
MessageBox.Show("图片压缩工具未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; try
} {
string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe");
string args = $"\"{txtInput.Text}\" \"{txtOutput.Text}\""; if (!File.Exists(toolPath))
args += $" -t {(rbLossy.Checked ? "lossy" : "lossless")}"; {
args += $" -q {tbQuality.Value}"; MessageBox.Show("图片压缩工具未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
if (cbKeepExif.Checked) args += " -e"; return;
}
var process = new Process();
process.StartInfo.FileName = toolPath; string args = $"\"{txtInput.Text}\" \"{txtOutput.Text}\"";
process.StartInfo.Arguments = args; args += $" -t {(rbLossy.Checked ? "lossy" : "lossless")}";
process.StartInfo.UseShellExecute = false; args += $" -q {tbQuality.Value}";
process.StartInfo.CreateNoWindow = true; if (cbKeepExif.Checked) args += " -e";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true; var process = new Process();
process.StartInfo.FileName = toolPath;
process.OutputDataReceived += (s, ev) => { process.StartInfo.Arguments = args;
if (!string.IsNullOrEmpty(ev.Data)) process.StartInfo.UseShellExecute = false;
Console.WriteLine(ev.Data); process.StartInfo.CreateNoWindow = true;
}; process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += (s, ev) => {
if (!string.IsNullOrEmpty(ev.Data)) process.OutputDataReceived += (s, ev) => {
Console.Error.WriteLine(ev.Data); if (!string.IsNullOrEmpty(ev.Data))
}; Console.WriteLine(ev.Data);
};
process.Start();
process.BeginOutputReadLine(); process.ErrorDataReceived += (s, ev) => {
process.BeginErrorReadLine(); if (!string.IsNullOrEmpty(ev.Data))
process.WaitForExit(); Console.Error.WriteLine(ev.Data);
};
if (process.ExitCode == 0)
{ process.Start();
MessageBox.Show("图片压缩完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); process.BeginOutputReadLine();
} process.BeginErrorReadLine();
else process.WaitForExit();
{
MessageBox.Show("图片压缩失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); if (process.ExitCode == 0)
} {
} MessageBox.Show("图片压缩完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
catch (Exception ex) }
{ else
MessageBox.Show($"压缩过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); {
} MessageBox.Show("图片压缩失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
finally }
{ }
btnCompress.Enabled = true; catch (Exception ex)
progressBar.Visible = false; {
} MessageBox.Show($"压缩过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} finally
} {
btnCompress.Enabled = true;
progressBar.Visible = false;
}
}
}
}

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#nullable enable #nullable enable
using System; using System;
using System.Drawing; using System.Drawing;
@@ -140,11 +146,19 @@ namespace AppStore
this.StartPosition = FormStartPosition.CenterScreen; this.StartPosition = FormStartPosition.CenterScreen;
this.Icon = new Icon("img/ico/icon.ico"); // 设置窗体图标 this.Icon = new Icon("img/ico/icon.ico"); // 设置窗体图标
// 注册主题变更事件
ThemeManager.ThemeChanged += (theme) =>
{
this.Invoke((MethodInvoker)delegate {
AnimateThemeChange();
});
};
// 现代化顶部导航栏 // 现代化顶部导航栏
Panel buttonPanel = new Panel(); Panel buttonPanel = new Panel();
buttonPanel.Dock = DockStyle.Top; buttonPanel.Dock = DockStyle.Top;
buttonPanel.Height = 70; buttonPanel.Height = 70;
buttonPanel.BackColor = Color.FromArgb(240, 240, 240); buttonPanel.BackColor = ThemeManager.ControlBackgroundColor;
buttonPanel.Padding = new Padding(10, 15, 10, 0); buttonPanel.Padding = new Padding(10, 15, 10, 0);
buttonPanel.AutoScroll = true; buttonPanel.AutoScroll = true;
buttonPanel.AutoSize = true; buttonPanel.AutoSize = true;
@@ -154,11 +168,13 @@ namespace AppStore
Action<Button> styleButton = (Button btn) => { Action<Button> styleButton = (Button btn) => {
btn.FlatStyle = FlatStyle.Flat; btn.FlatStyle = FlatStyle.Flat;
btn.FlatAppearance.BorderSize = 0; btn.FlatAppearance.BorderSize = 0;
btn.BackColor = Color.Transparent; btn.BackColor = ThemeManager.ControlBackgroundColor;
btn.ForeColor = Color.FromArgb(64, 64, 64); btn.ForeColor = ThemeManager.TextColor;
btn.Font = new Font("Microsoft YaHei", 10, FontStyle.Regular); btn.Font = new Font("Microsoft YaHei", 10, FontStyle.Regular);
btn.Size = new Size(120, 40); btn.Size = new Size(120, 40);
btn.Cursor = Cursors.Hand; btn.Cursor = Cursors.Hand;
btn.FlatAppearance.MouseOverBackColor = ThemeManager.ButtonHoverColor;
btn.FlatAppearance.MouseDownBackColor = ThemeManager.ButtonActiveColor;
// 悬停效果 // 悬停效果
btn.MouseEnter += (s, e) => { btn.MouseEnter += (s, e) => {
@@ -167,7 +183,7 @@ namespace AppStore
}; };
btn.MouseLeave += (s, e) => { btn.MouseLeave += (s, e) => {
btn.ForeColor = Color.FromArgb(64, 64, 64); btn.ForeColor = ThemeManager.TextColor;
btn.Font = new Font(btn.Font, FontStyle.Regular); btn.Font = new Font(btn.Font, FontStyle.Regular);
}; };
}; };
@@ -230,19 +246,22 @@ namespace AppStore
// 现代化内容区域 // 现代化内容区域
contentPanel = new Panel(); contentPanel = new Panel();
contentPanel.Dock = DockStyle.Fill; contentPanel.Dock = DockStyle.Fill;
contentPanel.BackColor = Color.White; contentPanel.BackColor = ThemeManager.BackgroundColor;
contentPanel.Padding = new Padding(20); contentPanel.Padding = new Padding(20);
contentPanel.AutoScroll = true;
this.Controls.Add(contentPanel); this.Controls.Add(contentPanel);
// 添加分隔线 // 添加分隔线
Panel separator = new Panel(); Panel separator = new Panel();
separator.Dock = DockStyle.Top; separator.Dock = DockStyle.Top;
separator.Height = 1; separator.Height = 1;
separator.BackColor = Color.FromArgb(230, 230, 230); separator.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.FromArgb(230, 230, 230)
: Color.FromArgb(60, 60, 60);
contentPanel.Controls.Add(separator); contentPanel.Controls.Add(separator);
this.Controls.Add(buttonPanel); this.Controls.Add(buttonPanel);
this.BackColor = Color.White; this.BackColor = ThemeManager.BackgroundColor;
// 默认显示软件下载视图 // 默认显示软件下载视图
ShowAppsView(); ShowAppsView();
@@ -446,43 +465,109 @@ namespace AppStore
}; };
flowPanel.Controls.Add(systemInfoCard); flowPanel.Controls.Add(systemInfoCard);
// 视频压缩工具卡片 // 计算器工具卡片
var videoCompressorCard = new ToolCard(); var calculatorCard = new CalculatorToolCard();
videoCompressorCard.ToolName = "视频压缩工具";
try try
{ {
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "video_compressor.png"); string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Calculator.png");
if (File.Exists(iconPath)) if (File.Exists(iconPath))
{ {
videoCompressorCard.ToolIcon = Image.FromFile(iconPath); calculatorCard.ToolIcon = Image.FromFile(iconPath);
}
calculatorCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载计算器图标失败", ex);
}
flowPanel.Controls.Add(calculatorCard);
// 图片查看工具卡片
var imageViewerCard = new KortAppZ.Tools.Viewer.ImageViewerToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "ImageCompressor.png");
if (File.Exists(iconPath))
{
imageViewerCard.ToolIcon = Image.FromFile(iconPath);
}
imageViewerCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载图片查看器图标失败", ex);
}
flowPanel.Controls.Add(imageViewerCard);
// 密码生成器工具卡片
var passwordGeneratorCard = new ToolCard();
passwordGeneratorCard.ToolName = "密码生成器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "password_generator.png");
if (File.Exists(iconPath))
{
passwordGeneratorCard.ToolIcon = Image.FromFile(iconPath);
} }
else else
{ {
videoCompressorCard.ToolIcon = SystemIcons.Shield.ToBitmap(); passwordGeneratorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
} }
} }
catch catch
{ {
videoCompressorCard.ToolIcon = SystemIcons.Shield.ToBitmap(); passwordGeneratorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
} }
passwordGeneratorCard.ToolCardClicked += (s, e) => {
videoCompressorCard.UpdateDisplay();
videoCompressorCard.ToolCardClicked += (s, e) => {
try { try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "video_compressor.exe"); string toolPath = Path.Combine(Application.StartupPath, "resource", "password_generator.exe");
if (File.Exists(toolPath)) { if (File.Exists(toolPath)) {
Process.Start(toolPath); Process.Start(toolPath);
} else { } else {
MessageBox.Show("视频压缩工具未找到,请确保已正确安装", "错误", MessageBox.Show("密码生成器工具未找到,请确保已正确安装", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} catch (Exception ex) { } catch (Exception ex) {
MessageBox.Show($"启动视频压缩工具失败: {ex.Message}", "错误", MessageBox.Show($"启动密码生成器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
}; };
flowPanel.Controls.Add(videoCompressorCard); passwordGeneratorCard.UpdateDisplay();
flowPanel.Controls.Add(passwordGeneratorCard);
// 自启动管理工具卡片
var selfStartingManagerCard = new SelfStartingManagerToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Self_starting_management.png");
if (File.Exists(iconPath))
{
selfStartingManagerCard.ToolIcon = Image.FromFile(iconPath);
}
selfStartingManagerCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载自启动管理工具图标失败", ex);
}
flowPanel.Controls.Add(selfStartingManagerCard);
// 图标提取器工具卡片
var iconExtractorCard = new AppStore.Tools.IconExtractor.IconExtractorToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "ico_extractor.png");
if (File.Exists(iconPath))
{
iconExtractorCard.ToolIcon = Image.FromFile(iconPath);
}
iconExtractorCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载图标提取器图标失败", ex);
}
flowPanel.Controls.Add(iconExtractorCard);
} }
catch (Exception ex) catch (Exception ex)
@@ -551,11 +636,11 @@ namespace AppStore
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 15, 15, 15); flowPanel.Padding = new Padding(15, 15, 15, 15);
flowPanel.WrapContents = true; flowPanel.WrapContents = false;
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 5000);
// 创建搜索框 // 创建搜索框
TextBox searchBox = new TextBox(); TextBox searchBox = new TextBox();
@@ -583,11 +668,11 @@ namespace AppStore
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 60, 15, 15); flowPanel.Padding = new Padding(15, 60, 15, 15);
flowPanel.WrapContents = true; flowPanel.WrapContents = false;
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 5000);
contentPanel.Controls.Add(flowPanel); contentPanel.Controls.Add(flowPanel);
// 添加窗体关闭事件处理 // 添加窗体关闭事件处理
@@ -625,7 +710,7 @@ namespace AppStore
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 3800);//大概一行250像素
contentPanel.Controls.Add(flowPanel); contentPanel.Controls.Add(flowPanel);
// 添加所有应用卡片并恢复位置 // 添加所有应用卡片并恢复位置
@@ -633,7 +718,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"XDM", "XDM",
"https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi", "https://ghproxy.net/https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi",
"img/png/XDM.png")); "img/png/XDM.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -698,9 +783,14 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"Msys2", "Msys2",
"https://github.com/msys2/msys2-installer/releases/download/2025-02-21/msys2-x86_64-20250221.exe", "https://ghproxy.net/https://github.com/msys2/msys2-installer/releases/download/2025-02-21/msys2-x86_64-20250221.exe",
"img/png/MSYS2.png")); "img/png/MSYS2.png"));
flowPanel.Controls.Add(CreateAppCard(
"OpenJDK by Azul JDKs",
"https://cdn.azul.com/zulu/bin/zulu21.42.19-ca-jdk21.0.7-win_x64.msi",
"img/png/Azul_JDKs.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
".NET SDK 8.0", ".NET SDK 8.0",
"https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer", "https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer",
@@ -736,6 +826,16 @@ namespace AppStore
"https://ghproxy.net/https://github.com/game1024/OpenSpeedy/releases/download/v1.7.1/OpenSpeedy-v1.7.1.zip", "https://ghproxy.net/https://github.com/game1024/OpenSpeedy/releases/download/v1.7.1/OpenSpeedy-v1.7.1.zip",
"img/png/openspeedy.png")); "img/png/openspeedy.png"));
flowPanel.Controls.Add(CreateAppCard(
"Final2x",
"https://ghproxy.net/https://github.com/Tohrusky/Final2x/releases/download/2024-12-14/Final2x-windows-x64-setup.exe",
"img/png/Final2x.png"));
flowPanel.Controls.Add(CreateAppCard(
"Pixpin",
"https://download.pixpin.cn/PixPin_2.0.0.3.exe",
"img/png/pixpin.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"QuickLook", "QuickLook",
"https://ghproxy.net/https://github.com/QL-Win/QuickLook/releases/download/4.0.2/QuickLook-4.0.2.exe", "https://ghproxy.net/https://github.com/QL-Win/QuickLook/releases/download/4.0.2/QuickLook-4.0.2.exe",
@@ -798,7 +898,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"frp", "frp",
"https://github.com/fatedier/frp/releases/download/v0.62.1/frp_0.62.1_windows_amd64.zip", "https://ghproxy.net/https://github.com/fatedier/frp/releases/download/v0.62.1/frp_0.62.1_windows_amd64.zip",
"")); ""));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -922,6 +1022,11 @@ namespace AppStore
"https://ghproxy.net/https://github.com/vnotex/vnote/releases/download/v3.19.2/VNote-3.19.2-win64.zip", "https://ghproxy.net/https://github.com/vnotex/vnote/releases/download/v3.19.2/VNote-3.19.2-win64.zip",
"img/png/vnote.png")); "img/png/vnote.png"));
flowPanel.Controls.Add(CreateAppCard(
"notepad--",
"https://www.ghproxy.cn/https://github.com/cxasm/notepad--/releases/download/notepad-v3.3/Notepad--v3.3-plugin-Installer.exe",
"img/png/notepad--.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"PowerToys", "PowerToys",
"https://ghproxy.net/https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe", "https://ghproxy.net/https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe",
@@ -932,6 +1037,21 @@ namespace AppStore
"https://ghproxy.net/https://github.com/microsoft/terminal/releases/download/v1.22.11141.0/Microsoft.WindowsTerminal_1.22.11141.0_x64.zip", "https://ghproxy.net/https://github.com/microsoft/terminal/releases/download/v1.22.11141.0/Microsoft.WindowsTerminal_1.22.11141.0_x64.zip",
"img/png/terminal.png")); "img/png/terminal.png"));
flowPanel.Controls.Add(CreateAppCard(
"github_cli",
"https://ghproxy.cn/https://github.com/cli/cli/releases/download/v2.74.2/gh_2.74.2_windows_arm64.msi",
"img/png/github_cli.png"));
flowPanel.Controls.Add(CreateAppCard(
"ReactOS",
"https://ghproxy.cn/https://github.com/reactos/reactos/releases/download/0.4.15-release/ReactOS-0.4.15-release-1-gdbb43bbaeb2-x86-iso.zip",
"img/png/ReactOS.png"));
flowPanel.Controls.Add(CreateAppCard(
"Ubuntu桌面发行版",
"https://releases.ubuntu.com/24.04/ubuntu-24.04.2-desktop-amd64.iso",
"img/png/Ubuntu.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"typescript", "typescript",
"https://ghproxy.net/https://github.com/microsoft/TypeScript/releases/download/v5.8.3/typescript-5.8.3.tgz", "https://ghproxy.net/https://github.com/microsoft/TypeScript/releases/download/v5.8.3/typescript-5.8.3.tgz",
@@ -942,6 +1062,11 @@ namespace AppStore
"https://mirror.nju.edu.cn/gimp/gimp/v3.0/windows/gimp-3.0.4-setup.exe", "https://mirror.nju.edu.cn/gimp/gimp/v3.0/windows/gimp-3.0.4-setup.exe",
"img/jpg/Gimp.jpg")); "img/jpg/Gimp.jpg"));
flowPanel.Controls.Add(CreateAppCard(
"ClamAV",
"https://www.clamav.net/downloads/production/clamav-1.4.3.win.x64.msi",
"img/png/ClamAV.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"Shotcut", "Shotcut",
"https://sourceforge.net/projects/shotcut/files/v25.05.11/shotcut-win64-250511.exe/download", "https://sourceforge.net/projects/shotcut/files/v25.05.11/shotcut-win64-250511.exe/download",
@@ -1097,6 +1222,11 @@ namespace AppStore
"https://ghproxy.net/https://github.com/cloudreve/cloudreve/releases/download/3.8.3/cloudreve_3.8.3_windows_amd64.zip", "https://ghproxy.net/https://github.com/cloudreve/cloudreve/releases/download/3.8.3/cloudreve_3.8.3_windows_amd64.zip",
"img/png/cloudreve.png")); "img/png/cloudreve.png"));
flowPanel.Controls.Add(CreateAppCard(
"ollama",
"https://www.ghproxy.cn/https://github.com/ollama/ollama/releases/download/v0.9.5/OllamaSetup.exe",
"img/png/ollama.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"SeelenUI", "SeelenUI",
"https://ghproxy.net/https://github.com/eythaann/Seelen-UI/releases/download/v2.3.8/Seelen.UI_2.3.8_x64-setup.exe", "https://ghproxy.net/https://github.com/eythaann/Seelen-UI/releases/download/v2.3.8/Seelen.UI_2.3.8_x64-setup.exe",
@@ -1201,6 +1331,9 @@ namespace AppStore
// 初始化窗体组件 // 初始化窗体组件
InitializeComponent(); InitializeComponent();
// 应用主题
ThemeManager.ApplyTheme(this);
// 订阅下载管理器事件 // 订阅下载管理器事件
DownloadManager.Instance.DownloadAdded += OnDownloadAdded; // 下载添加事件 DownloadManager.Instance.DownloadAdded += OnDownloadAdded; // 下载添加事件
DownloadManager.Instance.DownloadProgressChanged += OnDownloadProgressChanged; // 下载进度变化事件 DownloadManager.Instance.DownloadProgressChanged += OnDownloadProgressChanged; // 下载进度变化事件
@@ -1282,5 +1415,69 @@ namespace AppStore
Logger.Log($"下载完成: {item.FileName}, 状态: {item.Status}"); // 记录日志 Logger.Log($"下载完成: {item.FileName}, 状态: {item.Status}"); // 记录日志
item.UpdateDisplay(); // 更新UI显示 item.UpdateDisplay(); // 更新UI显示
} }
/// <summary>
/// 主题切换动画效果
/// </summary>
private void AnimateThemeChange()
{
const int animationSteps = 10;
const int animationInterval = 30;
var timer = new System.Windows.Forms.Timer { Interval = animationInterval };
int step = 0;
// 保存当前和目标颜色
var originalBackColor = this.BackColor;
var targetBackColor = ThemeManager.BackgroundColor;
var originalForeColor = this.ForeColor;
var targetForeColor = ThemeManager.TextColor;
timer.Tick += (s, e) => {
if (step >= animationSteps)
{
timer.Stop();
timer.Dispose();
// 确保最终颜色准确
ThemeManager.ApplyTheme(this);
return;
}
// 计算插值比例
float ratio = (float)step / animationSteps;
step++;
// 插值计算新颜色
var newBackColor = Color.FromArgb(
(int)(originalBackColor.R + (targetBackColor.R - originalBackColor.R) * ratio),
(int)(originalBackColor.G + (targetBackColor.G - originalBackColor.G) * ratio),
(int)(originalBackColor.B + (targetBackColor.B - originalBackColor.B) * ratio));
var newForeColor = Color.FromArgb(
(int)(originalForeColor.R + (targetForeColor.R - originalForeColor.R) * ratio),
(int)(originalForeColor.G + (targetForeColor.G - originalForeColor.G) * ratio),
(int)(originalForeColor.B + (targetForeColor.B - originalForeColor.B) * ratio));
// 应用新颜色
this.Invoke((MethodInvoker)delegate {
this.BackColor = newBackColor;
this.ForeColor = newForeColor;
foreach (Control control in this.Controls)
{
control.BackColor = newBackColor;
control.ForeColor = newForeColor;
// 特殊处理按钮的悬停状态
if (control is Button button)
{
button.FlatAppearance.MouseOverBackColor = ThemeManager.ButtonHoverColor;
button.FlatAppearance.MouseDownBackColor = ThemeManager.ButtonActiveColor;
}
}
});
};
timer.Start();
}
} }
} }

View File

@@ -1,16 +1,22 @@
using System; // _ _
using System.Windows.Forms; //| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
namespace AppStore //| | (_) | | | || (_| | |_) | |_) |_____/ /
{ //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
static class Program // |_| |_|
{ using System;
[STAThread] using System.Windows.Forms;
static void Main()
{ namespace AppStore
Application.EnableVisualStyles(); {
Application.SetCompatibleTextRenderingDefault(false); static class Program
Application.Run(new MainForm()); {
} [STAThread]
} static void Main()
} {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}

View File

@@ -5,14 +5,12 @@
## 项目开源行为 ## 项目开源行为
1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。 1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
2. 项目文档开源,允许任何人使用、修改、分发、商用,但必须注明原作者。 2. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
3. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者 3. 项目的任何衍生品包括但不限于网站、APP、插件等必须遵循以上开源协议
4. 项目的任何衍生品包括但不限于网站、APP、插件等必须遵循以上开源协议 4. 项目不接受任何形式的广告,不得在任何地方投放广告
5. 项目不接受任何形式的广告,不得在任何地方投放广告。 5. 项目不接受任何形式的捐赠、赞助
6. 项目不接受任何形式的捐赠。 6. 项目可以进行PR欢迎任何形式的PR不提交issue也可以
7. 项目不接受任何形式的赞助。 7. 项目可以PR一些你自己的项目如果star数量不到1k都会被删除
8. 项目可以进行PR欢迎任何形式的PR不提交issue也可以
9. 本项目可以PR一些你自己的项目如果star数量不到1k都会被删除
## 项目简介 ## 项目简介
@@ -35,14 +33,15 @@
### 打包指令 ### 打包指令
#### 32位版本 #### 32位版本
```bash ```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:Optimize=true /p:DebugType=None
``` ```
#### 64位版本 #### 64位版本
```bash ```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None
``` ```
打包后的可执行文件将包含指定的应用程序图标,输出路径为: 打包后的可执行文件将包含指定的应用程序图标,输出路径为:
@@ -51,7 +50,9 @@ bin\Release\net8.0-windows\[platform]\publish
``` ```
### 高级选项 ### 高级选项
- 添加`--self-contained true`生成独立包(体积较大 - 使用`--self-contained false`生成框架依赖包(默认
- 使用`/p:Optimize=true`启用代码优化(默认)
- 使用`/p:DebugType=None`禁用调试符号生成(默认)
- 添加`-p:PublishTrimmed=true`可减小包体积(实验性) - 添加`-p:PublishTrimmed=true`可减小包体积(实验性)
## 项目结构 ## 项目结构

View File

@@ -1,53 +1,229 @@
using System; // _ _
using System.Diagnostics; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.IO; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
using System.Windows.Forms; //| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
namespace AppStore // |_| |_|
{ using System;
public class SettingsUserControl : UserControl using System.Diagnostics;
{ using System.IO;
private Button btnCleanLogs; using System.Windows.Forms;
using System.Drawing;
public SettingsUserControl() using System.Text.Json;
{
this.Dock = DockStyle.Fill; namespace AppStore
this.BackColor = Color.White; {
public class SettingsUserControl : UserControl
// 设置顶部内边距 {
this.Padding = new Padding(0, 30, 0, 0); private Button btnCleanLogs;
private Button btnLightTheme;
btnCleanLogs = new Button(); private Button btnDarkTheme;
btnCleanLogs.Text = "清理日志";
btnCleanLogs.Size = new Size(150, 40); public SettingsUserControl()
btnCleanLogs.Location = new Point((this.Width - 150) / 2, 50); // 调整Y坐标为50靠近顶部 {
btnCleanLogs.Font = new Font("Microsoft YaHei", 10); this.Dock = DockStyle.Fill;
btnCleanLogs.Anchor = AnchorStyles.Top; // 添加顶部锚点 ThemeManager.ApplyTheme(this);
btnCleanLogs.Click += (s, e) => CleanLogs();
this.Controls.Add(btnCleanLogs); // 设置顶部内边距
} this.Padding = new Padding(0, 30, 0, 0);
private void CleanLogs() // 主题切换按钮
{ btnLightTheme = new Button();
try btnLightTheme.Text = "浅色模式";
{ btnLightTheme.Size = new Size(150, 40);
string logCleanerPath = Path.Combine("resource", "log_cleaner.exe"); btnLightTheme.Location = new Point((this.Width - 320) / 2, 50);
btnLightTheme.Font = new Font("Microsoft YaHei", 10);
if (File.Exists(logCleanerPath)) btnLightTheme.Anchor = AnchorStyles.Top;
{ btnLightTheme.Click += (s, e) => SwitchTheme(ThemeManager.ThemeMode.Light);
Process.Start(logCleanerPath); this.Controls.Add(btnLightTheme);
MessageBox.Show("日志清理程序已启动", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
} btnDarkTheme = new Button();
else btnDarkTheme.Text = "深色模式";
{ btnDarkTheme.Size = new Size(150, 40);
MessageBox.Show("日志清理程序未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); btnDarkTheme.Location = new Point(btnLightTheme.Right + 20, 50);
} btnDarkTheme.Font = new Font("Microsoft YaHei", 10);
} btnDarkTheme.Anchor = AnchorStyles.Top;
catch (Exception ex) btnDarkTheme.Click += (s, e) => SwitchTheme(ThemeManager.ThemeMode.Dark);
{ this.Controls.Add(btnDarkTheme);
Logger.LogError("清理日志时出错", ex);
MessageBox.Show($"清理日志时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); // 清理日志按钮
} btnCleanLogs = new Button();
} btnCleanLogs.Text = "清理日志";
} btnCleanLogs.Size = new Size(150, 40);
} btnCleanLogs.Location = new Point((this.Width - 150) / 2, 110);
btnCleanLogs.Font = new Font("Microsoft YaHei", 10);
btnCleanLogs.Anchor = AnchorStyles.Top;
btnCleanLogs.Click += (s, e) => CleanLogs();
this.Controls.Add(btnCleanLogs);
// 下载路径设置
Label lblDownloadPath = new Label();
lblDownloadPath.Text = "下载路径:";
lblDownloadPath.AutoSize = true;
lblDownloadPath.Location = new Point((this.Width - 300) / 2, 170);
lblDownloadPath.Font = new Font("Microsoft YaHei", 10);
lblDownloadPath.Anchor = AnchorStyles.Top;
this.Controls.Add(lblDownloadPath);
TextBox txtDownloadPath = new TextBox();
txtDownloadPath.Size = new Size(300, 30);
txtDownloadPath.Location = new Point((this.Width - 300) / 2, 200);
txtDownloadPath.Font = new Font("Microsoft YaHei", 10);
txtDownloadPath.Anchor = AnchorStyles.Top;
txtDownloadPath.ReadOnly = true;
this.Controls.Add(txtDownloadPath);
Button btnBrowse = new Button();
btnBrowse.Text = "浏览...";
btnBrowse.Size = new Size(80, 30);
btnBrowse.Location = new Point(txtDownloadPath.Right + 10, 200);
btnBrowse.Font = new Font("Microsoft YaHei", 10);
btnBrowse.Anchor = AnchorStyles.Top;
btnBrowse.Click += (s, e) => BrowseDownloadPath(txtDownloadPath);
this.Controls.Add(btnBrowse);
Button btnSavePath = new Button();
btnSavePath.Text = "保存路径";
btnSavePath.Size = new Size(100, 30);
btnSavePath.Location = new Point((this.Width - 100) / 2, 240);
btnSavePath.Font = new Font("Microsoft YaHei", 10);
btnSavePath.Anchor = AnchorStyles.Top;
btnSavePath.Click += (s, e) => SaveDownloadPath(txtDownloadPath.Text);
this.Controls.Add(btnSavePath);
ThemeManager.ThemeChanged += OnThemeChanged;
LoadDownloadPath(txtDownloadPath);
}
private void SwitchTheme(ThemeManager.ThemeMode theme)
{
ThemeManager.CurrentTheme = theme;
}
private void OnThemeChanged(ThemeManager.ThemeMode theme)
{
ThemeManager.ApplyTheme(this);
}
private void CleanLogs()
{
try
{
string logCleanerPath = Path.Combine("resource", "log_cleaner.exe");
if (File.Exists(logCleanerPath))
{
Process.Start(logCleanerPath);
MessageBox.Show("日志清理程序已启动", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("日志清理程序未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
Logger.LogError("清理日志时出错", ex);
MessageBox.Show($"清理日志时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BrowseDownloadPath(TextBox txtBox)
{
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
{
dialog.Description = "选择下载路径";
if (dialog.ShowDialog() == DialogResult.OK)
{
txtBox.Text = dialog.SelectedPath;
}
}
}
private void SaveDownloadPath(string path)
{
try
{
// 验证路径
if (string.IsNullOrWhiteSpace(path))
{
MessageBox.Show("下载路径不能为空", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 尝试创建目录(如果不存在)
try
{
Directory.CreateDirectory(path);
// 验证目录是否可写
string testFile = Path.Combine(path, "write_test.tmp");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
}
catch
{
MessageBox.Show($"无法访问路径: {path}\n请确保路径存在且有写入权限", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 保存路径
string dlPathDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path");
if (!Directory.Exists(dlPathDir))
{
Directory.CreateDirectory(dlPathDir);
}
string jsonPath = Path.Combine(dlPathDir, "download_path.json");
var jsonData = new { DownloadPath = path };
string jsonString = JsonSerializer.Serialize(jsonData);
File.WriteAllText(jsonPath, jsonString);
MessageBox.Show($"下载路径已保存到:\n{path}", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
Logger.LogError("保存下载路径时出错", ex);
MessageBox.Show($"保存下载路径时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadDownloadPath(TextBox txtBox)
{
// 默认下载路径为用户文件夹下的Downloads
string defaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Downloads");
try
{
string jsonPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path", "download_path.json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
var jsonData = JsonSerializer.Deserialize<JsonElement>(jsonString);
string customPath = jsonData.GetProperty("DownloadPath").GetString() ?? "";
// 如果自定义路径有效则显示,否则显示默认路径
txtBox.Text = !string.IsNullOrWhiteSpace(customPath) ? customPath : defaultPath;
}
else
{
txtBox.Text = defaultPath;
}
}
catch (Exception ex)
{
Logger.LogError("加载下载路径时出错", ex);
txtBox.Text = defaultPath;
}
}
}
}

12
TXT/Build.txt Normal file
View File

@@ -0,0 +1,12 @@
首先,如果希望编译程序,那么必须安装.NET8.0 SDK
下载链接https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer
使用一下指令编译
x86:
dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:Optimize=true /p:DebugType=None
x64:
dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None

7
TXT/Run.txt Normal file
View File

@@ -0,0 +1,7 @@
如果希望运行,那么必须安装.NET8.0 SDK
下载链接https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer
使用一下指令运行:
dotnet run

142
ThemeManager.cs Normal file
View File

@@ -0,0 +1,142 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Text.Json;
using System.IO;
namespace AppStore
{
public static class ThemeManager
{
public enum ThemeMode
{
Light,
Dark
}
private static readonly string ThemeConfigPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "theme.json");
private static ThemeMode _currentTheme = LoadTheme();
private static ThemeMode LoadTheme()
{
try
{
if (File.Exists(ThemeConfigPath))
{
var json = File.ReadAllText(ThemeConfigPath);
return JsonSerializer.Deserialize<ThemeMode>(json);
}
}
catch
{
// 忽略错误,使用默认主题
}
return ThemeMode.Light;
}
private static void SaveTheme(ThemeMode theme)
{
try
{
var dir = Path.GetDirectoryName(ThemeConfigPath);
if (dir == null) return;
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var json = JsonSerializer.Serialize(theme);
File.WriteAllText(ThemeConfigPath, json);
}
catch
{
// 忽略错误
}
}
// 浅色主题颜色
private static readonly Color LightBackground = Color.FromArgb(255, 255, 255);
private static readonly Color LightControlBackground = Color.FromArgb(240, 240, 240);
private static readonly Color LightText = Color.FromArgb(30, 30, 30);
private static readonly Color LightButtonHover = Color.FromArgb(230, 230, 230);
private static readonly Color LightButtonActive = Color.FromArgb(220, 220, 220);
// 深色主题颜色
private static readonly Color DarkBackground = Color.FromArgb(30, 30, 30);
private static readonly Color DarkControlBackground = Color.FromArgb(45, 45, 45);
private static readonly Color DarkText = Color.FromArgb(240, 240, 240);
private static readonly Color DarkButtonHover = Color.FromArgb(60, 60, 60);
private static readonly Color DarkButtonActive = Color.FromArgb(70, 70, 70);
private static readonly Color DarkBorder = Color.FromArgb(80, 80, 80);
// 浅色主题边框颜色
private static readonly Color LightBorder = Color.FromArgb(180, 180, 180);
public static event Action<ThemeMode> ThemeChanged = delegate {};
public static ThemeMode CurrentTheme
{
get => _currentTheme;
set
{
if (_currentTheme != value)
{
_currentTheme = value;
ThemeChanged?.Invoke(value);
SaveTheme(value);
}
}
}
public static Color BackgroundColor =>
_currentTheme == ThemeMode.Light ? LightBackground : DarkBackground;
public static Color ControlBackgroundColor =>
_currentTheme == ThemeMode.Light ? LightControlBackground : DarkControlBackground;
public static Color TextColor =>
_currentTheme == ThemeMode.Light ? LightText : DarkText;
public static Color ButtonHoverColor =>
_currentTheme == ThemeMode.Light ? LightButtonHover : DarkButtonHover;
public static Color ButtonActiveColor =>
_currentTheme == ThemeMode.Light ? LightButtonActive : DarkButtonActive;
public static Color BorderColor =>
_currentTheme == ThemeMode.Light ? LightBorder : DarkBorder;
public static void ApplyTheme(Control control)
{
ApplyThemeToControl(control);
}
private static void ApplyThemeToControl(Control control)
{
control.BackColor = BackgroundColor;
control.ForeColor = TextColor;
if (control is Button button)
{
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.FlatAppearance.MouseOverBackColor = ButtonHoverColor;
button.FlatAppearance.MouseDownBackColor = ButtonActiveColor;
}
foreach (Control childControl in control.Controls)
{
ApplyThemeToControl(childControl);
}
}
}
}

View File

@@ -1,77 +1,119 @@
using System; // _ _
using System.Drawing; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.Windows.Forms; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
namespace AppStore //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
{ // |_| |_|
public class ToolCard : UserControl using System;
{ using System.Drawing;
private PictureBox iconBox = new PictureBox(); using System.Windows.Forms;
private Label nameLabel = new Label();
namespace AppStore
public string ToolName { get; set; } = string.Empty; {
public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap(); public class ToolCard : UserControl
{
// 自定义点击事件初始化为空委托 private PictureBox iconBox = new PictureBox();
public event EventHandler ToolCardClicked = delegate {}; private Label nameLabel = new Label();
private Panel namePanel = new Panel();
public ToolCard() private Color borderColor = SystemColors.ControlDark;
{
// 启用双缓冲 public string ToolName { get; set; } = string.Empty;
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap();
ControlStyles.ResizeRedraw, true);
InitializeComponent(); // 自定义点击事件初始化为空委托
} public event EventHandler ToolCardClicked = delegate {};
private void InitializeComponent() public ToolCard()
{ {
this.Size = new Size(240, 220); // 启用双缓冲
this.BackColor = Color.White; this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
this.Padding = new Padding(5); ControlStyles.ResizeRedraw, true);
this.BorderStyle = BorderStyle.FixedSingle; InitializeComponent();
}
// 工具图标
iconBox = new PictureBox(); private void InitializeComponent()
iconBox.Size = new Size(80, 80); {
iconBox.Location = new Point((Width - 80) / 2, 15); this.Size = new Size(240, 220);
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; this.BackColor = Color.White;
this.Controls.Add(iconBox); this.Padding = new Padding(5);
this.BorderStyle = BorderStyle.FixedSingle;
// 工具名称
nameLabel = new Label(); // 工具图标
nameLabel.AutoSize = false; iconBox = new PictureBox();
nameLabel.Size = new Size(Width - 20, 30); iconBox.Size = new Size(80, 80);
nameLabel.Location = new Point(10, 100); iconBox.Location = new Point((Width - 80) / 2, 15);
nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold); iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
nameLabel.TextAlign = ContentAlignment.MiddleCenter; this.Controls.Add(iconBox);
this.Controls.Add(nameLabel);
// 工具名称 - 使用Panel包裹Label实现边框颜色
// 打开按钮 namePanel = new Panel();
var openButton = new Button(); namePanel.Size = new Size(Width - 20, 30);
openButton.Text = "打开工具"; namePanel.Location = new Point(10, 100);
openButton.Size = new Size(100, 30); namePanel.Paint += (sender, e) => {
openButton.Location = new Point((Width - 100) / 2, 140); ControlPaint.DrawBorder(e.Graphics, namePanel.ClientRectangle,
openButton.BackColor = Color.FromArgb(0, 120, 215); borderColor, ButtonBorderStyle.Solid);
openButton.ForeColor = Color.White; };
openButton.FlatStyle = FlatStyle.Flat;
openButton.FlatAppearance.BorderSize = 0; nameLabel = new Label();
openButton.Cursor = Cursors.Hand; nameLabel.Dock = DockStyle.Fill;
// 按钮点击直接触发ToolCardClicked事件 nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold);
openButton.Click += (s, e) => { nameLabel.TextAlign = ContentAlignment.MiddleCenter;
ToolCardClicked?.Invoke(this, e); namePanel.Controls.Add(nameLabel);
};
this.Controls.Add(openButton); // 初始主题设置
UpdateLabelTheme();
// 设置按钮悬停效果
openButton.BackColor = Color.FromArgb(0, 120, 215); // 订阅主题变化事件
openButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 100, 180); ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
openButton.FlatAppearance.MouseDownBackColor = Color.FromArgb(0, 80, 160);
} this.Controls.Add(namePanel);
public void UpdateDisplay() // 打开按钮
{ var openButton = new Button();
nameLabel.Text = ToolName; openButton.Text = "打开工具";
iconBox.Image = ToolIcon; openButton.Size = new Size(100, 30);
} openButton.Location = new Point((Width - 100) / 2, 140);
} openButton.BackColor = Color.FromArgb(0, 120, 215);
} openButton.ForeColor = Color.White;
openButton.FlatStyle = FlatStyle.Flat;
openButton.FlatAppearance.BorderSize = 0;
openButton.Cursor = Cursors.Hand;
// 按钮点击直接触发ToolCardClicked事件
openButton.Click += (s, e) => {
ToolCardClicked?.Invoke(this, e);
};
this.Controls.Add(openButton);
// 设置按钮悬停效果
openButton.BackColor = Color.FromArgb(0, 120, 215);
openButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 100, 180);
openButton.FlatAppearance.MouseDownBackColor = Color.FromArgb(0, 80, 160);
}
private void UpdateLabelTheme()
{
if (ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Dark)
{
nameLabel.BackColor = Color.Black;
nameLabel.ForeColor = Color.White;
namePanel.BackColor = Color.Black;
borderColor = Color.White;
}
else
{
nameLabel.BackColor = Color.White;
nameLabel.ForeColor = Color.Black;
namePanel.BackColor = Color.White;
borderColor = SystemColors.ControlDark;
}
namePanel.Invalidate(); // 触发重绘
}
public void UpdateDisplay()
{
nameLabel.Text = ToolName;
iconBox.Image = ToolIcon;
UpdateLabelTheme();
}
}
}

View File

@@ -1,64 +1,70 @@
#include <windows.h> // _ _
#include <vector> //| | _____ _ __| |_ __ _ _ __ _ __ ____
#include <fstream> //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
#include <cmath> //| | (_) | | | || (_| | |_) | |_) |_____/ /
#include <d2d1.h> //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
// 高性能边框路径生成 #include <windows.h>
void GenerateBorderPath(int width, int height, int radius, const char* outputPath) { #include <vector>
std::ofstream out(outputPath); #include <fstream>
const float pi = 3.1415926f; #include <cmath>
const int segments = 24; // 高分段数确保平滑 #include <d2d1.h>
std::vector<POINTFLOAT> points; // 高性能边框路径生成
void GenerateBorderPath(int width, int height, int radius, const char* outputPath) {
// 优化后的圆角路径生成 std::ofstream out(outputPath);
auto addArc = [&](float startAngle, float endAngle, float cx, float cy) { const float pi = 3.1415926f;
for (int i = 0; i <= segments; ++i) { const int segments = 24; // 高分段数确保平滑
float angle = startAngle + (endAngle - startAngle) * i / segments;
points.push_back({ std::vector<POINTFLOAT> points;
cx + radius * cosf(angle),
cy + radius * sinf(angle) // 优化后的圆角路径生成
}); auto addArc = [&](float startAngle, float endAngle, float cx, float cy) {
} for (int i = 0; i <= segments; ++i) {
}; float angle = startAngle + (endAngle - startAngle) * i / segments;
points.push_back({
// 左上角 cx + radius * cosf(angle),
addArc(pi, 3*pi/2, radius, radius); cy + radius * sinf(angle)
});
// 右上角 }
addArc(3*pi/2, 2*pi, width - radius, radius); };
// 右下 // 左上
addArc(0, pi/2, width - radius, height - radius); addArc(pi, 3*pi/2, radius, radius);
// 左下角 // 右上角
addArc(pi/2, pi, radius, height - radius); addArc(3*pi/2, 2*pi, width - radius, radius);
// 闭合路径 // 右下角
points.push_back(points[0]); addArc(0, pi/2, width - radius, height - radius);
// 写入优化格式 // 左下角
if (out.is_open()) { addArc(pi/2, pi, radius, height - radius);
for (const auto& p : points) {
out << p.x << "," << p.y << "\n"; // 闭合路径
} points.push_back(points[0]);
}
} // 写入优化格式
if (out.is_open()) {
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { for (const auto& p : points) {
int argc; out << p.x << "," << p.y << "\n";
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); }
}
if (argc != 5) return 1; }
int width = _wtoi(argv[1]); int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
int height = _wtoi(argv[2]); int argc;
int radius = _wtoi(argv[3]); LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
char outputPath[MAX_PATH]; if (argc != 5) return 1;
wcstombs(outputPath, argv[4], MAX_PATH);
int width = _wtoi(argv[1]);
GenerateBorderPath(width, height, radius, outputPath); int height = _wtoi(argv[2]);
return 0; int radius = _wtoi(argv[3]);
}
char outputPath[MAX_PATH];
wcstombs(outputPath, argv[4], MAX_PATH);
GenerateBorderPath(width, height, radius, outputPath);
return 0;
}

View File

@@ -1,90 +1,96 @@
#include <iostream> // _ _
#include <fstream> //| | _____ _ __| |_ __ _ _ __ _ __ ____
#include <vector> //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
#include <cmath> //| | (_) | | | || (_| | |_) | |_) |_____/ /
#include <windows.h> //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using namespace std; #include <iostream>
#include <fstream>
struct Point { #include <vector>
float x; #include <cmath>
float y; #include <windows.h>
};
using namespace std;
void WritePathToFile(const vector<Point>& path, const string& filename) {
ofstream outFile(filename); struct Point {
if (!outFile) { float x;
cerr << "无法打开输出文件: " << filename << endl; float y;
return; };
}
void WritePathToFile(const vector<Point>& path, const string& filename) {
for (const auto& point : path) { ofstream outFile(filename);
outFile << point.x << "," << point.y << "\n"; if (!outFile) {
} cerr << "无法打开输出文件: " << filename << endl;
outFile.close(); return;
} }
vector<Point> CalculateRoundedRectPath(int width, int height, int radius) { for (const auto& point : path) {
vector<Point> pathPoints; outFile << point.x << "," << point.y << "\n";
}
const int segments = 10; outFile.close();
const float angleStep = 3.1415926f / (2 * segments); }
// 左上角 vector<Point> CalculateRoundedRectPath(int width, int height, int radius) {
for (int i = 0; i <= segments; i++) { vector<Point> pathPoints;
float angle = 3.1415926f + i * angleStep;
pathPoints.push_back({ const int segments = 10;
radius + radius * cosf(angle), const float angleStep = 3.1415926f / (2 * segments);
radius + radius * sinf(angle)
}); // 左上角
} for (int i = 0; i <= segments; i++) {
float angle = 3.1415926f + i * angleStep;
// 右上角 pathPoints.push_back({
for (int i = 0; i <= segments; i++) { radius + radius * cosf(angle),
float angle = 3 * 3.1415926f / 2 + i * angleStep; radius + radius * sinf(angle)
pathPoints.push_back({ });
width - radius + radius * cosf(angle), }
radius + radius * sinf(angle)
}); // 右上角
} for (int i = 0; i <= segments; i++) {
float angle = 3 * 3.1415926f / 2 + i * angleStep;
// 右下角 pathPoints.push_back({
for (int i = 0; i <= segments; i++) { width - radius + radius * cosf(angle),
float angle = 0 + i * angleStep; radius + radius * sinf(angle)
pathPoints.push_back({ });
width - radius + radius * cosf(angle), }
height - radius + radius * sinf(angle)
}); // 右下角
} for (int i = 0; i <= segments; i++) {
float angle = 0 + i * angleStep;
// 左下角 pathPoints.push_back({
for (int i = 0; i <= segments; i++) { width - radius + radius * cosf(angle),
float angle = 3.1415926f / 2 + i * angleStep; height - radius + radius * sinf(angle)
pathPoints.push_back({ });
radius + radius * cosf(angle), }
height - radius + radius * sinf(angle)
}); // 左下角
} for (int i = 0; i <= segments; i++) {
float angle = 3.1415926f / 2 + i * angleStep;
// 闭合路径 pathPoints.push_back({
pathPoints.push_back(pathPoints[0]); radius + radius * cosf(angle),
height - radius + radius * sinf(angle)
return pathPoints; });
} }
int main(int argc, char* argv[]) { // 闭合路径
if (argc != 5) { pathPoints.push_back(pathPoints[0]);
cout << "用法: card_calculator [宽度] [高度] [圆角半径] [输出文件]" << endl;
return 1; return pathPoints;
} }
int width = stoi(argv[1]); int main(int argc, char* argv[]) {
int height = stoi(argv[2]); if (argc != 5) {
int radius = stoi(argv[3]); cout << "用法: card_calculator [宽度] [高度] [圆角半径] [输出文件]" << endl;
string outputFile = argv[4]; return 1;
}
auto path = CalculateRoundedRectPath(width, height, radius);
WritePathToFile(path, outputFile); int width = stoi(argv[1]);
int height = stoi(argv[2]);
return 0; int radius = stoi(argv[3]);
} string outputFile = argv[4];
auto path = CalculateRoundedRectPath(width, height, radius);
WritePathToFile(path, outputFile);
return 0;
}

View File

@@ -17,21 +17,7 @@
<h2>核心功能</h2> <h2>核心功能</h2>
<article class="feature"> <article class="feature">
<h3>应用程序管理</h3> <h3>目前没有什么东西,别看了,害羞(✿◡‿◡)
<p>批量安装、卸载(目前没有)和更新应用程序(目前没有),管理启动项(目前没有)。</p>
<p>优势:集中管理所有应用,节省时间,避免系统臃肿。</p>
</article>
<article class="feature">
<h3>资源监控(之后可能在内置工具里有)</h3>
<p>实时监控CPU、内存、磁盘和网络使用情况。</p>
<p>优势:直观的图表展示,及时发现资源瓶颈。</p>
</article>
<article class="feature">
<h3>文件管理(之后可能在内置工具里有)</h3>
<p>高级文件搜索、批量重命名和快速文件分类。</p>
<p>优势:提升文件管理效率,支持正则表达式搜索。</p>
</article> </article>
</section> </section>
</main> </main>

BIN
img/png/Azul_JDKs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
img/png/ClamAV.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/png/Final2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
img/png/ReactOS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
img/png/Ubuntu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

BIN
img/png/github_cli.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
img/png/notepad--.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
img/png/ollama.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
img/png/pixpin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 946 KiB

64
inno/innosetup_x64.iss Normal file
View File

@@ -0,0 +1,64 @@
; 脚本由 Inno Setup 脚本向导生成。
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z"
#define MyAppVersion "1.2.4"
#define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
[Setup]
; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
; (若要生成新的 GUID请在 IDE 中单击 "工具|生成 GUID"。)
AppId={{8020EC01-6133-40BB-8B8B-0EB71E49696C}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
; "ArchitecturesAllowed=x64compatible" 指定安装程序无法运行
; 除 Arm 上的 x64 和 Windows 11 之外的任何平台上。
ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" 要求
; 安装可以在 x64 或 Arm 上的 Windows 11 上以“64 位模式”完成,
; 这意味着它应该使用本机 64 位 Program Files 目录和
; 注册表的 64 位视图。
ArchitecturesInstallIn64BitMode=x64compatible
ChangesAssociations=yes
DisableProgramGroupPage=yes
LicenseFile=C:\Users\Administrator\Downloads\LICENSE.txt
; 取消注释以下行以在非管理安装模式下运行 (仅为当前用户安装)。
;PrivilegesRequired=lowest
OutputDir=D:\C#\kortapp-z_Release\setup
OutputBaseFilename=kortapp-z_setup_x64
SolidCompression=yes
WizardStyle=modern
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "D:\C#\kortapp-z_Release\x64_inno\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\C#\kortapp-z_Release\x64_inno\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; 注意:不要在任何共享系统文件上使用 "Flags: ignoreversion"
[Registry]
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

56
inno/innosetup_x86.iss Normal file
View File

@@ -0,0 +1,56 @@
; 脚本由 Inno Setup 脚本向导生成。
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z"
#define MyAppVersion "1.2.4"
#define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
[Setup]
; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
; (若要生成新的 GUID请在 IDE 中单击 "工具|生成 GUID"。)
AppId={{BF1944C3-CD0C-4119-A340-49C54961D48B}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
ChangesAssociations=yes
DisableProgramGroupPage=yes
LicenseFile=C:\Users\Administrator\Downloads\LICENSE.txt
; 取消注释以下行以在非管理安装模式下运行 (仅为当前用户安装)。
;PrivilegesRequired=lowest
OutputDir=D:\C#\kortapp-z_Release\setup
OutputBaseFilename=kortapp-z_setup_x86
SolidCompression=yes
WizardStyle=modern
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "D:\C#\kortapp-z_Release\x86_inno\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "D:\C#\kortapp-z_Release\x86_inno\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; 注意:不要在任何共享系统文件上使用 "Flags: ignoreversion"
[Registry]
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

View File

@@ -1,65 +1,71 @@
#include <iostream> // _ _
#include <filesystem> //| | _____ _ __| |_ __ _ _ __ _ __ ____
#include <chrono> //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
namespace fs = std::filesystem; //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
int main() { #include <iostream>
try { #include <filesystem>
auto start = std::chrono::high_resolution_clock::now(); #include <chrono>
// 定义日志目录路径 namespace fs = std::filesystem;
fs::path logDir;
int main() {
#ifdef _WIN32 try {
// Windows系统获取AppData路径 auto start = std::chrono::high_resolution_clock::now();
char* appData = nullptr;
size_t len = 0; // 定义日志目录路径
if (_dupenv_s(&appData, &len, "APPDATA") == 0 && appData != nullptr) { fs::path logDir;
logDir = fs::path(appData) / "zsyg" / "kortapp-z" / ".logs";
free(appData); #ifdef _WIN32
} else { // Windows系统获取AppData路径
std::cerr << "无法获取APPDATA环境变量" << std::endl; char* appData = nullptr;
return 1; size_t len = 0;
} if (_dupenv_s(&appData, &len, "APPDATA") == 0 && appData != nullptr) {
#else logDir = fs::path(appData) / "zsyg" / "kortapp-z" / ".logs";
// 非Windows系统使用默认路径 free(appData);
logDir = fs::path(getenv("HOME")) / ".zsyg" / "kortapp-z" / ".logs"; } else {
#endif std::cerr << "无法获取APPDATA环境变量" << std::endl;
size_t deletedCount = 0; return 1;
size_t errorCount = 0; }
#else
// 检查目录是否存在 // 非Windows系统使用默认路径
if (fs::exists(logDir) && fs::is_directory(logDir)) { logDir = fs::path(getenv("HOME")) / ".zsyg" / "kortapp-z" / ".logs";
// 遍历并删除所有日志文件 #endif
for (const auto& entry : fs::directory_iterator(logDir)) { size_t deletedCount = 0;
try { size_t errorCount = 0;
if (fs::is_regular_file(entry)) {
fs::remove(entry); // 检查目录是否存在
deletedCount++; if (fs::exists(logDir) && fs::is_directory(logDir)) {
} // 遍历并删除所有日志文件
} catch (const std::exception& e) { for (const auto& entry : fs::directory_iterator(logDir)) {
std::cerr << "删除文件失败: " << entry.path() << " - " << e.what() << std::endl; try {
errorCount++; if (fs::is_regular_file(entry)) {
} fs::remove(entry);
} deletedCount++;
} else { }
std::cout << "日志目录不存在,无需清理" << std::endl; } catch (const std::exception& e) {
return 0; std::cerr << "删除文件失败: " << entry.path() << " - " << e.what() << std::endl;
} errorCount++;
}
auto end = std::chrono::high_resolution_clock::now(); }
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); } else {
std::cout << "日志目录不存在,无需清理" << std::endl;
std::cout << "日志清理完成: " << std::endl; return 0;
std::cout << "删除文件数: " << deletedCount << std::endl; }
std::cout << "错误数: " << errorCount << std::endl;
std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl; auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
} catch (const std::exception& e) {
std::cerr << "发生错误: " << e.what() << std::endl; std::cout << "日志清理完成: " << std::endl;
return 1; std::cout << "删除文件数: " << deletedCount << std::endl;
} std::cout << "错误数: " << errorCount << std::endl;
std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl;
return 0;
} } catch (const std::exception& e) {
std::cerr << "发生错误: " << e.what() << std::endl;
return 1;
}
return 0;
}

162
logger.cs
View File

@@ -1,63 +1,99 @@
using System; // _ _
using System.IO; //| | _____ _ __| |_ __ _ _ __ _ __ ____
using System.Text; //| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
namespace AppStore //|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
{ // |_| |_|
public static class Logger using System;
{ using System.IO;
private static readonly string LogsDirectory = Path.Combine( using System.Text;
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".logs"); namespace AppStore
private static readonly object LockObject = new object(); {
public static class Logger
static Logger() {
{ private static readonly string LogsDirectory = Path.Combine(
try Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
{ "zsyg", "kortapp-z", ".logs");
// 确保logs目录存在 private static readonly object LockObject = new object();
if (!Directory.Exists(LogsDirectory))
{ static Logger()
Directory.CreateDirectory(LogsDirectory); {
} try
} {
catch (Exception ex) // 确保logs目录存在
{ if (!Directory.Exists(LogsDirectory))
Console.WriteLine($"无法创建日志目录: {LogsDirectory}, 错误: {ex.Message}"); {
throw; Directory.CreateDirectory(LogsDirectory);
} }
} }
catch (Exception ex)
public static void Log(string message) {
{ Console.WriteLine($"无法创建日志目录: {LogsDirectory}, 错误: {ex.Message}");
lock (LockObject) throw;
{ }
try }
{
string fileName = $"{DateTime.Now:yyyyMMddHHmmss}.log"; public static void Log(string message)
string filePath = Path.Combine(LogsDirectory, fileName); {
lock (LockObject)
using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8)) {
{ try
writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}"); {
} string fileName = $"{DateTime.Now:yyyyMMddHHmmss}.log";
} string filePath = Path.Combine(LogsDirectory, fileName);
catch (Exception ex)
{ using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8))
// 日志记录失败时输出到控制台 {
Console.WriteLine($"日志记录失败: {ex.Message}"); writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
} }
} }
} catch (Exception ex)
{
public static void LogError(string message, Exception? ex = null) // 日志记录失败时输出到控制台
{ Console.WriteLine($"日志记录失败: {ex.Message}");
string errorMessage = $"ERROR: {message}"; }
if (ex != null) }
{ }
errorMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
} public static void LogError(string message, Exception? ex = null)
Log(errorMessage); {
} string errorMessage = $"ERROR: {message}";
} if (ex != null)
} {
errorMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
}
Log(errorMessage);
}
public static void LogWarning(string message, Exception? ex = null)
{
string warningMessage = $"WARNING: {message}";
if (ex != null)
{
warningMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
}
Log(warningMessage);
}
public static void LogDebug(string message, Exception? ex = null)
{
string debugMessage = $"DEBUG: {message}";
if (ex != null)
{
debugMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
}
Log(debugMessage);
}
public static void LogTip(string message, Exception? ex = null)
{
string tipMessage = $"TIP: {message}";
if (ex != null)
{
tipMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
}
Log(tipMessage);
}
}
}

View File

@@ -0,0 +1,20 @@
CXX = g++
CXXFLAGS = -std=c++11 -Iinclude
LDFLAGS = -static -mwindows -lfltk -lole32 -luuid -lcomctl32 -lgdi32 -lwsock32 -lcomdlg32 -lwinspool
SRC = $(wildcard src/*.cpp)
OBJ = $(patsubst src/%.cpp,obj/%.o,$(SRC))
TARGET = password_generator
all: $(TARGET)
$(TARGET): $(OBJ)
$(CXX) $^ -o $@ $(LDFLAGS)
obj/%.o: src/%.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJ) $(TARGET)
.PHONY: all clean

View File

@@ -0,0 +1,27 @@
#ifndef PASSWORD_GENERATOR_HPP
#define PASSWORD_GENERATOR_HPP
#include "string.hpp"
class PasswordGenerator {
public:
enum Mode {
DIGITS, // 纯数字
ENGLISH, // 纯英文
SYMBOLS, // 纯符号
DIGITS_ENGLISH, // 数字+英文
DIGITS_SYMBOLS, // 数字+符号
ENGLISH_SYMBOLS // 英文+符号
};
PasswordGenerator();
String generate(int length, Mode mode = DIGITS);
private:
String generateDigits(int length);
String generateEnglish(int length);
String generateSymbols(int length);
String generateMixed(int length, const String& charSet);
};
#endif // PASSWORD_GENERATOR_HPP

View File

@@ -0,0 +1,28 @@
#ifndef PASSWORD_GUI_HPP
#define PASSWORD_GUI_HPP
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Output.H>
#include "password_generator.hpp"
class PasswordGUI {
public:
PasswordGUI();
void show();
private:
Fl_Window* window;
Fl_Input* lengthInput;
Fl_Choice* modeChoice;
Fl_Button* generateButton;
Fl_Output* passwordOutput;
PasswordGenerator generator;
static void onGenerate(Fl_Widget* widget, void* data);
};
#endif // PASSWORD_GUI_HPP

View File

@@ -0,0 +1,25 @@
#ifndef STRING_HPP
#define STRING_HPP
#include <cstddef>
class String {
public:
String();
String(const char* str);
String(const String& other);
~String();
size_t length() const;
const char* c_str() const;
String& operator=(const String& other);
String operator+(const String& other) const;
bool operator==(const String& other) const;
private:
char* data;
size_t len;
};
#endif // STRING_HPP

View File

@@ -0,0 +1,7 @@
#include "password_gui.hpp"
int main(int argc, char** argv) {
PasswordGUI gui;
gui.show();
return Fl::run();
}

View File

@@ -0,0 +1,96 @@
#include "password_generator.hpp"
#include <cstdlib>
#include <ctime>
PasswordGenerator::PasswordGenerator() {
srand(time(nullptr));
}
String PasswordGenerator::generate(int length, Mode mode) {
if (length < 1 || length > 100) {
return String("无效长度(1-100)");
}
switch (mode) {
case DIGITS:
return generateDigits(length);
case ENGLISH:
return generateEnglish(length);
case SYMBOLS:
return generateSymbols(length);
case DIGITS_ENGLISH: {
String charSet = String("0123456789") +
String("abcdefghijklmnopqrstuvwxyz") +
String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
return generateMixed(length, charSet);
}
case DIGITS_SYMBOLS: {
String charSet = String("0123456789") + String("!@#$%^&*()_+-=[]{}|;:,.<>?");
return generateMixed(length, charSet);
}
case ENGLISH_SYMBOLS: {
String charSet = String("abcdefghijklmnopqrstuvwxyz") +
String("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +
String("!@#$%^&*()_+-=[]{}|;:,.<>?");
return generateMixed(length, charSet);
}
default:
return String("不支持的模式");
}
}
String PasswordGenerator::generateDigits(int length) {
char* buffer = new char[length + 1];
for (int i = 0; i < length; ++i) {
buffer[i] = '0' + (rand() % 10);
}
buffer[length] = '\0';
String result(buffer);
delete[] buffer;
return result;
}
String PasswordGenerator::generateEnglish(int length) {
char* buffer = new char[length + 1];
for (int i = 0; i < length; ++i) {
int choice = rand() % 2;
if (choice == 0) {
buffer[i] = 'a' + (rand() % 26); // 小写字母
} else {
buffer[i] = 'A' + (rand() % 26); // 大写字母
}
}
buffer[length] = '\0';
String result(buffer);
delete[] buffer;
return result;
}
String PasswordGenerator::generateSymbols(int length) {
const char symbols[] = "!@#$%^&*()_+-=[]{}|;:,.<>?";
const int symbolCount = sizeof(symbols) - 1; // 减去末尾的\0
char* buffer = new char[length + 1];
for (int i = 0; i < length; ++i) {
buffer[i] = symbols[rand() % symbolCount];
}
buffer[length] = '\0';
String result(buffer);
delete[] buffer;
return result;
}
String PasswordGenerator::generateMixed(int length, const String& charSet) {
char* buffer = new char[length + 1];
for (int i = 0; i < length; ++i) {
buffer[i] = charSet.c_str()[rand() % charSet.length()];
}
buffer[length] = '\0';
String result(buffer);
delete[] buffer;
return result;
}

View File

@@ -0,0 +1,36 @@
#include "password_gui.hpp"
#include <cstdlib>
PasswordGUI::PasswordGUI() {
window = new Fl_Window(400, 350, "密码生成器");
lengthInput = new Fl_Input(150, 50, 200, 30, "密码长度:");
modeChoice = new Fl_Choice(150, 90, 200, 30, "密码模式:");
modeChoice->add("纯数字");
modeChoice->add("纯英文");
modeChoice->add("纯符号");
modeChoice->add("数字+英文");
modeChoice->add("数字+符号");
modeChoice->add("英文+符号");
modeChoice->value(0); // 默认选择纯数字模式
generateButton = new Fl_Button(150, 140, 100, 40, "生成密码");
passwordOutput = new Fl_Output(100, 210, 250, 40, "生成结果:");
generateButton->callback(onGenerate, this);
window->end();
}
void PasswordGUI::show() {
window->show();
}
void PasswordGUI::onGenerate(Fl_Widget* widget, void* data) {
PasswordGUI* gui = static_cast<PasswordGUI*>(data);
const char* lengthStr = gui->lengthInput->value();
int length = atoi(lengthStr);
PasswordGenerator::Mode mode = static_cast<PasswordGenerator::Mode>(gui->modeChoice->value());
String password = gui->generator.generate(length, mode);
gui->passwordOutput->value(password.c_str());
}

View File

@@ -0,0 +1,51 @@
#include "string.hpp"
#include <cstring>
String::String() : data(nullptr), len(0) {}
String::String(const char* str) {
len = strlen(str);
data = new char[len + 1];
strcpy(data, str);
}
String::String(const String& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
String::~String() {
delete[] data;
}
size_t String::length() const {
return len;
}
const char* String::c_str() const {
return data;
}
String& String::operator=(const String& other) {
if (this != &other) {
delete[] data;
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
return *this;
}
String String::operator+(const String& other) const {
String result;
result.len = len + other.len;
result.data = new char[result.len + 1];
strcpy(result.data, data);
strcat(result.data, other.data);
return result;
}
bool String::operator==(const String& other) const {
return strcmp(data, other.data) == 0;
}

View File

@@ -1,8 +1,8 @@
#ifndef ABOUT_DIALOG_H #ifndef ABOUT_DIALOG_H
#define ABOUT_DIALOG_H #define ABOUT_DIALOG_H
#include <windows.h> #include <windows.h>
void show_about_dialog(HWND hParent); void show_about_dialog(HWND hParent);
#endif // ABOUT_DIALOG_H #endif // ABOUT_DIALOG_H

View File

@@ -1,10 +1,10 @@
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
#define APP_NAME "系统信息查看器" #define APP_NAME "系统信息查看器"
#define APP_VERSION "1.0" #define APP_VERSION "1.0"
#define MAX_DISKS 26 #define MAX_DISKS 26
#define MAX_ADAPTERS 10 #define MAX_ADAPTERS 10
#define UPDATE_INTERVAL 5000 // 5秒 #define UPDATE_INTERVAL 5000 // 5秒
#endif // CONFIG_H #endif // CONFIG_H

View File

@@ -1,15 +1,15 @@
#ifndef DISK_INFO_H #ifndef DISK_INFO_H
#define DISK_INFO_H #define DISK_INFO_H
#include <windows.h> #include <windows.h>
typedef struct { typedef struct {
char driveLetter; char driveLetter;
DWORD64 totalBytes; DWORD64 totalBytes;
DWORD64 freeBytes; DWORD64 freeBytes;
char fileSystem[32]; char fileSystem[32];
} DiskInfo; } DiskInfo;
void get_disk_info(DiskInfo* disks, int* count); void get_disk_info(DiskInfo* disks, int* count);
#endif // DISK_INFO_H #endif // DISK_INFO_H

View File

@@ -1,15 +1,15 @@
#ifndef LOGGING_H #ifndef LOGGING_H
#define LOGGING_H #define LOGGING_H
#include <windows.h> #include <windows.h>
typedef enum { typedef enum {
LOG_DEBUG, LOG_DEBUG,
LOG_INFO, LOG_INFO,
LOG_WARNING, LOG_WARNING,
LOG_ERROR LOG_ERROR
} LogLevel; } LogLevel;
void log_message(LogLevel level, const char* format, ...); void log_message(LogLevel level, const char* format, ...);
#endif // LOGGING_H #endif // LOGGING_H

View File

@@ -5,5 +5,7 @@
#include "system_info.h" #include "system_info.h"
void update_main_window(HWND hWnd, SystemInfo* sysInfo); void update_main_window(HWND hWnd, SystemInfo* sysInfo);
void toggle_fullscreen(HWND hWnd);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif // MAIN_WINDOW_H #endif // MAIN_WINDOW_H

View File

@@ -1,23 +1,23 @@
#ifndef NETWORK_INFO_H #ifndef NETWORK_INFO_H
#define NETWORK_INFO_H #define NETWORK_INFO_H
#include <windows.h> #include <windows.h>
#include <iphlpapi.h> #include <iphlpapi.h>
// 确保GetAdaptersInfo函数声明 // 确保GetAdaptersInfo函数声明
#ifndef _IPHLPAPI_ #ifndef _IPHLPAPI_
#define _IPHLPAPI_ #define _IPHLPAPI_
DWORD GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen); DWORD GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen);
#endif #endif
typedef struct { typedef struct {
char adapterName[MAX_ADAPTER_NAME_LENGTH + 4]; char adapterName[MAX_ADAPTER_NAME_LENGTH + 4];
char description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; char description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
char ipAddress[16]; char ipAddress[16];
char macAddress[18]; char macAddress[18];
ULONG speed; // in Mbps ULONG speed; // in Mbps
} NetworkAdapterInfo; } NetworkAdapterInfo;
void get_network_adapters(NetworkAdapterInfo* adapters, int* count); void get_network_adapters(NetworkAdapterInfo* adapters, int* count);
#endif // NETWORK_INFO_H #endif // NETWORK_INFO_H

View File

@@ -1,26 +1,26 @@
#ifndef PERFORMANCE_INFO_H #ifndef PERFORMANCE_INFO_H
#define PERFORMANCE_INFO_H #define PERFORMANCE_INFO_H
#include <windows.h> #include <windows.h>
#include <pdh.h> #include <pdh.h>
// 确保PDH函数声明 // 确保PDH函数声明
#ifndef _PDH_H_ #ifndef _PDH_H_
#define _PDH_H_ #define _PDH_H_
PDH_STATUS PdhOpenQueryA(LPCSTR szDataSource, DWORD_PTR dwUserData, PDH_HQUERY* phQuery); PDH_STATUS PdhOpenQueryA(LPCSTR szDataSource, DWORD_PTR dwUserData, PDH_HQUERY* phQuery);
PDH_STATUS PdhAddCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath, DWORD_PTR dwUserData, PDH_HCOUNTER* phCounter); PDH_STATUS PdhAddCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath, DWORD_PTR dwUserData, PDH_HCOUNTER* phCounter);
PDH_STATUS PdhCollectQueryData(PDH_HQUERY hQuery); PDH_STATUS PdhCollectQueryData(PDH_HQUERY hQuery);
PDH_STATUS PdhGetFormattedCounterValue(PDH_HCOUNTER hCounter, DWORD dwFormat, LPDWORD lpdwType, PPDH_FMT_COUNTERVALUE pValue); PDH_STATUS PdhGetFormattedCounterValue(PDH_HCOUNTER hCounter, DWORD dwFormat, LPDWORD lpdwType, PPDH_FMT_COUNTERVALUE pValue);
#endif #endif
typedef struct { typedef struct {
DWORD cpuUsage; // CPU使用率百分比 DWORD cpuUsage; // CPU使用率百分比
DWORD memoryUsage; // 内存使用率百分比 DWORD memoryUsage; // 内存使用率百分比
DWORD processesCount; // 进程数量 DWORD processesCount; // 进程数量
DWORD threadsCount; // 线程数量 DWORD threadsCount; // 线程数量
DWORD handlesCount; // 句柄数量 DWORD handlesCount; // 句柄数量
} PerformanceInfo; } PerformanceInfo;
void get_performance_info(PerformanceInfo* perfInfo); void get_performance_info(PerformanceInfo* perfInfo);
#endif // PERFORMANCE_INFO_H #endif // PERFORMANCE_INFO_H

View File

@@ -1,11 +1,11 @@
#ifndef RESOURCE_H #ifndef RESOURCE_H
#define RESOURCE_H #define RESOURCE_H
#define IDI_MAIN_ICON 101 #define IDI_MAIN_ICON 101
#define IDR_MAIN_MENU 102 #define IDR_MAIN_MENU 102
#define IDM_EXIT 1001 #define IDM_EXIT 1001
#define IDM_ABOUT 1002 #define IDM_ABOUT 1002
#define IDM_SHOW_INFO 1003 #define IDM_SHOW_INFO 1003
#endif // RESOURCE_H #endif // RESOURCE_H

View File

@@ -1,21 +1,21 @@
#ifndef SYSTEM_INFO_H #ifndef SYSTEM_INFO_H
#define SYSTEM_INFO_H #define SYSTEM_INFO_H
#include <windows.h> #include <windows.h>
typedef struct { typedef struct {
char cpuName[256]; char cpuName[256];
DWORD cpuCores; DWORD cpuCores;
DWORD cpuThreads; DWORD cpuThreads;
MEMORYSTATUSEX memoryStatus; MEMORYSTATUSEX memoryStatus;
SYSTEM_INFO systemInfo; SYSTEM_INFO systemInfo;
OSVERSIONINFOEX osVersion; OSVERSIONINFOEX osVersion;
} SystemInfo; } SystemInfo;
// 初始化系统信息 // 初始化系统信息
void init_system_info(SystemInfo* sysInfo); void init_system_info(SystemInfo* sysInfo);
// 创建主窗口 // 创建主窗口
int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage); int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage);
#endif // SYSTEM_INFO_H #endif // SYSTEM_INFO_H

View File

@@ -1,13 +1,13 @@
#ifndef SYSTEM_TRAY_H #ifndef SYSTEM_TRAY_H
#define SYSTEM_TRAY_H #define SYSTEM_TRAY_H
#include <windows.h> #include <windows.h>
#define WM_TRAYICON (WM_USER + 1) #define WM_TRAYICON (WM_USER + 1)
#define ID_TRAYICON 100 #define ID_TRAYICON 100
void create_tray_icon(HWND hWnd, HICON hIcon); void create_tray_icon(HWND hWnd, HICON hIcon);
void update_tray_icon(HWND hWnd, HICON hIcon, LPCTSTR tooltip); void update_tray_icon(HWND hWnd, HICON hIcon, LPCTSTR tooltip);
void remove_tray_icon(HWND hWnd); void remove_tray_icon(HWND hWnd);
#endif // SYSTEM_TRAY_H #endif // SYSTEM_TRAY_H

View File

@@ -1,18 +1,18 @@
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H
#include <windows.h> #include <windows.h>
// 安全释放内存 // 安全释放内存
#define SAFE_FREE(ptr) if (ptr) { free(ptr); ptr = NULL; } #define SAFE_FREE(ptr) if (ptr) { free(ptr); ptr = NULL; }
// 宽字符转多字节字符串 // 宽字符转多字节字符串
char* wchar_to_mb(const wchar_t* wstr); char* wchar_to_mb(const wchar_t* wstr);
// 多字节字符串转宽字符 // 多字节字符串转宽字符
wchar_t* mb_to_wchar(const char* str); wchar_t* mb_to_wchar(const char* str);
// 获取当前时间字符串 // 获取当前时间字符串
char* get_current_time_string(); char* get_current_time_string();
#endif // UTILS_H #endif // UTILS_H

View File

@@ -1,16 +1,16 @@
#ifndef WINDOW_UTILS_H #ifndef WINDOW_UTILS_H
#define WINDOW_UTILS_H #define WINDOW_UTILS_H
#include <windows.h> #include <windows.h>
#include "system_info.h" #include "system_info.h"
// 窗口过程函数 // 窗口过程函数
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// 创建主窗口 // 创建主窗口
int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage); int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage);
// 注册窗口类 // 注册窗口类
BOOL register_window_class(HINSTANCE hInstance); BOOL register_window_class(HINSTANCE hInstance);
#endif // WINDOW_UTILS_H #endif // WINDOW_UTILS_H

View File

@@ -1,9 +1,9 @@
#include "about_dialog.h" #include "about_dialog.h"
#include <tchar.h> #include <tchar.h>
void show_about_dialog(HWND hParent) { void show_about_dialog(HWND hParent) {
MessageBox(hParent, MessageBox(hParent,
_T("系统信息查看器\n版本 1.0\n\n一个简单的Windows系统信息工具"), _T("系统信息查看器\n版本 1.0\n\n一个简单的Windows系统信息工具"),
_T("关于"), _T("关于"),
MB_OK | MB_ICONINFORMATION); MB_OK | MB_ICONINFORMATION);
} }

View File

@@ -1,40 +1,40 @@
#include "disk_info.h" #include "disk_info.h"
#include <tchar.h> #include <tchar.h>
void get_disk_info(DiskInfo* disks, int* count) { void get_disk_info(DiskInfo* disks, int* count) {
DWORD drives = GetLogicalDrives(); DWORD drives = GetLogicalDrives();
*count = 0; *count = 0;
for (char drive = 'A'; drive <= 'Z'; drive++) { for (char drive = 'A'; drive <= 'Z'; drive++) {
if (drives & (1 << (drive - 'A'))) { if (drives & (1 << (drive - 'A'))) {
TCHAR rootPath[4] = {drive, ':', '\\', '\0'}; TCHAR rootPath[4] = {drive, ':', '\\', '\0'};
TCHAR fileSystem[32]; TCHAR fileSystem[32];
DWORD serialNumber, maxComponentLength, fileSystemFlags; DWORD serialNumber, maxComponentLength, fileSystemFlags;
if (GetVolumeInformation( if (GetVolumeInformation(
rootPath, rootPath,
NULL, 0, NULL, 0,
&serialNumber, &serialNumber,
&maxComponentLength, &maxComponentLength,
&fileSystemFlags, &fileSystemFlags,
fileSystem, sizeof(fileSystem))) { fileSystem, sizeof(fileSystem))) {
DiskInfo* disk = &disks[(*count)++]; DiskInfo* disk = &disks[(*count)++];
disk->driveLetter = drive; disk->driveLetter = drive;
ULARGE_INTEGER freeBytes, totalBytes, totalFreeBytes; ULARGE_INTEGER freeBytes, totalBytes, totalFreeBytes;
if (GetDiskFreeSpaceEx( if (GetDiskFreeSpaceEx(
rootPath, rootPath,
&freeBytes, &freeBytes,
&totalBytes, &totalBytes,
&totalFreeBytes)) { &totalFreeBytes)) {
disk->totalBytes = totalBytes.QuadPart; disk->totalBytes = totalBytes.QuadPart;
disk->freeBytes = freeBytes.QuadPart; disk->freeBytes = freeBytes.QuadPart;
} }
strncpy(disk->fileSystem, fileSystem, sizeof(disk->fileSystem) - 1); strncpy(disk->fileSystem, fileSystem, sizeof(disk->fileSystem) - 1);
disk->fileSystem[sizeof(disk->fileSystem) - 1] = '\0'; disk->fileSystem[sizeof(disk->fileSystem) - 1] = '\0';
} }
} }
} }
} }

View File

@@ -1,41 +1,41 @@
#include "logging.h" #include "logging.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <time.h> #include <time.h>
static const char* level_strings[] = { static const char* level_strings[] = {
"DEBUG", "DEBUG",
"INFO", "INFO",
"WARNING", "WARNING",
"ERROR" "ERROR"
}; };
void log_message(LogLevel level, const char* format, ...) { void log_message(LogLevel level, const char* format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
// 获取当前时间 // 获取当前时间
time_t now; time_t now;
time(&now); time(&now);
struct tm* timeinfo = localtime(&now); struct tm* timeinfo = localtime(&now);
char time_str[20]; char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo); strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo);
// 格式化日志消息 // 格式化日志消息
char message[1024]; char message[1024];
vsnprintf(message, sizeof(message), format, args); vsnprintf(message, sizeof(message), format, args);
// 输出到调试控制台 // 输出到调试控制台
char output[2048]; char output[2048];
snprintf(output, sizeof(output), "[%s] [%s] %s\n", time_str, level_strings[level], message); snprintf(output, sizeof(output), "[%s] [%s] %s\n", time_str, level_strings[level], message);
OutputDebugStringA(output); OutputDebugStringA(output);
// 输出到文件 // 输出到文件
FILE* log_file = fopen("system_info.log", "a"); FILE* log_file = fopen("system_info.log", "a");
if (log_file) { if (log_file) {
fprintf(log_file, "%s", output); fprintf(log_file, "%s", output);
fclose(log_file); fclose(log_file);
} }
va_end(args); va_end(args);
} }

View File

@@ -1,29 +1,29 @@
#include <windows.h> #include <windows.h>
#include <tchar.h> #include <tchar.h>
#include <locale.h> #include <locale.h>
#include "system_info.h" #include "system_info.h"
#include "window_utils.h" #include "window_utils.h"
#include "logging.h" #include "logging.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(lpCmdLine);
UNREFERENCED_PARAMETER(nCmdShow); UNREFERENCED_PARAMETER(nCmdShow);
// 设置控制台编码为UTF-8 // 设置控制台编码为UTF-8
SetConsoleOutputCP(65001); SetConsoleOutputCP(65001);
setlocale(LC_ALL, "chs"); setlocale(LC_ALL, "chs");
// 初始化日志系统 // 初始化日志系统
log_message(LOG_INFO, "应用程序启动"); log_message(LOG_INFO, "应用程序启动");
// 初始化系统信息收集 // 初始化系统信息收集
SystemInfo sysInfo; SystemInfo sysInfo;
init_system_info(&sysInfo); init_system_info(&sysInfo);
// 创建并显示主窗口传递UTF-8编码标识 // 创建并显示主窗口传递UTF-8编码标识
int result = create_main_window(hInstance, &sysInfo, 65001); int result = create_main_window(hInstance, &sysInfo, 65001);
log_message(LOG_INFO, "应用程序退出"); log_message(LOG_INFO, "应用程序退出");
return result; return result;
} }

View File

@@ -1,31 +1,101 @@
#include "main_window.h" #include "main_window.h"
#include "disk_info.h"
#include <tchar.h> #include <tchar.h>
#include <commctrl.h> #include <commctrl.h>
#include <wchar.h> #include <wchar.h>
#include <stdio.h> #include <stdio.h>
#define IDC_INFO_TEXT 1002 #define IDC_INFO_TEXT 1002
#define IDM_FULLSCREEN 1003
// 全屏状态标志
static BOOL g_isFullScreen = FALSE;
// 保存原始窗口位置和大小
static RECT g_windowRect;
void toggle_fullscreen(HWND hWnd) {
g_isFullScreen = !g_isFullScreen;
if (g_isFullScreen) {
// 保存当前窗口位置和大小
GetWindowRect(hWnd, &g_windowRect);
// 设置全屏样式
SetWindowLong(hWnd, GWL_STYLE,
WS_OVERLAPPEDWINDOW & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU));
// 设置全屏尺寸
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi);
SetWindowPos(hWnd, HWND_TOP,
mi.rcMonitor.left,
mi.rcMonitor.top,
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
SWP_FRAMECHANGED);
} else {
// 恢复窗口样式
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
// 恢复原始大小和位置
SetWindowPos(hWnd, NULL,
g_windowRect.left,
g_windowRect.top,
g_windowRect.right - g_windowRect.left,
g_windowRect.bottom - g_windowRect.top,
SWP_FRAMECHANGED);
}
}
void update_main_window(HWND hWnd, SystemInfo* sysInfo) { void update_main_window(HWND hWnd, SystemInfo* sysInfo) {
// 根据全屏状态计算窗口尺寸
int windowWidth, windowHeight;
if (g_isFullScreen) {
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi);
windowWidth = mi.rcMonitor.right - mi.rcMonitor.left;
windowHeight = mi.rcMonitor.bottom - mi.rcMonitor.top;
} else {
// 普通模式下使用70%屏幕尺寸
windowWidth = (int)(GetSystemMetrics(SM_CXSCREEN) * 0.7);
windowHeight = (int)(GetSystemMetrics(SM_CYSCREEN) * 0.7);
}
HWND hInfoText = GetDlgItem(hWnd, IDC_INFO_TEXT); HWND hInfoText = GetDlgItem(hWnd, IDC_INFO_TEXT);
if (!hInfoText) { if (hInfoText) {
// 更新现有控件大小和位置
SetWindowPos(hInfoText, NULL,
30, 50, windowWidth - 60, windowHeight - 80,
SWP_NOZORDER);
} else {
// 创建信息显示控件 // 创建信息显示控件
hInfoText = CreateWindow( hInfoText = CreateWindow(
_T("EDIT"), _T("EDIT"),
_T(""), _T(""),
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY, WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | WS_BORDER,
20, 50, 800, 550, 30, 50, windowWidth - 60, windowHeight - 80,
hWnd, hWnd,
(HMENU)IDC_INFO_TEXT, (HMENU)IDC_INFO_TEXT,
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); NULL);
SendMessage(hInfoText, WM_SETFONT, // 计算动态字体大小
(WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); int fontSize = max(16, windowHeight / 30);
// 创建支持中文的字体
HFONT hFont = CreateFont(
fontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
_T("Microsoft YaHei"));
SendMessage(hInfoText, WM_SETFONT, (WPARAM)hFont, TRUE);
} }
// 使用宽字符处理所有文本 // 使用宽字符处理所有文本
wchar_t infoText[2048]; wchar_t infoText[4096]; // 增大缓冲区以适应磁盘信息
wchar_t cpuNameW[256]; wchar_t cpuNameW[256];
MultiByteToWideChar(CP_UTF8, 0, sysInfo->cpuName, -1, cpuNameW, 256); MultiByteToWideChar(CP_UTF8, 0, sysInfo->cpuName, -1, cpuNameW, 256);
@@ -51,5 +121,26 @@ void update_main_window(HWND hWnd, SystemInfo* sysInfo) {
sysInfo->osVersion.dwMinorVersion, sysInfo->osVersion.dwMinorVersion,
sysInfo->osVersion.dwBuildNumber); sysInfo->osVersion.dwBuildNumber);
// 添加磁盘信息
DiskInfo disks[26];
int diskCount;
get_disk_info(disks, &diskCount);
wchar_t diskInfoText[2048];
swprintf(diskInfoText, 2048,
L"\r\n[磁盘信息]\r\n"
L"磁盘数量: %d\r\n", diskCount);
wcscat(infoText, diskInfoText);
for (int i = 0; i < diskCount; i++) {
swprintf(diskInfoText, 2048,
L"%c: 文件系统: %ls, 总容量: %.2f GB, 剩余容量: %.2f GB\r\n",
disks[i].driveLetter,
disks[i].fileSystem[0] ? L"NTFS" : L"",
(float)disks[i].totalBytes / (1024 * 1024 * 1024),
(float)disks[i].freeBytes / (1024 * 1024 * 1024));
wcscat(infoText, diskInfoText);
}
SetWindowTextW(hInfoText, infoText); SetWindowTextW(hInfoText, infoText);
} }

View File

@@ -1,85 +1,85 @@
#include "network_info.h" #include "network_info.h"
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
// 定义函数指针类型 // 定义函数指针类型
typedef DWORD (WINAPI *GetAdaptersInfoFunc)(_Out_ PIP_ADAPTER_INFO, _Inout_ PULONG); typedef DWORD (WINAPI *GetAdaptersInfoFunc)(_Out_ PIP_ADAPTER_INFO, _Inout_ PULONG);
// 安全的函数指针转换函数 // 安全的函数指针转换函数
static FARPROC safe_get_proc_address(HMODULE module, const char* name) { static FARPROC safe_get_proc_address(HMODULE module, const char* name) {
FARPROC proc = GetProcAddress(module, name); FARPROC proc = GetProcAddress(module, name);
if (!proc) { if (!proc) {
return NULL; return NULL;
} }
return proc; return proc;
} }
// 辅助宏用于安全的函数指针转换 // 辅助宏用于安全的函数指针转换
#define GET_PROC_ADDRESS(module, name, type) \ #define GET_PROC_ADDRESS(module, name, type) \
((type)(void*)safe_get_proc_address(module, name)) ((type)(void*)safe_get_proc_address(module, name))
void get_network_adapters(NetworkAdapterInfo* adapters, int* count) { void get_network_adapters(NetworkAdapterInfo* adapters, int* count) {
HMODULE hModule = LoadLibrary("iphlpapi.dll"); HMODULE hModule = LoadLibrary("iphlpapi.dll");
if (!hModule) return; if (!hModule) return;
// 使用类型安全的函数指针转换 // 使用类型安全的函数指针转换
GetAdaptersInfoFunc pGetAdaptersInfo = GET_PROC_ADDRESS(hModule, "GetAdaptersInfo", GetAdaptersInfoFunc); GetAdaptersInfoFunc pGetAdaptersInfo = GET_PROC_ADDRESS(hModule, "GetAdaptersInfo", GetAdaptersInfoFunc);
if (!pGetAdaptersInfo) { if (!pGetAdaptersInfo) {
FreeLibrary(hModule); FreeLibrary(hModule);
return; return;
} }
PIP_ADAPTER_INFO pAdapterInfo = NULL; PIP_ADAPTER_INFO pAdapterInfo = NULL;
PIP_ADAPTER_INFO pAdapter = NULL; PIP_ADAPTER_INFO pAdapter = NULL;
DWORD dwRetVal = 0; DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO); ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
*count = 0; *count = 0;
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO)); pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) { if (pAdapterInfo == NULL) {
FreeLibrary(hModule); FreeLibrary(hModule);
return; return;
} }
// 第一次调用获取缓冲区大小 // 第一次调用获取缓冲区大小
if (pGetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { if (pGetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo); free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen); pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) { if (pAdapterInfo == NULL) {
FreeLibrary(hModule); FreeLibrary(hModule);
return; return;
} }
} }
if ((dwRetVal = pGetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { if ((dwRetVal = pGetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
pAdapter = pAdapterInfo; pAdapter = pAdapterInfo;
while (pAdapter && *count < MAX_ADAPTERS) { while (pAdapter && *count < MAX_ADAPTERS) {
NetworkAdapterInfo* adapter = &adapters[(*count)++]; NetworkAdapterInfo* adapter = &adapters[(*count)++];
// 复制适配器名称和描述 // 复制适配器名称和描述
strncpy(adapter->adapterName, pAdapter->AdapterName, MAX_ADAPTER_NAME_LENGTH); strncpy(adapter->adapterName, pAdapter->AdapterName, MAX_ADAPTER_NAME_LENGTH);
strncpy(adapter->description, pAdapter->Description, MAX_ADAPTER_DESCRIPTION_LENGTH); strncpy(adapter->description, pAdapter->Description, MAX_ADAPTER_DESCRIPTION_LENGTH);
// 格式化IP地址 // 格式化IP地址
if (pAdapter->IpAddressList.IpAddress.String[0] != '\0') { if (pAdapter->IpAddressList.IpAddress.String[0] != '\0') {
strncpy(adapter->ipAddress, pAdapter->IpAddressList.IpAddress.String, 15); strncpy(adapter->ipAddress, pAdapter->IpAddressList.IpAddress.String, 15);
adapter->ipAddress[15] = '\0'; adapter->ipAddress[15] = '\0';
} else { } else {
strcpy(adapter->ipAddress, "N/A"); strcpy(adapter->ipAddress, "N/A");
} }
// 格式化MAC地址 // 格式化MAC地址
sprintf(adapter->macAddress, "%02X:%02X:%02X:%02X:%02X:%02X", sprintf(adapter->macAddress, "%02X:%02X:%02X:%02X:%02X:%02X",
pAdapter->Address[0], pAdapter->Address[1], pAdapter->Address[0], pAdapter->Address[1],
pAdapter->Address[2], pAdapter->Address[3], pAdapter->Address[2], pAdapter->Address[3],
pAdapter->Address[4], pAdapter->Address[5]); pAdapter->Address[4], pAdapter->Address[5]);
adapter->speed = pAdapter->DhcpEnabled ? 100 : 10; // 简化处理,实际项目中应使用更精确的方法 adapter->speed = pAdapter->DhcpEnabled ? 100 : 10; // 简化处理,实际项目中应使用更精确的方法
pAdapter = pAdapter->Next; pAdapter = pAdapter->Next;
} }
} }
free(pAdapterInfo); free(pAdapterInfo);
} }

View File

@@ -1,137 +1,137 @@
#include "performance_info.h" #include "performance_info.h"
#include <pdh.h> #include <pdh.h>
#include <psapi.h> #include <psapi.h>
#include "performance_info.h" #include "performance_info.h"
#include <windows.h> #include <windows.h>
#include <psapi.h> #include <psapi.h>
// 定义函数指针类型 // 定义函数指针类型
typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)(_In_opt_ LPCSTR, _In_ DWORD_PTR, _Out_ PDH_HQUERY*); typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)(_In_opt_ LPCSTR, _In_ DWORD_PTR, _Out_ PDH_HQUERY*);
typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)(_In_ PDH_HQUERY, _In_ LPCSTR, _In_ DWORD_PTR, _Out_ PDH_HCOUNTER*); typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)(_In_ PDH_HQUERY, _In_ LPCSTR, _In_ DWORD_PTR, _Out_ PDH_HCOUNTER*);
typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)(_In_ PDH_HQUERY); typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)(_In_ PDH_HQUERY);
typedef PDH_STATUS (WINAPI *PdhGetFormattedCounterValueFunc)(_In_ PDH_HCOUNTER, _In_ DWORD, _Out_opt_ LPDWORD, _Out_ PPDH_FMT_COUNTERVALUE); typedef PDH_STATUS (WINAPI *PdhGetFormattedCounterValueFunc)(_In_ PDH_HCOUNTER, _In_ DWORD, _Out_opt_ LPDWORD, _Out_ PPDH_FMT_COUNTERVALUE);
// 安全的函数指针转换函数 // 安全的函数指针转换函数
static FARPROC safe_get_proc_address(HMODULE module, const char* name) { static FARPROC safe_get_proc_address(HMODULE module, const char* name) {
FARPROC proc = GetProcAddress(module, name); FARPROC proc = GetProcAddress(module, name);
if (!proc) { if (!proc) {
return NULL; return NULL;
} }
return proc; return proc;
} }
// 辅助宏用于安全的函数指针转换 // 辅助宏用于安全的函数指针转换
#define GET_PROC_ADDRESS(module, name, type) \ #define GET_PROC_ADDRESS(module, name, type) \
((type)(void*)safe_get_proc_address(module, name)) ((type)(void*)safe_get_proc_address(module, name))
static PDH_HQUERY cpuQuery; static PDH_HQUERY cpuQuery;
static PDH_HCOUNTER cpuTotal; static PDH_HCOUNTER cpuTotal;
static HMODULE hPdhModule = NULL; static HMODULE hPdhModule = NULL;
static PdhOpenQueryFunc pPdhOpenQuery = NULL; static PdhOpenQueryFunc pPdhOpenQuery = NULL;
static PdhAddCounterFunc pPdhAddCounter = NULL; static PdhAddCounterFunc pPdhAddCounter = NULL;
static PdhCollectQueryDataFunc pPdhCollectQueryData = NULL; static PdhCollectQueryDataFunc pPdhCollectQueryData = NULL;
static PdhGetFormattedCounterValueFunc pPdhGetFormattedCounterValue = NULL; static PdhGetFormattedCounterValueFunc pPdhGetFormattedCounterValue = NULL;
void init_performance_counters() { void init_performance_counters() {
hPdhModule = LoadLibrary("pdh.dll"); hPdhModule = LoadLibrary("pdh.dll");
if (!hPdhModule) { if (!hPdhModule) {
return; return;
} }
pPdhOpenQuery = GET_PROC_ADDRESS(hPdhModule, "PdhOpenQueryA", PdhOpenQueryFunc); pPdhOpenQuery = GET_PROC_ADDRESS(hPdhModule, "PdhOpenQueryA", PdhOpenQueryFunc);
pPdhAddCounter = GET_PROC_ADDRESS(hPdhModule, "PdhAddCounterA", PdhAddCounterFunc); pPdhAddCounter = GET_PROC_ADDRESS(hPdhModule, "PdhAddCounterA", PdhAddCounterFunc);
pPdhCollectQueryData = GET_PROC_ADDRESS(hPdhModule, "PdhCollectQueryData", PdhCollectQueryDataFunc); pPdhCollectQueryData = GET_PROC_ADDRESS(hPdhModule, "PdhCollectQueryData", PdhCollectQueryDataFunc);
pPdhGetFormattedCounterValue = GET_PROC_ADDRESS(hPdhModule, "PdhGetFormattedCounterValue", PdhGetFormattedCounterValueFunc); pPdhGetFormattedCounterValue = GET_PROC_ADDRESS(hPdhModule, "PdhGetFormattedCounterValue", PdhGetFormattedCounterValueFunc);
if (pPdhOpenQuery && pPdhAddCounter && pPdhCollectQueryData) { if (pPdhOpenQuery && pPdhAddCounter && pPdhCollectQueryData) {
// 初始化性能计数器 // 初始化性能计数器
if (pPdhOpenQuery(NULL, 0, &cpuQuery) != ERROR_SUCCESS) { if (pPdhOpenQuery(NULL, 0, &cpuQuery) != ERROR_SUCCESS) {
return; return;
} }
// 添加CPU计数器 // 添加CPU计数器
if (pPdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", 0, &cpuTotal) != ERROR_SUCCESS) { if (pPdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", 0, &cpuTotal) != ERROR_SUCCESS) {
return; return;
} }
// 第一次收集数据,用于初始化 // 第一次收集数据,用于初始化
pPdhCollectQueryData(cpuQuery); pPdhCollectQueryData(cpuQuery);
Sleep(1000); // 等待1秒获取基准数据 Sleep(1000); // 等待1秒获取基准数据
pPdhCollectQueryData(cpuQuery); pPdhCollectQueryData(cpuQuery);
} }
} }
void get_performance_info(PerformanceInfo* perfInfo) { void get_performance_info(PerformanceInfo* perfInfo) {
static BOOL initialized = FALSE; static BOOL initialized = FALSE;
if (!initialized) { if (!initialized) {
init_performance_counters(); init_performance_counters();
initialized = TRUE; initialized = TRUE;
} }
// 获取CPU使用率 // 获取CPU使用率
if (pPdhCollectQueryData && pPdhGetFormattedCounterValue) { if (pPdhCollectQueryData && pPdhGetFormattedCounterValue) {
// 第一次收集数据作为基准 // 第一次收集数据作为基准
pPdhCollectQueryData(cpuQuery); pPdhCollectQueryData(cpuQuery);
Sleep(1000); // 等待1秒 Sleep(1000); // 等待1秒
pPdhCollectQueryData(cpuQuery); // 第二次收集数据 pPdhCollectQueryData(cpuQuery); // 第二次收集数据
PDH_FMT_COUNTERVALUE counterVal; PDH_FMT_COUNTERVALUE counterVal;
if (pPdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal) == ERROR_SUCCESS) { if (pPdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal) == ERROR_SUCCESS) {
perfInfo->cpuUsage = (DWORD)counterVal.doubleValue; perfInfo->cpuUsage = (DWORD)counterVal.doubleValue;
} else { } else {
perfInfo->cpuUsage = 0; perfInfo->cpuUsage = 0;
} }
} else { } else {
// 如果PDH不可用使用GetSystemTimes作为备用方案 // 如果PDH不可用使用GetSystemTimes作为备用方案
FILETIME idleTime, kernelTime, userTime; FILETIME idleTime, kernelTime, userTime;
if (GetSystemTimes(&idleTime, &kernelTime, &userTime)) { if (GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
ULONGLONG idle = ((ULONGLONG)idleTime.dwHighDateTime << 32) | idleTime.dwLowDateTime; ULONGLONG idle = ((ULONGLONG)idleTime.dwHighDateTime << 32) | idleTime.dwLowDateTime;
ULONGLONG kernel = ((ULONGLONG)kernelTime.dwHighDateTime << 32) | kernelTime.dwLowDateTime; ULONGLONG kernel = ((ULONGLONG)kernelTime.dwHighDateTime << 32) | kernelTime.dwLowDateTime;
ULONGLONG user = ((ULONGLONG)userTime.dwHighDateTime << 32) | userTime.dwLowDateTime; ULONGLONG user = ((ULONGLONG)userTime.dwHighDateTime << 32) | userTime.dwLowDateTime;
static ULONGLONG prevIdle = 0, prevKernel = 0, prevUser = 0; static ULONGLONG prevIdle = 0, prevKernel = 0, prevUser = 0;
ULONGLONG idleDiff = idle - prevIdle; ULONGLONG idleDiff = idle - prevIdle;
ULONGLONG kernelDiff = kernel - prevKernel; ULONGLONG kernelDiff = kernel - prevKernel;
ULONGLONG userDiff = user - prevUser; ULONGLONG userDiff = user - prevUser;
if (prevIdle != 0 && (kernelDiff + userDiff) > 0) { if (prevIdle != 0 && (kernelDiff + userDiff) > 0) {
perfInfo->cpuUsage = (DWORD)(100.0 - (100.0 * idleDiff) / (kernelDiff + userDiff)); perfInfo->cpuUsage = (DWORD)(100.0 - (100.0 * idleDiff) / (kernelDiff + userDiff));
} else { } else {
perfInfo->cpuUsage = 0; perfInfo->cpuUsage = 0;
} }
prevIdle = idle; prevIdle = idle;
prevKernel = kernel; prevKernel = kernel;
prevUser = user; prevUser = user;
} else { } else {
perfInfo->cpuUsage = 0; perfInfo->cpuUsage = 0;
} }
} }
// 获取内存使用率 // 获取内存使用率
MEMORYSTATUSEX memInfo; MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX); memInfo.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&memInfo); GlobalMemoryStatusEx(&memInfo);
// 使用GlobalMemoryStatusEx获取更精确的内存使用率 // 使用GlobalMemoryStatusEx获取更精确的内存使用率
MEMORYSTATUSEX memStatus; MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus); memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus); GlobalMemoryStatusEx(&memStatus);
perfInfo->memoryUsage = memStatus.dwMemoryLoad; perfInfo->memoryUsage = memStatus.dwMemoryLoad;
// 获取进程和线程数量 // 获取进程和线程数量
PERFORMANCE_INFORMATION perfInfoStruct; PERFORMANCE_INFORMATION perfInfoStruct;
GetPerformanceInfo(&perfInfoStruct, sizeof(perfInfoStruct)); GetPerformanceInfo(&perfInfoStruct, sizeof(perfInfoStruct));
perfInfo->processesCount = perfInfoStruct.ProcessCount; perfInfo->processesCount = perfInfoStruct.ProcessCount;
perfInfo->threadsCount = perfInfoStruct.ThreadCount; perfInfo->threadsCount = perfInfoStruct.ThreadCount;
perfInfo->handlesCount = perfInfoStruct.HandleCount; perfInfo->handlesCount = perfInfoStruct.HandleCount;
} }
void cleanup_performance_counters() { void cleanup_performance_counters() {
if (hPdhModule) { if (hPdhModule) {
FreeLibrary(hPdhModule); FreeLibrary(hPdhModule);
hPdhModule = NULL; hPdhModule = NULL;
} }
} }

View File

@@ -1,26 +1,26 @@
#include "system_info.h" #include "system_info.h"
#include <intrin.h> #include <intrin.h>
void init_system_info(SystemInfo* sysInfo) { void init_system_info(SystemInfo* sysInfo) {
// 获取CPU信息 // 获取CPU信息
int cpuInfo[4] = {0}; int cpuInfo[4] = {0};
__cpuid(cpuInfo, 0x80000002); __cpuid(cpuInfo, 0x80000002);
memcpy(sysInfo->cpuName, cpuInfo, sizeof(cpuInfo)); memcpy(sysInfo->cpuName, cpuInfo, sizeof(cpuInfo));
__cpuid(cpuInfo, 0x80000003); __cpuid(cpuInfo, 0x80000003);
memcpy(sysInfo->cpuName + 16, cpuInfo, sizeof(cpuInfo)); memcpy(sysInfo->cpuName + 16, cpuInfo, sizeof(cpuInfo));
__cpuid(cpuInfo, 0x80000004); __cpuid(cpuInfo, 0x80000004);
memcpy(sysInfo->cpuName + 32, cpuInfo, sizeof(cpuInfo)); memcpy(sysInfo->cpuName + 32, cpuInfo, sizeof(cpuInfo));
// 获取系统信息 // 获取系统信息
GetSystemInfo(&sysInfo->systemInfo); GetSystemInfo(&sysInfo->systemInfo);
sysInfo->cpuCores = sysInfo->systemInfo.dwNumberOfProcessors; sysInfo->cpuCores = sysInfo->systemInfo.dwNumberOfProcessors;
sysInfo->cpuThreads = sysInfo->cpuCores; // 简化处理 sysInfo->cpuThreads = sysInfo->cpuCores; // 简化处理
// 获取内存信息 // 获取内存信息
sysInfo->memoryStatus.dwLength = sizeof(sysInfo->memoryStatus); sysInfo->memoryStatus.dwLength = sizeof(sysInfo->memoryStatus);
GlobalMemoryStatusEx(&sysInfo->memoryStatus); GlobalMemoryStatusEx(&sysInfo->memoryStatus);
// 获取操作系统版本 // 获取操作系统版本
sysInfo->osVersion.dwOSVersionInfoSize = sizeof(sysInfo->osVersion); sysInfo->osVersion.dwOSVersionInfoSize = sizeof(sysInfo->osVersion);
GetVersionEx((LPOSVERSIONINFO)&sysInfo->osVersion); GetVersionEx((LPOSVERSIONINFO)&sysInfo->osVersion);
} }

View File

@@ -1,45 +1,45 @@
#include "system_tray.h" #include "system_tray.h"
#include <shellapi.h> #include <shellapi.h>
#include <strsafe.h> #include <strsafe.h>
void create_tray_icon(HWND hWnd, HICON hIcon) { void create_tray_icon(HWND hWnd, HICON hIcon) {
NOTIFYICONDATA nid = {0}; NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(NOTIFYICONDATA); nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd; nid.hWnd = hWnd;
nid.uID = ID_TRAYICON; nid.uID = ID_TRAYICON;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_TRAYICON; nid.uCallbackMessage = WM_TRAYICON;
nid.hIcon = hIcon; nid.hIcon = hIcon;
#if defined(UNICODE) || defined(_UNICODE) #if defined(UNICODE) || defined(_UNICODE)
StringCbCopyW(nid.szTip, sizeof(nid.szTip), L"系统信息查看器"); StringCbCopyW(nid.szTip, sizeof(nid.szTip), L"系统信息查看器");
#else #else
StringCbCopyA(nid.szTip, sizeof(nid.szTip), "系统信息查看器"); StringCbCopyA(nid.szTip, sizeof(nid.szTip), "系统信息查看器");
#endif #endif
Shell_NotifyIcon(NIM_ADD, &nid); Shell_NotifyIcon(NIM_ADD, &nid);
} }
void update_tray_icon(HWND hWnd, HICON hIcon, LPCTSTR tooltip) { void update_tray_icon(HWND hWnd, HICON hIcon, LPCTSTR tooltip) {
NOTIFYICONDATA nid = {0}; NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(NOTIFYICONDATA); nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd; nid.hWnd = hWnd;
nid.uID = ID_TRAYICON; nid.uID = ID_TRAYICON;
nid.uFlags = NIF_ICON | NIF_TIP; nid.uFlags = NIF_ICON | NIF_TIP;
nid.hIcon = hIcon; nid.hIcon = hIcon;
#if defined(UNICODE) || defined(_UNICODE) #if defined(UNICODE) || defined(_UNICODE)
StringCbCopyW(nid.szTip, sizeof(nid.szTip), tooltip); StringCbCopyW(nid.szTip, sizeof(nid.szTip), tooltip);
#else #else
StringCbCopyA(nid.szTip, sizeof(nid.szTip), tooltip); StringCbCopyA(nid.szTip, sizeof(nid.szTip), tooltip);
#endif #endif
Shell_NotifyIcon(NIM_MODIFY, &nid); Shell_NotifyIcon(NIM_MODIFY, &nid);
} }
void remove_tray_icon(HWND hWnd) { void remove_tray_icon(HWND hWnd) {
NOTIFYICONDATA nid = {0}; NOTIFYICONDATA nid = {0};
nid.cbSize = sizeof(NOTIFYICONDATA); nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd; nid.hWnd = hWnd;
nid.uID = ID_TRAYICON; nid.uID = ID_TRAYICON;
Shell_NotifyIcon(NIM_DELETE, &nid); Shell_NotifyIcon(NIM_DELETE, &nid);
} }

View File

@@ -1,38 +1,38 @@
#include "utils.h" #include "utils.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
char* wchar_to_mb(const wchar_t* wstr) { char* wchar_to_mb(const wchar_t* wstr) {
if (!wstr) return NULL; if (!wstr) return NULL;
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
char* mbstr = (char*)malloc(size); char* mbstr = (char*)malloc(size);
if (mbstr) { if (mbstr) {
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, mbstr, size, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, mbstr, size, NULL, NULL);
} }
return mbstr; return mbstr;
} }
wchar_t* mb_to_wchar(const char* str) { wchar_t* mb_to_wchar(const char* str) {
if (!str) return NULL; if (!str) return NULL;
int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
wchar_t* wstr = (wchar_t*)malloc(size * sizeof(wchar_t)); wchar_t* wstr = (wchar_t*)malloc(size * sizeof(wchar_t));
if (wstr) { if (wstr) {
MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size); MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size);
} }
return wstr; return wstr;
} }
char* get_current_time_string() { char* get_current_time_string() {
time_t now; time_t now;
time(&now); time(&now);
struct tm* timeinfo = localtime(&now); struct tm* timeinfo = localtime(&now);
char* time_str = (char*)malloc(20); char* time_str = (char*)malloc(20);
if (time_str) { if (time_str) {
strftime(time_str, 20, "%Y-%m-%d %H:%M:%S", timeinfo); strftime(time_str, 20, "%Y-%m-%d %H:%M:%S", timeinfo);
} }
return time_str; return time_str;
} }

View File

@@ -21,7 +21,7 @@ BOOL register_window_class(HINSTANCE hInstance) {
wcex.hInstance = hInstance; wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.hbrBackground = CreateSolidBrush(RGB(240, 240, 240));
wcex.lpszMenuName = NULL; wcex.lpszMenuName = NULL;
wcex.lpszClassName = _T("SystemInfoWindowClass"); wcex.lpszClassName = _T("SystemInfoWindowClass");
wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION); wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
@@ -47,10 +47,10 @@ int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage)
HWND hWnd = CreateWindowW( HWND hWnd = CreateWindowW(
L"SystemInfoWindowClass", L"SystemInfoWindowClass",
windowTitle, windowTitle,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN, WS_POPUP | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
450, 300, 800, 600,
NULL, NULL, hInstance, NULL); NULL, NULL, hInstance, sysInfo);
if (!hWnd) { if (!hWnd) {
return 0; return 0;
@@ -75,14 +75,28 @@ int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage)
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) { switch (message) {
case WM_CREATE: { case WM_CREATE: {
// 安全初始化系统信息指针
if (lParam) {
g_sysInfo = (SystemInfo*)((CREATESTRUCT*)lParam)->lpCreateParams;
}
if (!g_sysInfo) {
MessageBoxW(hWnd, L"系统信息初始化失败", L"错误", MB_ICONERROR);
return -1;
}
// 创建显示系统信息的按钮 // 创建显示系统信息的按钮
CreateWindowW(L"BUTTON", L"刷新信息", CreateWindowW(L"BUTTON", L"刷新信息",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_BORDER,
10, 10, 150, 30, 10, 10, 150, 30,
hWnd, (HMENU)IDC_MAIN_BUTTON, hWnd, (HMENU)IDC_MAIN_BUTTON,
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
break; break;
} }
case WM_SIZE: {
// 窗口大小变化时更新布局
update_main_window(hWnd, g_sysInfo);
break;
}
case WM_COMMAND: { case WM_COMMAND: {
if (LOWORD(wParam) == IDC_MAIN_BUTTON) { if (LOWORD(wParam) == IDC_MAIN_BUTTON) {
// 刷新系统信息 // 刷新系统信息
@@ -91,6 +105,25 @@ LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPar
} }
break; break;
} }
case WM_KEYDOWN: {
// F11键切换全屏
if (wParam == VK_F11) {
toggle_fullscreen(hWnd);
update_main_window(hWnd, g_sysInfo);
}
break;
}
case WM_NCCALCSIZE:
if (wParam) {
return 0;
}
break;
case WM_ERASEBKGND: {
RECT rc;
GetClientRect(hWnd, &rc);
FillRect((HDC)wParam, &rc, (HBRUSH)GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND));
return 1;
}
case WM_DESTROY: { case WM_DESTROY: {
PostQuitMessage(0); PostQuitMessage(0);
break; break;

View File

@@ -1,2 +1,2 @@
ren bin\Release\net8.0-windows\win-x86\publish\AppStore.exe kortapp.exe ren bin\Release\net8.0-windows\win-x86\publish\AppStore.exe kortapp-z.exe
ren bin\Release\net8.0-windows\win-x64\publish\AppStore.exe kortapp.exe ren bin\Release\net8.0-windows\win-x64\publish\AppStore.exe kortapp-z.exe

Binary file not shown.

Binary file not shown.

Binary file not shown.

34
tools/Calculator.cs Normal file
View File

@@ -0,0 +1,34 @@
using System;
namespace AppStore
{
/// <summary>
/// 提供基本数学运算功能的工具类
/// </summary>
public static class Calculator
{
/// <summary>
/// 加法运算
/// </summary>
public static double Add(double a, double b) => a + b;
/// <summary>
/// 减法运算
/// </summary>
public static double Subtract(double a, double b) => a - b;
/// <summary>
/// 乘法运算
/// </summary>
public static double Multiply(double a, double b) => a * b;
/// <summary>
/// 除法运算
/// </summary>
public static double Divide(double a, double b)
{
if (b == 0) throw new DivideByZeroException("除数不能为零");
return a / b;
}
}
}

118
tools/CalculatorForm.cs Normal file
View File

@@ -0,0 +1,118 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using AppStore;
namespace AppStore
{
public class CalculatorForm : Form
{
private TextBox display = new TextBox();
private double firstNumber = 0;
private string operation = "";
public CalculatorForm()
{
this.Text = "计算器";
this.Size = new Size(300, 400);
this.StartPosition = FormStartPosition.CenterScreen;
CreateControls();
}
private void CreateControls()
{
// 显示框
display = new TextBox();
display.ReadOnly = true;
display.TextAlign = HorizontalAlignment.Right;
display.Font = new Font("Microsoft YaHei", 14);
display.Size = new Size(260, 40);
display.Location = new Point(20, 20);
this.Controls.Add(display);
// 按钮布局
string[] buttonLabels = {
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
};
for (int i = 0; i < buttonLabels.Length; i++)
{
var btn = new Button();
btn.Text = buttonLabels[i];
btn.Size = new Size(60, 50);
btn.Location = new Point(20 + (i % 4) * 65, 70 + (i / 4) * 55);
btn.Font = new Font("Microsoft YaHei", 12);
btn.Click += ButtonClickHandler;
this.Controls.Add(btn);
}
// 清除按钮
var clearBtn = new Button();
clearBtn.Text = "C";
clearBtn.Size = new Size(260, 40);
clearBtn.Location = new Point(20, 300);
clearBtn.Font = new Font("Microsoft YaHei", 12);
clearBtn.Click += (s, e) => {
display.Text = "";
firstNumber = 0;
operation = "";
};
this.Controls.Add(clearBtn);
}
private void ButtonClickHandler(object sender, EventArgs e)
{
var button = sender as Button;
if (button == null) return;
switch (button.Text)
{
case "+":
case "-":
case "*":
case "/":
if (!string.IsNullOrEmpty(display.Text))
{
firstNumber = double.Parse(display.Text);
operation = button.Text;
display.Text = "";
}
break;
case "=":
if (!string.IsNullOrEmpty(operation) && !string.IsNullOrEmpty(display.Text))
{
double secondNumber = double.Parse(display.Text);
double result = 0;
try
{
result = operation switch
{
"+" => AppStore.Calculator.Add(firstNumber, secondNumber),
"-" => AppStore.Calculator.Subtract(firstNumber, secondNumber),
"*" => AppStore.Calculator.Multiply(firstNumber, secondNumber),
"/" => AppStore.Calculator.Divide(firstNumber, secondNumber),
_ => 0
};
display.Text = result.ToString();
}
catch (DivideByZeroException)
{
MessageBox.Show("除数不能为零", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
operation = "";
}
break;
default:
display.Text += button.Text;
break;
}
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace AppStore
{
public class CalculatorToolCard : ToolCard
{
public CalculatorToolCard()
{
ToolName = "计算器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Calculator.png");
if (File.Exists(iconPath))
{
ToolIcon = Image.FromFile(iconPath);
}
else
{
ToolIcon = SystemIcons.Application.ToBitmap();
}
}
catch
{
ToolIcon = SystemIcons.Application.ToBitmap();
}
this.ToolCardClicked += OnCalculatorCardClicked;
UpdateDisplay();
}
private void OnCalculatorCardClicked(object sender, EventArgs e)
{
var calculatorForm = new CalculatorForm();
calculatorForm.ShowDialog();
}
}
}

View File

@@ -1,136 +1,136 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using ZXing; using ZXing;
using ZXing.Common; using ZXing.Common;
using ZXing.QrCode; using ZXing.QrCode;
namespace AppStore namespace AppStore
{ {
public class QrCodeGeneratorForm : Form public class QrCodeGeneratorForm : Form
{ {
private TextBox? inputTextBox; private TextBox? inputTextBox;
private PictureBox? qrCodePictureBox; private PictureBox? qrCodePictureBox;
private Button? generateButton; private Button? generateButton;
private Button? saveButton; private Button? saveButton;
public QrCodeGeneratorForm() public QrCodeGeneratorForm()
{ {
InitializeComponent(); InitializeComponent();
this.Text = "二维码生成器"; this.Text = "二维码生成器";
this.Size = new Size(500, 600); this.Size = new Size(500, 600);
this.StartPosition = FormStartPosition.CenterScreen; this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedDialog; this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false; this.MaximizeBox = false;
} }
private void InitializeComponent() private void InitializeComponent()
{ {
// 输入文本框 // 输入文本框
inputTextBox = new TextBox(); inputTextBox = new TextBox();
inputTextBox.Multiline = true; inputTextBox.Multiline = true;
inputTextBox.ScrollBars = ScrollBars.Vertical; inputTextBox.ScrollBars = ScrollBars.Vertical;
inputTextBox.Size = new Size(400, 100); inputTextBox.Size = new Size(400, 100);
inputTextBox.Location = new Point(50, 30); inputTextBox.Location = new Point(50, 30);
inputTextBox.PlaceholderText = "请输入要生成二维码的文本内容..."; inputTextBox.PlaceholderText = "请输入要生成二维码的文本内容...";
this.Controls.Add(inputTextBox); this.Controls.Add(inputTextBox);
// 生成按钮 // 生成按钮
generateButton = new Button(); generateButton = new Button();
generateButton.Text = "生成二维码"; generateButton.Text = "生成二维码";
generateButton.Size = new Size(150, 40); generateButton.Size = new Size(150, 40);
generateButton.Location = new Point(50, 150); generateButton.Location = new Point(50, 150);
generateButton.Click += GenerateButton_Click; generateButton.Click += GenerateButton_Click;
this.Controls.Add(generateButton); this.Controls.Add(generateButton);
// 二维码显示区域 // 二维码显示区域
qrCodePictureBox = new PictureBox(); qrCodePictureBox = new PictureBox();
qrCodePictureBox.Size = new Size(300, 300); qrCodePictureBox.Size = new Size(300, 300);
qrCodePictureBox.Location = new Point(100, 220); qrCodePictureBox.Location = new Point(100, 220);
qrCodePictureBox.SizeMode = PictureBoxSizeMode.StretchImage; qrCodePictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
qrCodePictureBox.BorderStyle = BorderStyle.FixedSingle; qrCodePictureBox.BorderStyle = BorderStyle.FixedSingle;
this.Controls.Add(qrCodePictureBox); this.Controls.Add(qrCodePictureBox);
// 保存按钮 // 保存按钮
saveButton = new Button(); saveButton = new Button();
saveButton.Text = "保存二维码"; saveButton.Text = "保存二维码";
saveButton.Size = new Size(150, 40); saveButton.Size = new Size(150, 40);
saveButton.Location = new Point(300, 150); saveButton.Location = new Point(300, 150);
saveButton.Click += SaveButton_Click; saveButton.Click += SaveButton_Click;
this.Controls.Add(saveButton); this.Controls.Add(saveButton);
} }
private void GenerateButton_Click(object sender, EventArgs e) private void GenerateButton_Click(object sender, EventArgs e)
{ {
if (string.IsNullOrWhiteSpace(inputTextBox?.Text)) if (string.IsNullOrWhiteSpace(inputTextBox?.Text))
{ {
MessageBox.Show("请输入要生成二维码的文本内容", "提示", MessageBox.Show("请输入要生成二维码的文本内容", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
try try
{ {
var writer = new BarcodeWriterPixelData var writer = new BarcodeWriterPixelData
{ {
Format = BarcodeFormat.QR_CODE, Format = BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions Options = new QrCodeEncodingOptions
{ {
Width = 300, Width = 300,
Height = 300, Height = 300,
Margin = 1 Margin = 1
} }
}; };
var pixelData = writer.Write(inputTextBox.Text); var pixelData = writer.Write(inputTextBox.Text);
var bitmap = new Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb); var bitmap = new Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, pixelData.Width, pixelData.Height), var bitmapData = bitmap.LockBits(new Rectangle(0, 0, pixelData.Width, pixelData.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb); System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
try try
{ {
System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length); System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length);
} }
finally finally
{ {
bitmap.UnlockBits(bitmapData); bitmap.UnlockBits(bitmapData);
} }
qrCodePictureBox!.Image = bitmap; qrCodePictureBox!.Image = bitmap;
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"生成二维码失败: {ex.Message}", "错误", MessageBox.Show($"生成二维码失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
private void SaveButton_Click(object sender, EventArgs e) private void SaveButton_Click(object sender, EventArgs e)
{ {
if (qrCodePictureBox?.Image == null) if (qrCodePictureBox?.Image == null)
{ {
MessageBox.Show("请先生成二维码", "提示", MessageBox.Show("请先生成二维码", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
using (var saveDialog = new SaveFileDialog()) using (var saveDialog = new SaveFileDialog())
{ {
saveDialog.Filter = "PNG 图片|*.png|JPEG 图片|*.jpg|BMP 图片|*.bmp"; saveDialog.Filter = "PNG 图片|*.png|JPEG 图片|*.jpg|BMP 图片|*.bmp";
saveDialog.Title = "保存二维码图片"; saveDialog.Title = "保存二维码图片";
if (saveDialog.ShowDialog() == DialogResult.OK) if (saveDialog.ShowDialog() == DialogResult.OK)
{ {
try try
{ {
qrCodePictureBox!.Image.Save(saveDialog.FileName); qrCodePictureBox!.Image.Save(saveDialog.FileName);
MessageBox.Show("二维码保存成功", "成功", MessageBox.Show("二维码保存成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBoxButtons.OK, MessageBoxIcon.Information);
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"保存二维码失败: {ex.Message}", "错误", MessageBox.Show($"保存二维码失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
} }
} }
} }
} }

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using Microsoft.Win32;
using System.Security;
using System.Diagnostics;
namespace AppStore
{
/// <summary>
/// 提供Windows自启动项管理功能
/// </summary>
public static class SelfStartingManager
{
private const string RunKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
/// <summary>
/// 获取所有自启动项
/// </summary>
public static Dictionary<string, string> GetAllStartupItems()
{
var items = new Dictionary<string, string>();
try
{
Logger.Log("开始获取自启动项...");
using (var key = Registry.CurrentUser.OpenSubKey(RunKey))
{
if (key != null)
{
Logger.Log($"正在读取注册表键: {RunKey}");
foreach (var valueName in key.GetValueNames())
{
var value = key.GetValue(valueName)?.ToString() ?? string.Empty;
Logger.Log($"找到自启动项: {valueName} = {value}");
items.Add(valueName, value);
}
}
}
Logger.Log($"共找到 {items.Count} 个自启动项");
}
catch (SecurityException ex)
{
Logger.LogError("访问注册表时权限不足", ex);
throw;
}
catch (Exception ex)
{
Logger.LogError("获取自启动项时发生错误", ex);
throw;
}
return items;
}
/// <summary>
/// 添加自启动项
/// </summary>
public static bool AddStartupItem(string name, string path)
{
try
{
Logger.Log($"正在添加自启动项: {name} = {path}");
using (var key = Registry.CurrentUser.OpenSubKey(RunKey, true))
{
key?.SetValue(name, path, RegistryValueKind.String);
key?.Flush();
Logger.Log($"成功添加自启动项: {name}");
// 验证是否添加成功
var verifyValue = key?.GetValue(name)?.ToString();
if (verifyValue != path)
{
Logger.LogError($"验证失败: 自启动项 {name} 未正确添加", null);
return false;
}
return true;
}
}
catch (Exception ex)
{
Logger.LogError($"添加自启动项失败: {name}", ex);
return false;
}
}
/// <summary>
/// 删除自启动项
/// </summary>
public static bool RemoveStartupItem(string name)
{
try
{
Logger.Log($"正在删除自启动项: {name}");
using (var key = Registry.CurrentUser.OpenSubKey(RunKey, true))
{
// 先获取值用于日志记录
var value = key?.GetValue(name)?.ToString() ?? "";
key?.DeleteValue(name, false);
key?.Flush();
Logger.Log($"已删除自启动项: {name} = {value}");
// 验证是否删除成功
if (key?.GetValue(name) != null)
{
Logger.LogError($"验证失败: 自启动项 {name} 未正确删除", null);
return false;
}
return true;
}
}
catch (Exception ex)
{
Logger.LogError($"删除自启动项失败: {name}", ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,337 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Security;
namespace AppStore
{
public class SelfStartingManagerForm : Form
{
private DataGridView dataGridView = new DataGridView();
private Button refreshButton = new Button();
private Button addButton = new Button();
private Button removeButton = new Button();
public SelfStartingManagerForm()
{
InitializeComponent();
LoadStartupItems();
}
private void InitializeComponent()
{
this.Text = "自启动项管理";
this.Size = new Size(600, 400);
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.SuspendLayout();
// 主表格
dataGridView.Dock = DockStyle.Fill;
dataGridView.Margin = new Padding(10);
dataGridView.ReadOnly = true;
dataGridView.AllowUserToAddRows = false;
dataGridView.AllowUserToDeleteRows = false;
dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView.BackgroundColor = SystemColors.Window;
dataGridView.BorderStyle = BorderStyle.Fixed3D;
// 添加列
var iconColumn = new DataGridViewImageColumn();
iconColumn.HeaderText = "图标";
iconColumn.Name = "Icon";
iconColumn.ImageLayout = DataGridViewImageCellLayout.Zoom;
iconColumn.FillWeight = 10;
var nameColumn = new DataGridViewTextBoxColumn();
nameColumn.HeaderText = "名称";
nameColumn.Name = "Name";
nameColumn.FillWeight = 25;
var pathColumn = new DataGridViewTextBoxColumn();
pathColumn.HeaderText = "路径";
pathColumn.Name = "Path";
pathColumn.FillWeight = 65;
dataGridView.Columns.AddRange(iconColumn, nameColumn, pathColumn);
// 按钮面板
var buttonPanel = new Panel();
buttonPanel.Dock = DockStyle.Bottom;
buttonPanel.Height = 60;
buttonPanel.BackColor = SystemColors.Control;
buttonPanel.Padding = new Padding(10);
// 按钮样式
var buttonStyle = new Size(90, 32);
var buttonFont = new Font("Microsoft YaHei", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
// 刷新按钮
refreshButton.Text = "刷新";
refreshButton.Size = buttonStyle;
refreshButton.Font = buttonFont;
refreshButton.Click += (s, e) => LoadStartupItems();
// 添加按钮
addButton.Text = "添加";
addButton.Size = buttonStyle;
addButton.Font = buttonFont;
addButton.Click += AddButton_Click;
// 删除按钮
removeButton.Text = "删除";
removeButton.Size = buttonStyle;
removeButton.Font = buttonFont;
removeButton.Click += RemoveButton_Click;
// 布局按钮
var flowLayout = new FlowLayoutPanel();
flowLayout.Dock = DockStyle.Fill;
flowLayout.FlowDirection = FlowDirection.LeftToRight;
flowLayout.Controls.AddRange(new Control[] { refreshButton, addButton, removeButton });
flowLayout.WrapContents = false;
flowLayout.AutoSize = true;
buttonPanel.Controls.Add(flowLayout);
// 主布局
var mainPanel = new Panel();
mainPanel.Dock = DockStyle.Fill;
mainPanel.Padding = new Padding(10);
mainPanel.Controls.Add(dataGridView);
mainPanel.Controls.Add(buttonPanel);
this.Controls.Add(mainPanel);
this.ResumeLayout(false);
// 初始加载
LoadStartupItems();
}
private void LoadStartupItems()
{
dataGridView.Rows.Clear();
dataGridView.Enabled = false;
refreshButton.Enabled = false;
try
{
Cursor = Cursors.WaitCursor;
var items = SelfStartingManager.GetAllStartupItems();
if (items.Count == 0)
{
dataGridView.Rows.Add("未找到自启动项", "");
}
else
{
foreach (var item in items)
{
Image? iconImage = null;
try
{
if (!string.IsNullOrEmpty(item.Value) && System.IO.File.Exists(item.Value))
{
using (Icon icon = Icon.ExtractAssociatedIcon(item.Value))
{
iconImage = icon?.ToBitmap() ?? SystemIcons.Application.ToBitmap();
}
}
else
{
// 使用默认图标
iconImage = SystemIcons.Application?.ToBitmap() ?? SystemIcons.WinLogo.ToBitmap();
}
}
catch (Exception ex)
{
Logger.LogWarning($"无法加载程序图标: {item.Value}", ex);
// 确保在任何情况下都有有效的图标
iconImage = SystemIcons.Warning?.ToBitmap()
?? SystemIcons.Error?.ToBitmap()
?? SystemIcons.Application?.ToBitmap()
?? SystemIcons.WinLogo?.ToBitmap()
?? SystemIcons.Shield?.ToBitmap()
?? new Bitmap(16, 16);
if (iconImage == null)
{
iconImage = new Bitmap(16, 16);
}
}
dataGridView.Rows.Add(iconImage!, item.Key, item.Value);
}
}
}
catch (SecurityException ex)
{
MessageBox.Show($"权限不足,无法读取注册表: {ex.Message}", "权限错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show($"加载自启动项失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
dataGridView.Enabled = true;
refreshButton.Enabled = true;
Cursor = Cursors.Default;
}
}
private void AddButton_Click(object sender, EventArgs e)
{
using (var dialog = new AddStartupItemDialog())
{
if (dialog.ShowDialog() == DialogResult.OK)
{
if (SelfStartingManager.AddStartupItem(dialog.ItemName, dialog.ItemPath))
{
MessageBox.Show("添加成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadStartupItems();
}
else
{
MessageBox.Show("添加失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
private void RemoveButton_Click(object sender, EventArgs e)
{
if (dataGridView.SelectedRows.Count == 0)
{
MessageBox.Show("请先选择要删除的项", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var selectedItem = dataGridView.SelectedRows[0].Cells["Name"].Value?.ToString() ?? "";
if (MessageBox.Show($"确定要删除 '{selectedItem}' 吗?", "确认删除",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
if (SelfStartingManager.RemoveStartupItem(selectedItem))
{
MessageBox.Show("删除成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadStartupItems();
}
else
{
MessageBox.Show("删除失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
public class AddStartupItemDialog : Form
{
public string ItemName { get; private set; } = "";
public string ItemPath { get; private set; } = "";
private TextBox nameTextBox = new TextBox();
private TextBox pathTextBox = new TextBox();
private Button browseButton = new Button();
private Button okButton = new Button();
private Button cancelButton = new Button();
public AddStartupItemDialog()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.Text = "添加自启动项";
this.Size = new Size(400, 200);
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
// 名称标签和文本框
var nameLabel = new Label();
nameLabel.Text = "名称:";
nameLabel.Location = new Point(20, 20);
nameLabel.Size = new Size(60, 20);
nameTextBox.Location = new Point(90, 20);
nameTextBox.Size = new Size(280, 20);
// 路径标签和文本框
var pathLabel = new Label();
pathLabel.Text = "路径:";
pathLabel.Location = new Point(20, 50);
pathLabel.Size = new Size(60, 20);
pathTextBox.Location = new Point(90, 50);
pathTextBox.Size = new Size(200, 20);
// 浏览按钮
browseButton.Text = "浏览...";
browseButton.Location = new Point(300, 50);
browseButton.Size = new Size(70, 23);
browseButton.Click += BrowseButton_Click;
// 确定按钮
okButton.Text = "确定";
okButton.DialogResult = DialogResult.OK;
okButton.Location = new Point(150, 100);
okButton.Size = new Size(80, 30);
okButton.Click += OkButton_Click;
// 取消按钮
cancelButton.Text = "取消";
cancelButton.DialogResult = DialogResult.Cancel;
cancelButton.Location = new Point(250, 100);
cancelButton.Size = new Size(80, 30);
// 添加控件
this.Controls.Add(nameLabel);
this.Controls.Add(nameTextBox);
this.Controls.Add(pathLabel);
this.Controls.Add(pathTextBox);
this.Controls.Add(browseButton);
this.Controls.Add(okButton);
this.Controls.Add(cancelButton);
}
private void BrowseButton_Click(object sender, EventArgs e)
{
using (var dialog = new OpenFileDialog())
{
dialog.Filter = "可执行文件 (*.exe)|*.exe|所有文件 (*.*)|*.*";
if (dialog.ShowDialog() == DialogResult.OK)
{
pathTextBox.Text = dialog.FileName;
if (string.IsNullOrEmpty(nameTextBox.Text))
{
nameTextBox.Text = System.IO.Path.GetFileNameWithoutExtension(dialog.FileName);
}
}
}
}
private void OkButton_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
{
MessageBox.Show("请输入名称", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
{
MessageBox.Show("请选择路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
ItemName = nameTextBox.Text;
ItemPath = pathTextBox.Text;
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Security.Principal;
namespace AppStore
{
public class SelfStartingManagerToolCard : ToolCard
{
public SelfStartingManagerToolCard()
{
this.ToolName = "自启动管理";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Self_starting_management.png");
if (File.Exists(iconPath))
{
this.ToolIcon = Image.FromFile(iconPath);
}
this.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载自启动管理图标失败", ex);
}
// 订阅点击事件
this.ToolCardClicked += OnSelfStartingManagerClicked;
}
private void OnSelfStartingManagerClicked(object sender, EventArgs e)
{
if (!IsRunningAsAdmin())
{
var result = MessageBox.Show("自启动管理需要管理员权限,是否立即以管理员身份重新运行?",
"权限提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
RestartAsAdmin();
}
return;
}
try
{
var form = new SelfStartingManagerForm();
form.ShowDialog();
}
catch (Exception ex)
{
MessageBox.Show($"启动自启动管理器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Logger.LogError("启动自启动管理器失败", ex);
}
}
private bool IsRunningAsAdmin()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private void RestartAsAdmin()
{
try
{
var startInfo = new ProcessStartInfo
{
FileName = Application.ExecutablePath,
UseShellExecute = true,
Verb = "runas"
};
Process.Start(startInfo);
Application.Exit();
}
catch (Exception ex)
{
MessageBox.Show($"无法以管理员身份重新运行: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Logger.LogError("以管理员身份重新运行失败", ex);
}
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace AppStore.Tools.IconExtractor
{
public static class IconExtractor
{
/// <summary>
/// 获取ICO文件中的实际尺寸列表
/// </summary>
public static List<int> GetIconDimensions(string filePath)
{
var sizes = new List<int>();
try
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (var reader = new BinaryReader(fs))
{
// 读取ICO文件头
reader.ReadUInt16(); // 保留字段
ushort type = reader.ReadUInt16(); // 1=ICO, 2=CUR
ushort count = reader.ReadUInt16(); // 图像数量
if (type != 1) return sizes; // 不是ICO文件
// 读取每个图像目录项
for (int i = 0; i < count; i++)
{
byte width = reader.ReadByte();
byte height = reader.ReadByte();
reader.ReadBytes(14); // 跳过其他字段
// 宽度/高度为0表示256像素
int size = width == 0 ? 256 : width;
sizes.Add(size);
}
}
}
catch
{
// 忽略所有错误,返回空列表
}
return sizes;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int ExtractIconEx(
string lpszFile,
int nIconIndex,
[AllowNull] IntPtr[] phiconLarge,
[AllowNull] IntPtr[] phiconSmall,
int nIcons);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool DestroyIcon(IntPtr handle);
/// <summary>
/// 从文件中提取图标
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="iconIndex">图标索引</param>
/// <param name="largeIcon">是否提取大图标</param>
/// <returns>提取的图标</returns>
public static Icon ExtractIconFromFile(string filePath, int iconIndex = 0)
{
IntPtr[] hIcons = new IntPtr[1];
int extractedCount = ExtractIconEx(filePath, iconIndex, hIcons, null, 1);
if (extractedCount <= 0 || hIcons[0] == IntPtr.Zero)
throw new FileNotFoundException("无法从文件中提取图标");
// 直接返回原始图标
Icon icon = (Icon)Icon.FromHandle(hIcons[0]).Clone();
DestroyIcon(hIcons[0]);
return icon;
}
/// <summary>
/// 将图标保存为文件
/// </summary>
/// <param name="icon">图标对象</param>
/// <param name="outputPath">输出路径</param>
public static void SaveIconToFile(Icon icon, string outputPath)
{
using (FileStream fs = new FileStream(outputPath, FileMode.Create))
{
icon.Save(fs);
}
}
/// <summary>
/// 获取文件中的图标数量
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>图标数量</returns>
public static int GetIconCount(string filePath)
{
return ExtractIconEx(filePath, -1, null, null, 0);
}
}
}

View File

@@ -0,0 +1,19 @@
namespace AppStore.Tools.IconExtractor
{
public static class IconExtractorConstants
{
public const string FileFilter = "可执行文件|*.exe;*.dll;*.ocx;*.cpl|所有文件|*.*";
public const string SaveFilter = "图标文件|*.ico|位图文件|*.bmp|PNG文件|*.png";
public static readonly int[] SupportedSizes = { 16, 24, 32, 48, 64, 128, 256, 512 };
public static readonly int DefaultExtractSize = 256;
public const string ErrorNoIconsFound = "文件不包含任何图标";
public const string ErrorExtractionFailed = "图标提取失败";
public const string ErrorInvalidIndex = "无效的图标索引";
public const string ErrorFileNotFound = "文件未找到";
public const int MaxRecentFiles = 5;
public const int DefaultPreviewSize = 128;
}
}

View File

@@ -0,0 +1,186 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using AppStore.Tools.IconExtractor;
namespace AppStore.Tools.IconExtractor
{
public class IconExtractorForm : Form
{
private Button btnBrowse = new Button();
private Button btnSave = new Button();
private NumericUpDown numIconIndex = new NumericUpDown();
private PictureBox picIcon = new PictureBox();
private Label lblStatus = new Label();
private TextBox txtFilePath = new TextBox();
private ComboBox cmbIconSize = new ComboBox();
private string currentFilePath = string.Empty;
public IconExtractorForm()
{
this.Text = "图标提取器";
this.Size = new Size(500, 550);
this.StartPosition = FormStartPosition.CenterScreen;
InitializeComponents();
}
private void InitializeComponents()
{
// 文件路径文本框
txtFilePath.Location = new Point(20, 20);
txtFilePath.Size = new Size(300, 25);
txtFilePath.ReadOnly = true;
this.Controls.Add(txtFilePath);
// 浏览按钮
btnBrowse.Text = "浏览...";
btnBrowse.Location = new Point(330, 20);
btnBrowse.Size = new Size(80, 25);
btnBrowse.Click += BtnBrowse_Click;
this.Controls.Add(btnBrowse);
// 图标索引标签
Label lblIndex = new Label();
lblIndex.Text = "图标索引:";
lblIndex.Location = new Point(20, 60);
lblIndex.Size = new Size(80, 20);
this.Controls.Add(lblIndex);
// 图标索引选择器
numIconIndex.Location = new Point(100, 60);
numIconIndex.Size = new Size(80, 20);
numIconIndex.Minimum = 0;
numIconIndex.ValueChanged += NumIconIndex_ValueChanged;
this.Controls.Add(numIconIndex);
// 图标预览区域
picIcon.Location = new Point(20, 100);
picIcon.Size = new Size(256, 256);
picIcon.SizeMode = PictureBoxSizeMode.Zoom;
picIcon.BorderStyle = BorderStyle.FixedSingle;
this.Controls.Add(picIcon);
// 索引说明
Label lblIndexHelp = new Label();
lblIndexHelp.Text = "索引号表示文件中的第几个图标从0开始";
lblIndexHelp.Location = new Point(20, 240);
lblIndexHelp.Size = new Size(300, 20);
this.Controls.Add(lblIndexHelp);
// 保存按钮
btnSave.Text = "保存图标";
btnSave.Location = new Point(20, 450);
btnSave.Size = new Size(100, 30);
btnSave.Click += BtnSave_Click;
this.Controls.Add(btnSave);
// 状态标签
lblStatus.Location = new Point(20, 490);
lblStatus.Size = new Size(400, 20);
lblStatus.Text = "请选择包含图标的文件";
this.Controls.Add(lblStatus);
}
private void BtnBrowse_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "可执行文件|*.exe;*.dll;*.ocx;*.cpl|所有文件|*.*";
if (ofd.ShowDialog() == DialogResult.OK)
{
currentFilePath = ofd.FileName;
txtFilePath.Text = currentFilePath;
LoadIconInfo();
}
}
}
private void LoadIconInfo()
{
try
{
int iconCount = IconExtractor.GetIconCount(currentFilePath);
numIconIndex.Maximum = Math.Max(0, iconCount - 1);
// 如果是ICO文件获取实际包含的尺寸
if (currentFilePath.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
{
var sizes = IconExtractor.GetIconDimensions(currentFilePath);
if (sizes.Count > 0)
{
cmbIconSize.Items.Clear();
foreach (var size in sizes)
{
cmbIconSize.Items.Add($"{size}x{size}");
}
// 默认选择最接近256的尺寸
int closest = sizes.OrderBy(s => Math.Abs(s - 256)).First();
cmbIconSize.SelectedIndex = sizes.IndexOf(closest);
}
}
lblStatus.Text = $"找到 {iconCount} 个图标";
ExtractAndDisplayIcon();
}
catch (Exception ex)
{
lblStatus.Text = $"错误: {ex.Message}";
picIcon.Image = null;
}
}
private void NumIconIndex_ValueChanged(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(currentFilePath))
{
ExtractAndDisplayIcon();
}
}
private void ExtractAndDisplayIcon()
{
try
{
Icon icon = IconExtractor.ExtractIconFromFile(currentFilePath, (int)numIconIndex.Value);
picIcon.Image = icon.ToBitmap();
lblStatus.Text = $"显示原始图标 #{numIconIndex.Value}";
}
catch (Exception ex)
{
lblStatus.Text = $"提取图标失败: {ex.Message}";
picIcon.Image = null;
}
}
private void BtnSave_Click(object sender, EventArgs e)
{
if (picIcon.Image == null)
{
MessageBox.Show("没有可保存的图标", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "图标文件|*.ico|位图文件|*.bmp|PNG文件|*.png";
if (sfd.ShowDialog() == DialogResult.OK)
{
try
{
using (Icon icon = IconExtractor.ExtractIconFromFile(currentFilePath, (int)numIconIndex.Value))
{
IconExtractor.SaveIconToFile(icon, sfd.FileName);
lblStatus.Text = $"已保存原始图标到 {sfd.FileName}";
}
}
catch (Exception ex)
{
MessageBox.Show($"保存图标失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
namespace AppStore.Tools.IconExtractor
{
public static class IconExtractorHelper
{
/// <summary>
/// 将图标转换为位图
/// </summary>
public static Bitmap ConvertIconToBitmap(Icon icon, Size size)
{
Bitmap bitmap = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawIcon(icon, new Rectangle(0, 0, size.Width, size.Height));
}
return bitmap;
}
/// <summary>
/// 批量提取文件中的所有图标
/// </summary>
public static List<Icon> ExtractAllIcons(string filePath)
{
List<Icon> icons = new List<Icon>();
int count = IconExtractor.GetIconCount(filePath);
for (int i = 0; i < count; i++)
{
try
{
Icon icon = IconExtractor.ExtractIconFromFile(filePath, i);
icons.Add(icon);
}
catch
{
// 忽略提取失败的图标
}
}
return icons;
}
/// <summary>
/// 将图标保存为PNG格式
/// </summary>
public static void SaveIconAsPng(Icon icon, string outputPath)
{
using (Bitmap bitmap = icon.ToBitmap())
{
bitmap.Save(outputPath, System.Drawing.Imaging.ImageFormat.Png);
}
}
/// <summary>
/// 检查文件是否包含图标
/// </summary>
public static bool HasIcons(string filePath)
{
try
{
return IconExtractor.GetIconCount(filePath) > 0;
}
catch
{
return false;
}
}
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.IO;
using System.Text.Json;
namespace AppStore.Tools.IconExtractor
{
public static class IconExtractorSettings
{
private static readonly string SettingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"kortapp-z", "icon_extractor_settings.json");
public static SettingsData CurrentSettings { get; private set; } = new SettingsData();
public class SettingsData
{
public string LastUsedDirectory { get; set; } = string.Empty;
public string DefaultSaveFormat { get; set; } = "ico";
public int DefaultIconSize { get; set; } = 128;
public string[] RecentFiles { get; set; } = Array.Empty<string>();
}
/// <summary>
/// 加载设置
/// </summary>
public static void LoadSettings()
{
try
{
if (File.Exists(SettingsPath))
{
string json = File.ReadAllText(SettingsPath);
if (!string.IsNullOrWhiteSpace(json))
{
try
{
using (JsonDocument doc = JsonDocument.Parse(json))
{
var root = doc.RootElement;
CurrentSettings = new SettingsData
{
LastUsedDirectory = root.TryGetProperty("LastUsedDirectory", out var dir) ? dir.GetString() ?? string.Empty : string.Empty,
DefaultSaveFormat = root.TryGetProperty("DefaultSaveFormat", out var format) ? format.GetString() ?? "ico" : "ico",
DefaultIconSize = root.TryGetProperty("DefaultIconSize", out var size) ? size.GetInt32() : 128,
RecentFiles = root.TryGetProperty("RecentFiles", out var files) ?
JsonSerializer.Deserialize<string[]>(files.GetRawText()) ?? Array.Empty<string>() :
Array.Empty<string>()
};
}
}
catch
{
CurrentSettings = new SettingsData();
}
}
}
}
catch
{
// 加载失败时使用默认设置
CurrentSettings = new SettingsData();
}
}
/// <summary>
/// 保存设置
/// </summary>
public static void SaveSettings()
{
try
{
if (string.IsNullOrEmpty(SettingsPath))
return;
string directory = Path.GetDirectoryName(SettingsPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
string json = JsonSerializer.Serialize(CurrentSettings);
File.WriteAllText(SettingsPath, json);
}
catch
{
// 忽略保存错误
}
}
/// <summary>
/// 添加最近使用的文件
/// </summary>
public static void AddRecentFile(string filePath)
{
if (CurrentSettings.RecentFiles.Length >= 5)
{
var list = new List<string>(CurrentSettings.RecentFiles);
list.RemoveAt(0);
CurrentSettings.RecentFiles = list.ToArray();
}
var newList = new List<string>(CurrentSettings.RecentFiles);
if (!newList.Contains(filePath))
{
newList.Add(filePath);
CurrentSettings.RecentFiles = newList.ToArray();
SaveSettings();
}
}
/// <summary>
/// 更新最后使用的目录
/// </summary>
public static void UpdateLastUsedDirectory(string directory)
{
if (Directory.Exists(directory) && CurrentSettings.LastUsedDirectory != directory)
{
CurrentSettings.LastUsedDirectory = directory;
SaveSettings();
}
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
namespace AppStore.Tools.IconExtractor
{
public class IconExtractorToolCard : ToolCard
{
public IconExtractorToolCard()
{
ToolName = "图标提取器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "QRcode.png");
if (File.Exists(iconPath))
{
ToolIcon = Image.FromFile(iconPath);
}
else
{
ToolIcon = SystemIcons.Application.ToBitmap();
}
}
catch
{
ToolIcon = SystemIcons.Application.ToBitmap();
}
this.ToolCardClicked += OnIconExtractorCardClicked;
UpdateDisplay();
}
private void OnIconExtractorCardClicked(object sender, EventArgs e)
{
var iconExtractorForm = new IconExtractorForm();
iconExtractorForm.ShowDialog();
}
}
}

View File

@@ -1,103 +1,103 @@
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <string> #include <string>
#include <windows.h> #include <windows.h>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h> #include <stb/stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h> #include <stb/stb_image_write.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
void ShowHelp() { void ShowHelp() {
std::cout << "图片压缩工具 v1.0\n"; std::cout << "图片压缩工具 v1.0\n";
std::cout << "用法: image_compressor [选项] 输入文件 输出文件\n"; std::cout << "用法: image_compressor [选项] 输入文件 输出文件\n";
std::cout << "选项:\n"; std::cout << "选项:\n";
std::cout << " -t <type> 压缩类型: lossy(有损)/lossless(无损) (默认: lossy)\n"; std::cout << " -t <type> 压缩类型: lossy(有损)/lossless(无损) (默认: lossy)\n";
std::cout << " -q <quality> 压缩质量 (1-1000) (默认: 80)\n"; std::cout << " -q <quality> 压缩质量 (1-1000) (默认: 80)\n";
std::cout << " -e 保留EXIF信息 (默认: 不保留)\n"; std::cout << " -e 保留EXIF信息 (默认: 不保留)\n";
std::cout << " -h 显示帮助信息\n"; std::cout << " -h 显示帮助信息\n";
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
// 默认参数 // 默认参数
std::string compressType = "lossy"; std::string compressType = "lossy";
int quality = 80; int quality = 80;
bool keepExif = false; bool keepExif = false;
std::string inputFile; std::string inputFile;
std::string outputFile; std::string outputFile;
// 解析命令行参数 // 解析命令行参数
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
std::string arg = argv[i]; std::string arg = argv[i];
if (arg == "-h") { if (arg == "-h") {
ShowHelp(); ShowHelp();
return 0; return 0;
} else if (arg == "-t" && i + 1 < argc) { } else if (arg == "-t" && i + 1 < argc) {
compressType = argv[++i]; compressType = argv[++i];
} else if (arg == "-q" && i + 1 < argc) { } else if (arg == "-q" && i + 1 < argc) {
quality = std::stoi(argv[++i]); quality = std::stoi(argv[++i]);
} else if (arg == "-e") { } else if (arg == "-e") {
keepExif = true; keepExif = true;
} else if (inputFile.empty()) { } else if (inputFile.empty()) {
inputFile = arg; inputFile = arg;
} else { } else {
outputFile = arg; outputFile = arg;
} }
} }
// 验证参数 // 验证参数
if (inputFile.empty() || outputFile.empty()) { if (inputFile.empty() || outputFile.empty()) {
std::cerr << "错误: 必须指定输入文件和输出文件\n"; std::cerr << "错误: 必须指定输入文件和输出文件\n";
ShowHelp(); ShowHelp();
return 1; return 1;
} }
if (quality < 1 || quality > 1000) { if (quality < 1 || quality > 1000) {
std::cerr << "错误: 压缩质量必须在1-1000之间\n"; std::cerr << "错误: 压缩质量必须在1-1000之间\n";
return 1; return 1;
} }
try { try {
std::cout << "正在压缩: " << inputFile << " -> " << outputFile << std::endl; std::cout << "正在压缩: " << inputFile << " -> " << outputFile << std::endl;
std::cout << "类型: " << compressType << std::endl; std::cout << "类型: " << compressType << std::endl;
std::cout << "质量: " << quality << std::endl; std::cout << "质量: " << quality << std::endl;
std::cout << "保留EXIF: " << (keepExif ? "" : "") << std::endl; std::cout << "保留EXIF: " << (keepExif ? "" : "") << std::endl;
// 加载图片 // 加载图片
int width, height, channels; int width, height, channels;
unsigned char* image = stbi_load(inputFile.c_str(), &width, &height, &channels, 0); unsigned char* image = stbi_load(inputFile.c_str(), &width, &height, &channels, 0);
if (!image) { if (!image) {
std::cerr << "错误: 无法加载图片 " << inputFile << " - " << stbi_failure_reason() << std::endl; std::cerr << "错误: 无法加载图片 " << inputFile << " - " << stbi_failure_reason() << std::endl;
return 1; return 1;
} }
// 压缩图片 // 压缩图片
int result = 0; int result = 0;
if (compressType == "lossy") { if (compressType == "lossy") {
// 有损压缩 (JPEG) // 有损压缩 (JPEG)
int jpeg_quality = quality / 10; // 将1-1000映射到1-100 int jpeg_quality = quality / 10; // 将1-1000映射到1-100
jpeg_quality = std::max(1, std::min(jpeg_quality, 100)); jpeg_quality = std::max(1, std::min(jpeg_quality, 100));
result = stbi_write_jpg(outputFile.c_str(), width, height, channels, image, jpeg_quality); result = stbi_write_jpg(outputFile.c_str(), width, height, channels, image, jpeg_quality);
} else { } else {
// 无损压缩 (PNG) // 无损压缩 (PNG)
result = stbi_write_png(outputFile.c_str(), width, height, channels, image, width * channels); result = stbi_write_png(outputFile.c_str(), width, height, channels, image, width * channels);
} }
// 释放内存 // 释放内存
stbi_image_free(image); stbi_image_free(image);
if (!result) { if (!result) {
std::cerr << "错误: 无法保存图片 " << outputFile << std::endl; std::cerr << "错误: 无法保存图片 " << outputFile << std::endl;
return 1; return 1;
} }
std::cout << "压缩完成\n"; std::cout << "压缩完成\n";
return 0; return 0;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "发生错误: " << e.what() << std::endl; std::cerr << "发生错误: " << e.what() << std::endl;
return 1; return 1;
} }
} }

View File

@@ -1,96 +1,96 @@
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include <windows.h> #include <windows.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
bool IsAdmin() { bool IsAdmin() {
BOOL isAdmin = FALSE; BOOL isAdmin = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID AdminGroup; PSID AdminGroup;
if (AllocateAndInitializeSid(&NtAuthority, 2, if (AllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &AdminGroup)) { 0, 0, 0, 0, 0, 0, &AdminGroup)) {
if (!CheckTokenMembership(NULL, AdminGroup, &isAdmin)) { if (!CheckTokenMembership(NULL, AdminGroup, &isAdmin)) {
isAdmin = FALSE; isAdmin = FALSE;
} }
FreeSid(AdminGroup); FreeSid(AdminGroup);
} }
return isAdmin == TRUE; return isAdmin == TRUE;
} }
void CleanDirectory(const fs::path& dir, size_t& deletedCount, size_t& errorCount) { void CleanDirectory(const fs::path& dir, size_t& deletedCount, size_t& errorCount) {
if (!fs::exists(dir)) { if (!fs::exists(dir)) {
std::cout << "目录不存在: " << dir << std::endl; std::cout << "目录不存在: " << dir << std::endl;
return; return;
} }
for (const auto& entry : fs::directory_iterator(dir)) { for (const auto& entry : fs::directory_iterator(dir)) {
try { try {
if (fs::is_regular_file(entry)) { if (fs::is_regular_file(entry)) {
fs::remove(entry); fs::remove(entry);
deletedCount++; deletedCount++;
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "删除文件失败: " << entry.path() << " - " << e.what() << std::endl; std::cerr << "删除文件失败: " << entry.path() << " - " << e.what() << std::endl;
errorCount++; errorCount++;
} }
} }
} }
int main() { int main() {
try { try {
if (!IsAdmin()) { if (!IsAdmin()) {
std::cerr << "请以管理员身份运行此程序以清理所有目录" << std::endl; std::cerr << "请以管理员身份运行此程序以清理所有目录" << std::endl;
} }
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
size_t totalDeleted = 0; size_t totalDeleted = 0;
size_t totalErrors = 0; size_t totalErrors = 0;
// 定义要清理的目录列表 // 定义要清理的目录列表
std::vector<fs::path> directories = { std::vector<fs::path> directories = {
"C:\\Windows\\Temp", "C:\\Windows\\Temp",
fs::path(std::getenv("USERPROFILE")) / "AppData" / "Local" / "Temp", fs::path(std::getenv("USERPROFILE")) / "AppData" / "Local" / "Temp",
"C:\\Windows\\SoftwareDistribution\\Download", "C:\\Windows\\SoftwareDistribution\\Download",
"C:\\Windows\\Logs", "C:\\Windows\\Logs",
"C:\\Windows\\Minidump" "C:\\Windows\\Minidump"
}; };
// 只有管理员才能清理Prefetch目录 // 只有管理员才能清理Prefetch目录
if (IsAdmin()) { if (IsAdmin()) {
directories.push_back("C:\\Windows\\Prefetch"); directories.push_back("C:\\Windows\\Prefetch");
} }
// 清理每个目录 // 清理每个目录
for (const auto& dir : directories) { for (const auto& dir : directories) {
size_t dirDeleted = 0; size_t dirDeleted = 0;
size_t dirErrors = 0; size_t dirErrors = 0;
std::cout << "正在清理: " << dir << std::endl; std::cout << "正在清理: " << dir << std::endl;
CleanDirectory(dir, dirDeleted, dirErrors); CleanDirectory(dir, dirDeleted, dirErrors);
totalDeleted += dirDeleted; totalDeleted += dirDeleted;
totalErrors += dirErrors; totalErrors += dirErrors;
std::cout << "已删除 " << dirDeleted << " 个文件" << std::endl; std::cout << "已删除 " << dirDeleted << " 个文件" << std::endl;
} }
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "\n清理完成:" << std::endl; std::cout << "\n清理完成:" << std::endl;
std::cout << "总共删除文件数: " << totalDeleted << std::endl; std::cout << "总共删除文件数: " << totalDeleted << std::endl;
std::cout << "总共错误数: " << totalErrors << std::endl; std::cout << "总共错误数: " << totalErrors << std::endl;
std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl; std::cout << "耗时: " << duration.count() << " 毫秒" << std::endl;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "发生错误: " << e.what() << std::endl; std::cerr << "发生错误: " << e.what() << std::endl;
return 1; return 1;
} }
return 0; return 0;
} }

View File

@@ -0,0 +1,21 @@
namespace KortAppZ.Tools.Viewer
{
public interface IImageViewer
{
/// <summary>
/// 加载图片文件
/// </summary>
/// <param name="filePath">图片文件路径</param>
void LoadImage(string filePath);
/// <summary>
/// 显示图片
/// </summary>
void ShowImage();
/// <summary>
/// 关闭图片查看器
/// </summary>
void CloseViewer();
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
namespace KortAppZ.Tools.Viewer
{
public class ImageDisplayForm : Form
{
private PictureBox? pictureBox;
private Image? currentImage;
public ImageDisplayForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.pictureBox = new PictureBox();
//
// pictureBox
//
this.pictureBox.Dock = DockStyle.Fill;
this.pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
this.pictureBox.BackColor = Color.Black;
//
// ImageDisplayForm
//
this.Text = "图片查看";
this.ClientSize = new Size(800, 600);
this.MaximizeBox = false;
this.MinimizeBox = true;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.ControlBox = true;
this.ShowIcon = true;
this.MaximizedBounds = Screen.FromHandle(this.Handle).WorkingArea;
this.Controls.Add(this.pictureBox);
this.FormClosing += new FormClosingEventHandler(ImageDisplayForm_FormClosing);
}
public void LoadImage(string filePath)
{
try
{
if (currentImage != null)
{
currentImage.Dispose();
currentImage = null;
}
currentImage = ImageFileHandler.LoadImage(filePath);
if (pictureBox != null)
{
pictureBox.Image = currentImage;
}
this.Text = $"图片查看 - {Path.GetFileName(filePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"无法加载图片: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ImageDisplayForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (currentImage != null)
{
currentImage.Dispose();
currentImage = null;
}
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Drawing;
using System.IO;
namespace KortAppZ.Tools.Viewer
{
public static class ImageFileHandler
{
private static readonly string[] SupportedExtensions =
{
".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff", ".tif"
};
public static bool IsImageFile(string filePath)
{
if (string.IsNullOrEmpty(filePath))
return false;
string extension = Path.GetExtension(filePath).ToLower();
return Array.Exists(SupportedExtensions, ext => ext == extension);
}
public static Image LoadImage(string filePath)
{
if (!IsImageFile(filePath))
throw new ArgumentException("不支持的图片格式");
if (!File.Exists(filePath))
throw new FileNotFoundException("图片文件不存在", filePath);
try
{
return Image.FromFile(filePath);
}
catch (Exception ex)
{
throw new Exception($"无法加载图片: {ex.Message}", ex);
}
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using KortAppZ.Tools.Viewer;
namespace KortAppZ.Tools.Viewer
{
public class ImageViewerForm : Form
{
private Button? openButton;
public ImageViewerForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.openButton = new Button();
//
// openButton
//
this.openButton.Text = "打开图片";
this.openButton.Size = new Size(120, 40);
this.openButton.Font = new Font("Microsoft YaHei", 10);
this.openButton.Anchor = AnchorStyles.None;
this.openButton.Click += new EventHandler(OpenButton_Click);
// 初始位置
CenterOpenButton();
// 窗体大小变化时重新居中按钮
this.Resize += (s, e) => CenterOpenButton();
//
// ImageViewerForm
//
this.Text = "图片查看器";
this.ClientSize = new Size(400, 300);
this.StartPosition = FormStartPosition.CenterScreen;
this.MinimumSize = new Size(300, 200);
this.MaximizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizedBounds = Screen.FromHandle(this.Handle).WorkingArea;
this.Controls.Add(this.openButton);
}
private void CenterOpenButton()
{
if (openButton != null)
{
openButton.Location = new Point(
(this.ClientSize.Width - openButton.Width) / 2,
(this.ClientSize.Height - openButton.Height) / 2);
}
}
private void OpenButton_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.tif|所有文件|*.*";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
ShowImage(filePath);
}
}
private void ShowImage(string filePath)
{
try
{
if (!ImageFileHandler.IsImageFile(filePath))
{
MessageBox.Show("不支持的图片格式", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
ImageDisplayForm displayForm = new ImageDisplayForm();
displayForm.LoadImage(filePath);
displayForm.Show();
}
catch (Exception ex)
{
MessageBox.Show($"无法加载图片: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using AppStore;
namespace KortAppZ.Tools.Viewer
{
public class ImageViewerToolCard : ToolCard
{
public ImageViewerToolCard()
{
this.ToolName = "图片查看";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "ImageCompressor.png");
if (File.Exists(iconPath))
{
this.ToolIcon = Image.FromFile(iconPath);
}
else
{
this.ToolIcon = SystemIcons.Application.ToBitmap();
}
}
catch
{
this.ToolIcon = SystemIcons.Application.ToBitmap();
}
this.ToolCardClicked += (s, e) => {
try
{
ImageViewerForm viewerForm = new ImageViewerForm();
viewerForm.Show();
}
catch (Exception ex)
{
MessageBox.Show($"打开图片查看器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
this.UpdateDisplay();
}
}
}