Compare commits

...

114 Commits

Author SHA1 Message Date
zsyg
cbf4a2bc2d 修改版本号 2025-07-23 08:20:30 +08:00
zsyg
9269032315 修改freemove描述 2025-07-22 17:28:56 +08:00
zsyg
3c6117933c 修改描述 2025-07-22 15:23:29 +08:00
zsyg
59c6ae623e Add files via upload 2025-07-22 13:59:26 +08:00
zsyg
6ff1506bc2 Add files via upload 2025-07-22 13:57:52 +08:00
zsyg
b8b8b03713 修改cmake配置 2025-07-21 20:54:14 +08:00
zsyg
104aba01da Add files via upload 2025-07-20 13:25:35 +08:00
zsyg
a93e4d67f4 Add files via upload 2025-07-20 13:24:20 +08:00
zsyg
d1c62f4488 Add files via upload 2025-07-20 13:22:11 +08:00
zsyg
f4f4d86faa Add files via upload 2025-07-20 13:21:19 +08:00
zsyg
4c13fe0008 修改版本号 2025-07-20 13:16:24 +08:00
zsyg
6a931e9a85 添加部分软件注释 2025-07-20 10:34:37 +08:00
zsyg
916c944a90 Update README.md 2025-07-20 09:08:15 +08:00
zsyg
996d73f9fe 提供部分软件描述 2025-07-20 08:58:28 +08:00
zsyg
d4afc5a5e7 修改版本号 2025-07-19 20:17:29 +08:00
zsyg
a445bbdc2d 提供avif格式转换 2025-07-19 20:13:18 +08:00
zsyg
0e90195f9a 提供更多文本转换模式 2025-07-19 20:13:01 +08:00
zsyg
11df47ff2e 改为cmake构建 2025-07-17 12:03:29 +08:00
zsyg
7d87d9be7e 添加更多格式 2025-07-14 14:05:13 +08:00
zsyg
5530c6413d 修改版本号 2025-07-14 14:01:31 +08:00
zsyg
b79db2e6bd 添加更多格式 2025-07-14 13:59:37 +08:00
zsyg
6056c984c0 改为静态编译 2025-07-14 11:10:15 +08:00
zsyg
2395c13b60 改为静态编译 2025-07-14 11:09:42 +08:00
zsyg
6624cee1a3 添加软件图标 2025-07-11 16:10:24 +08:00
zsyg
165703575b 修改版本号 2025-07-11 16:08:47 +08:00
zsyg
efc1498a25 添加应用卡片 2025-07-11 16:07:43 +08:00
zsyg
481fc24735 修改版本号 2025-07-11 08:17:57 +08:00
zsyg
bebd804b7a 美化ui 2025-07-11 08:17:26 +08:00
zsyg
4202013265 添加依赖 2025-07-11 08:16:11 +08:00
zsyg
d26ac95dd5 添加图片转换器代码 2025-07-10 13:21:27 +08:00
zsyg
0432c5fa42 添加图片转换器工具图标 2025-07-10 13:21:03 +08:00
zsyg
94da204ca9 提交同转换器程序 2025-07-10 13:20:34 +08:00
zsyg
9770c58166 添加应用卡片 2025-07-10 13:19:02 +08:00
zsyg
c006f78693 修改版本号 2025-07-10 13:18:22 +08:00
zsyg
ab6080cdd0 修改版本号 2025-07-09 12:53:17 +08:00
zsyg
4b77884d32 增强调试信息 2025-07-09 11:36:12 +08:00
zsyg
d45e3029b7 支持显示图标 2025-07-09 11:35:36 +08:00
zsyg
41b056c074 支持显示图标 2025-07-09 11:35:08 +08:00
zsyg
6955655e64 支持显示图标 2025-07-09 11:34:21 +08:00
zsyg
4310557659 添加图标 2025-07-08 21:19:34 +08:00
zsyg
510362688a 添加Code::Blocks 2025-07-08 21:19:02 +08:00
zsyg
34962bba3f 添加Dev-cpp 2025-07-08 21:16:09 +08:00
zsyg
0bab6ccaab 添加构建工具 2025-07-08 21:08:34 +08:00
zsyg
973d984c55 废弃的零宽字符隐藏器
2025-07-07 20:37:34 +08:00
zsyg
280a9122b9 废弃的聊天室代码
😭😭😭
2025-07-07 20:34:34 +08:00
zsyg
837aba38ba Add files via upload 2025-07-07 19:15:34 +08:00
zsyg
92c2a57773 添加应用卡片 2025-07-07 19:14:20 +08:00
zsyg
43027d7953 Add files via upload 2025-07-07 18:07:29 +08:00
zsyg
6d2711da08 删除艺术字 2025-07-07 17:52:13 +08:00
zsyg
e47f905a8c Add files via upload 2025-07-07 16:59:18 +08:00
zsyg
6899e4767f 添加文本转换器程序 2025-07-07 16:53:44 +08:00
zsyg
d5a0564847 添加文本转换器代码 2025-07-07 16:52:56 +08:00
zsyg
525c823397 添加应用图标 2025-07-06 13:47:54 +08:00
zsyg
7db2d8813e Add files via upload 2025-07-06 13:47:04 +08:00
zsyg
7216f62cef 添加OCR代码
这是一个废弃代码
2025-07-06 12:51:43 +08:00
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
295 changed files with 13140 additions and 4019 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

@@ -17,12 +17,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 +51,7 @@ namespace AppStore
// 初始化并添加应用信息 // 初始化并添加应用信息
infoLabel = new Label(); infoLabel = new Label();
infoLabel.Text = "kortapp-z\n版本: 1.1.0\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件"; infoLabel.Text = "kortapp-z\n版本: 1.3.5\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 +69,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 +111,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

@@ -13,13 +13,17 @@ namespace AppStore
{ {
private PictureBox iconBox; private PictureBox iconBox;
private Label nameLabel; private Label nameLabel;
private Panel namePanel;
private Button downloadBtn; private Button downloadBtn;
private ToolTip? toolTip;
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>();
public string AppName { get; set; } = string.Empty; public string AppName { get; set; } = string.Empty;
public Image AppIcon { get; set; } = SystemIcons.Application.ToBitmap(); public Image AppIcon { get; set; } = SystemIcons.Application.ToBitmap();
public string DownloadUrl { get; set; } = string.Empty; public string DownloadUrl { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool ShowDownloadButton { get; set; } = true; public bool ShowDownloadButton { get; set; } = true;
public AppCard() public AppCard()
@@ -27,6 +31,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 +50,153 @@ 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);
}
// 下载按钮 // 初始化ToolTip控件
downloadBtn = new Button(); toolTip = new ToolTip();
downloadBtn.Text = "下载"; toolTip.AutoPopDelay = 5000;
downloadBtn.Size = new Size(100, 32); toolTip.InitialDelay = 500;
downloadBtn.Location = new Point((Width - 100) / 2, 150); toolTip.ReshowDelay = 500;
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); toolTip.ShowAlways = true;
downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat; // 下载按钮 - 添加null检查
downloadBtn.FlatAppearance.BorderSize = 0; if (downloadBtn != null)
downloadBtn.Cursor = Cursors.Hand; {
downloadBtn.Font = new Font("Microsoft YaHei", 9); downloadBtn.Text = "下载";
downloadBtn.Size = new Size(100, 32);
// 按钮悬停效果 downloadBtn.Location = new Point((Width - 100) / 2, 150);
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);
if (!string.IsNullOrEmpty(Description))
{
toolTip.SetToolTip(downloadBtn, Description);
}
}
};
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 +208,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 +270,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 +338,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 {
@@ -252,7 +386,7 @@ namespace AppStore
try try
{ {
var safePath = path ?? CalculatePathFallback(Width, Height, 10); var safePath = path ?? CalculatePathFallback(Width, Height, 10);
// 更严格的null检查包括路径和控件状态 // 更严格的null检查,包括路径和控件状态
if (safePath != null && if (safePath != null &&
safePath.PointCount > 0 && safePath.PointCount > 0 &&
this.IsHandleCreated && this.IsHandleCreated &&
@@ -310,8 +444,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)
@@ -319,16 +463,17 @@ namespace AppStore
try try
{ {
// 更严格的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 +481,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 {

57
AppSearch.cs Normal file
View File

@@ -0,0 +1,57 @@
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

@@ -12,6 +12,7 @@
<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>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -33,4 +34,9 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="SunnyUI" Version="3.8.6" />
<PackageReference Include="ZXing.Net" Version="0.16.9" />
</ItemGroup>
</Project> </Project>

View File

@@ -23,13 +23,41 @@ namespace AppStore
cancelBtn = new Button(); cancelBtn = new Button();
InitializeComponent(); InitializeComponent();
// 监听主题变化
ThemeManager.ThemeChanged += (theme) => {
this.Invoke((MethodInvoker)delegate {
ApplyTheme();
});
};
}
private void ApplyTheme()
{
this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.White
: Color.Black;
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.Black
: Color.White;
cancelBtn.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? SystemColors.Control
: Color.FromArgb(70, 70, 70);
cancelBtn.ForeColor = ThemeManager.TextColor;
} }
private void InitializeComponent() private void InitializeComponent()
{ {
this.Size = new Size(400, 60); this.Size = new Size(400, 60);
this.BackColor = Color.White; this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
this.BorderStyle = BorderStyle.FixedSingle; ? Color.White
: Color.Black;
this.BorderStyle = BorderStyle.None; // 禁用默认边框
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.Black
: Color.White;
this.Paint += DownloadItem_Paint; // 添加自定义绘制
// 文件名标签 // 文件名标签
nameLabel = new Label(); nameLabel = new Label();
@@ -56,6 +84,12 @@ namespace AppStore
cancelBtn.Text = "取消"; cancelBtn.Text = "取消";
cancelBtn.Size = new Size(60, 25); cancelBtn.Size = new Size(60, 25);
cancelBtn.Location = new Point(320, 30); 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; cancelBtn.Click += CancelBtn_Click;
this.Controls.Add(cancelBtn); this.Controls.Add(cancelBtn);
} }
@@ -65,6 +99,17 @@ namespace AppStore
nameLabel.Text = FileName; nameLabel.Text = FileName;
progressBar.Value = Progress; progressBar.Value = Progress;
statusLabel.Text = Status; statusLabel.Text = Status;
this.Invalidate(); // 触发重绘
}
private void DownloadItem_Paint(object sender, PaintEventArgs e)
{
// 自定义边框绘制
using (var pen = new Pen(ThemeManager.BorderColor, 1))
{
e.Graphics.DrawRectangle(pen,
new Rectangle(0, 0, this.Width - 1, this.Height - 1));
}
} }
private void CancelBtn_Click(object sender, EventArgs e) private void CancelBtn_Click(object sender, EventArgs e)

View File

@@ -1,346 +1,508 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Text.Json;
using System.Windows.Forms; using System.Threading.Tasks;
using System.Windows.Forms;
namespace AppStore
{ namespace AppStore
public class DownloadManager {
{ public class DownloadManager
{
[DllImport("shell32.dll")]
private static extern int SHGetKnownFolderPath( [DllImport("shell32.dll")]
[MarshalAs(UnmanagedType.LPStruct)] Guid rfid, private static extern int SHGetKnownFolderPath(
uint dwFlags, [MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
IntPtr hToken, uint dwFlags,
out IntPtr ppszPath); IntPtr hToken,
out IntPtr ppszPath);
private static DownloadManager instance = null!;
public static DownloadManager Instance => instance ??= new DownloadManager(); private static DownloadManager instance = null!;
public static DownloadManager Instance => instance ??= new DownloadManager();
private Process? currentProcess;
public List<DownloadItem> DownloadItems { get; } = new List<DownloadItem>(); private Process? currentProcess;
public List<DownloadItem> DownloadItems { get; } = new List<DownloadItem>();
public event Action<DownloadItem> DownloadAdded = delegate { };
public event Action<DownloadItem> DownloadProgressChanged = delegate { }; public event Action<DownloadItem> DownloadAdded = delegate { };
public event Action<DownloadItem> DownloadCompleted = delegate { }; public event Action<DownloadItem> DownloadProgressChanged = delegate { };
public event Action<DownloadItem> DownloadCompleted = delegate { };
// 内部类封装进程结果
private class ProcessResult // 内部类封装进程结果
{ private class ProcessResult
public int ExitCode { get; set; } = -1; {
public bool HasExited { get; set; } public int ExitCode { get; set; } = -1;
} public bool HasExited { get; set; }
}
// 安全获取进程结果
private ProcessResult GetProcessResult(Process? process) // 安全获取进程结果
{ private ProcessResult GetProcessResult(Process? process)
var result = new ProcessResult(); {
if (process == null) return result; var result = new ProcessResult();
if (process == null || process.StartInfo == null) return result;
try
{ try
if (!process.HasExited) {
{ if (!process.HasExited)
process.WaitForExit(5000); {
} process.WaitForExit(5000);
}
result.HasExited = process.HasExited;
if (result.HasExited) result.HasExited = process.HasExited;
{ if (result.HasExited)
result.ExitCode = process.ExitCode; {
} result.ExitCode = process.ExitCode;
} }
catch }
{ catch
// 忽略所有异常,使用默认值 {
} // 忽略所有异常,使用默认值
}
return result;
} return result;
}
public void StartDownload(string fileName, string url)
{ public void StartDownload(string fileName, string url)
// 从URL获取原始文件名用于显示 {
var uri = new Uri(url); // 从URL获取原始文件名用于显示
var originalFileName = Path.GetFileName(uri.LocalPath); var uri = new Uri(url);
var originalFileName = Path.GetFileName(uri.LocalPath);
var downloadItem = new DownloadItem
{ var downloadItem = new DownloadItem
FileName = originalFileName, // 显示原始文件名 {
Progress = 0, FileName = originalFileName, // 显示原始文件名
Status = "准备下载" Progress = 0,
}; Status = "准备下载"
};
DownloadItems.Add(downloadItem);
DownloadAdded?.Invoke(downloadItem); DownloadItems.Add(downloadItem);
DownloadAdded?.Invoke(downloadItem);
Task.Run(() => DownloadFile(downloadItem, fileName, url));
} Task.Run(() => DownloadFile(downloadItem, fileName, url));
}
private void DownloadFile(DownloadItem downloadItem, string fileName, string url)
{ private void DownloadFile(DownloadItem downloadItem, string fileName, string url)
try {
{ string downloadsDir = string.Empty;
// 设置下载目录为用户文件夹中的Downloads try
// 获取系统下载文件夹路径 {
// 获取系统下载文件夹路径 // 获取并验证下载路径
string downloadsDir; downloadsDir = GetDownloadPath();
IntPtr pathPtr = IntPtr.Zero;
try try
{ {
// 使用SHGetKnownFolderPath API获取下载文件夹 // 检查路径是否有效
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B"); if (string.IsNullOrWhiteSpace(downloadsDir))
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) != 0) {
{ throw new Exception("下载路径为空");
throw new Exception("无法获取下载文件夹路径"); }
}
// 尝试创建目录(如果不存在)
downloadsDir = Marshal.PtrToStringUni(pathPtr); Directory.CreateDirectory(downloadsDir);
}
catch // 验证目录是否可写
{ string testFile = Path.Combine(downloadsDir, "write_test.tmp");
throw new Exception("无法确定下载文件夹位置,请手动指定下载路径"); File.WriteAllText(testFile, "test");
} File.Delete(testFile);
finally }
{ catch (Exception ex)
if (pathPtr != IntPtr.Zero) {
{ // 回退到默认下载路径
Marshal.FreeCoTaskMem(pathPtr); string defaultPath = Path.Combine(
} Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
} "Downloads");
Directory.CreateDirectory(downloadsDir);
Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
downloadsDir = defaultPath;
Directory.CreateDirectory(downloadsDir);
// 构建aria2c路径 }
var aria2cPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "resource", "aria2c.exe");
if (!File.Exists(aria2cPath))
{ // 构建aria2c路径
throw new FileNotFoundException($"找不到aria2c.exe: {aria2cPath}"); var aria2cPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "resource", "aria2c.exe");
}
if (!File.Exists(aria2cPath))
// 设置线程数为16并添加详细日志 {
// 从URL获取原始文件名 throw new FileNotFoundException($"找不到aria2c.exe: {aria2cPath}");
var uri = new Uri(url); }
var originalFileName = Path.GetFileName(uri.LocalPath);
var arguments = $"--out=\"{originalFileName}\" --dir=\"{downloadsDir}\" --split=16 --max-connection-per-server=16 {url}"; // 设置线程数为16并添加详细日志
// 从URL获取原始文件名
var uri = new Uri(url);
currentProcess = new Process var originalFileName = Path.GetFileName(uri.LocalPath);
{ var arguments = $"--out=\"{originalFileName}\" --dir=\"{downloadsDir}\" --split=16 --max-connection-per-server=16 {url}";
StartInfo = new ProcessStartInfo
{
FileName = aria2cPath, currentProcess = new Process
Arguments = arguments, {
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory, StartInfo = new ProcessStartInfo
UseShellExecute = false, {
CreateNoWindow = true, FileName = aria2cPath,
RedirectStandardOutput = true, Arguments = arguments,
RedirectStandardError = true WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
} UseShellExecute = false,
}; CreateNoWindow = true,
RedirectStandardOutput = true,
// 获取目标文件路径 RedirectStandardError = true
string downloadPath = Path.Combine( }
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), };
"Downloads",
fileName); // 获取目标文件路径
string downloadPath = Path.Combine(
long totalSize = 0; Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Downloads",
// 添加进度检测超时机制 fileName);
var progressTimer = new System.Timers.Timer(5000); // 5秒无更新视为完成
progressTimer.Elapsed += (s, e) => { long totalSize = 0;
progressTimer.Stop();
if (downloadItem.Progress < 100) { // 添加进度检测超时机制
downloadItem.Progress = 100; var progressTimer = new System.Timers.Timer(5000); // 5秒无更新视为完成
downloadItem.Status = "下载完成 (100%)"; progressTimer.Elapsed += (s, e) => {
DownloadProgressChanged?.Invoke(downloadItem); progressTimer.Stop();
} if (downloadItem.Progress < 100) {
}; downloadItem.Progress = 100;
downloadItem.Status = "下载完成 (100%)";
currentProcess.OutputDataReceived += (sender, e) => DownloadProgressChanged?.Invoke(downloadItem);
{ }
if (!string.IsNullOrEmpty(e.Data)) };
{
currentProcess.OutputDataReceived += (sender, e) =>
{
// 重置超时计时器 if (!string.IsNullOrEmpty(e.Data))
progressTimer.Stop(); {
progressTimer.Start();
// 解析总大小 // 重置超时计时器
if (e.Data.Contains("Length:")) progressTimer.Stop();
{ progressTimer.Start();
var sizeStr = e.Data.Split(new[]{"Length:"}, StringSplitOptions.RemoveEmptyEntries)[1]
.Split('(')[0].Trim(); // 解析总大小
if (long.TryParse(sizeStr, out totalSize)) if (e.Data.Contains("Length:"))
{ {
var sizeStr = e.Data.Split(new[]{"Length:"}, StringSplitOptions.RemoveEmptyEntries)[1]
} .Split('(')[0].Trim();
} if (long.TryParse(sizeStr, out totalSize))
{
// 解析进度百分比
if (e.Data.Contains("%)")) }
{ }
var start = e.Data.IndexOf("(") + 1;
var end = e.Data.IndexOf("%)"); // 解析进度百分比
if (start > 0 && end > start) if (e.Data.Contains("%)"))
{ {
var progressStr = e.Data.Substring(start, end - start); var start = e.Data.IndexOf("(") + 1;
if (int.TryParse(progressStr, out int progress)) var end = e.Data.IndexOf("%)");
{ if (start > 0 && end > start)
progress = Math.Min(progress, 100); {
downloadItem.Progress = progress; var progressStr = e.Data.Substring(start, end - start);
downloadItem.Status = $"下载中({progress}%)"; if (int.TryParse(progressStr, out int progress))
DownloadProgressChanged?.Invoke(downloadItem); {
} progress = Math.Min(progress, 100);
} downloadItem.Progress = progress;
} downloadItem.Status = $"下载中({progress}%)";
} DownloadProgressChanged?.Invoke(downloadItem);
}; }
}
currentProcess.ErrorDataReceived += (sender, e) => }
{ }
if (!string.IsNullOrEmpty(e.Data)) };
{
currentProcess.ErrorDataReceived += (sender, e) =>
downloadItem.Status = $"错误: {e.Data}"; {
DownloadProgressChanged?.Invoke(downloadItem); if (!string.IsNullOrEmpty(e.Data))
} {
};
downloadItem.Status = $"错误: {e.Data}";
currentProcess.Exited += (sender, e) => DownloadProgressChanged?.Invoke(downloadItem);
{ }
var process = currentProcess; };
if (process == null) return;
currentProcess.Exited += (sender, e) =>
var result = GetProcessResult(process); {
var process = currentProcess;
if (result.ExitCode == 0) if (process == null) return;
{
// 最终状态强制更新 var result = GetProcessResult(process);
downloadItem.Progress = 100;
downloadItem.Status = "下载完成 (100%)"; if (result.ExitCode == 0)
{
// 验证文件完整性 // 最终状态强制更新
string downloadPath = Path.Combine( downloadItem.Progress = 100;
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), downloadItem.Status = "下载完成 (100%)";
"Downloads",
downloadItem.FileName); // 验证文件完整性
string downloadPath = Path.Combine(
if (File.Exists(downloadPath)) Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
{ "Downloads",
downloadItem.FileName);
}
else if (File.Exists(downloadPath))
{ {
} }
else
// 触发界面更新 {
DownloadProgressChanged?.Invoke(downloadItem);
DownloadCompleted?.Invoke(downloadItem); }
downloadItem.UpdateDisplay();
// 触发界面更新
try DownloadProgressChanged?.Invoke(downloadItem);
{ DownloadCompleted?.Invoke(downloadItem);
// 双重确保在主线程显示提示 downloadItem.UpdateDisplay();
if (Application.OpenForms.Count > 0)
{ try
var mainForm = Application.OpenForms[0]; {
mainForm.Invoke((MethodInvoker)delegate { // 双重确保在主线程显示提示
MessageBox.Show(mainForm, if (Application.OpenForms.Count > 0)
$"文件 {downloadItem.FileName} 已成功下载到:\n{Path.Combine(downloadsDir, downloadItem.FileName)}", {
"下载完成", var mainForm = Application.OpenForms[0];
MessageBoxButtons.OK, mainForm.Invoke((MethodInvoker)delegate {
MessageBoxIcon.Information); MessageBox.Show(mainForm,
}); $"文件 {downloadItem.FileName} 已成功下载到:\n{Path.Combine(downloadsDir, downloadItem.FileName)}",
} "下载完成",
else MessageBoxButtons.OK,
{ MessageBoxIcon.Information);
});
} }
} else
catch {
{
} }
} }
else catch
{ {
downloadItem.Status = $"下载失败 (代码: {result.ExitCode})"; }
MessageBox.Show($"文件 {downloadItem.FileName} 下载失败", "下载失败", }
MessageBoxButtons.OK, MessageBoxIcon.Error); else
} {
downloadItem.Status = $"下载失败 (代码: {result.ExitCode})";
DownloadCompleted?.Invoke(downloadItem); MessageBox.Show($"文件 {downloadItem.FileName} 下载失败", "下载失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
try }
{
process?.Dispose(); DownloadCompleted?.Invoke(downloadItem);
}
finally try
{ {
if (process != null) process?.Dispose();
{ }
currentProcess = null; finally
} {
} if (process != null)
{
// 强制更新显示 currentProcess = null;
downloadItem.UpdateDisplay(); }
}; }
// 强制更新显示
downloadItem.UpdateDisplay();
if (!currentProcess.Start()) };
{
throw new Exception("进程启动失败");
}
if (!currentProcess.Start())
currentProcess.BeginOutputReadLine(); {
currentProcess.BeginErrorReadLine(); throw new Exception("进程启动失败");
progressTimer.Start(); }
}
catch (Exception ex) currentProcess.BeginOutputReadLine();
{ currentProcess.BeginErrorReadLine();
downloadItem.Status = $"下载错误: {ex.Message}"; progressTimer.Start();
DownloadCompleted?.Invoke(downloadItem); }
catch (Exception ex)
} {
} string errorDetails = $"下载错误: {ex.Message}\n";
errorDetails += $"目标路径: {downloadsDir}\n";
public void CancelDownload(DownloadItem item) errorDetails += $"URL: {url}";
{
try downloadItem.Status = $"下载失败: {ex.Message}";
{ Logger.LogError(errorDetails, ex);
var process = currentProcess;
if (process == null || process.HasExited || process.StartInfo == null) MessageBox.Show($"下载失败:\n{errorDetails}", "错误",
{ MessageBoxButtons.OK, MessageBoxIcon.Error);
item.Status = "已取消";
DownloadProgressChanged?.Invoke(item); DownloadCompleted?.Invoke(downloadItem);
return; }
} }
process.Kill(); public void CancelDownload(DownloadItem item)
process.Dispose(); {
currentProcess = null; try
{
item.Status = "已取消"; var process = currentProcess;
DownloadProgressChanged?.Invoke(item); if (process?.StartInfo == null || process.HasExited)
} {
catch (Exception ex) item.Status = "已取消";
{ DownloadProgressChanged?.Invoke(item);
item.Status = $"取消失败: {ex.Message}"; return;
DownloadProgressChanged?.Invoke(item); }
}
} 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,190 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
namespace AppStore namespace AppStore
{ {
public class ImageCompressorForm : Form public class ImageCompressorForm : Form
{ {
private Button btnSelectInput = new Button(); private Button btnSelectInput = new Button();
private Button btnSelectOutput = new Button(); private Button btnSelectOutput = new Button();
private Button btnCompress = new Button(); private Button btnCompress = new Button();
private TextBox txtInput = new TextBox(); private TextBox txtInput = new TextBox();
private TextBox txtOutput = new TextBox(); private TextBox txtOutput = new TextBox();
private RadioButton rbLossy = new RadioButton(); private RadioButton rbLossy = new RadioButton();
private RadioButton rbLossless = new RadioButton(); private RadioButton rbLossless = new RadioButton();
private TrackBar tbQuality = new TrackBar(); private TrackBar tbQuality = new TrackBar();
private Label lblQuality = new Label(); private Label lblQuality = new Label();
private CheckBox cbKeepExif = new CheckBox(); private CheckBox cbKeepExif = new CheckBox();
private ProgressBar progressBar = new ProgressBar(); private ProgressBar progressBar = new ProgressBar();
public ImageCompressorForm() public ImageCompressorForm()
{ {
InitializeComponent(); InitializeComponent();
} }
private void InitializeComponent() private void InitializeComponent()
{ {
this.Text = "图片压缩工具"; this.Text = "图片压缩工具";
this.Size = new Size(500, 350); this.Size = new Size(500, 350);
this.StartPosition = FormStartPosition.CenterScreen; this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedDialog; this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false; this.MaximizeBox = false;
// 输入文件选择 // 输入文件选择
btnSelectInput.Text = "选择..."; btnSelectInput.Text = "选择...";
btnSelectInput.Location = new Point(400, 20); btnSelectInput.Location = new Point(400, 20);
btnSelectInput.Click += (s, e) => SelectFile(txtInput); btnSelectInput.Click += (s, e) => SelectFile(txtInput);
this.Controls.Add(btnSelectInput); this.Controls.Add(btnSelectInput);
txtInput.Location = new Point(20, 20); txtInput.Location = new Point(20, 20);
txtInput.Size = new Size(370, 20); txtInput.Size = new Size(370, 20);
txtInput.ReadOnly = true; txtInput.ReadOnly = true;
this.Controls.Add(txtInput); this.Controls.Add(txtInput);
Label lblInput = new Label(); Label lblInput = new Label();
lblInput.Text = "输入文件:"; lblInput.Text = "输入文件:";
lblInput.Location = new Point(20, 0); lblInput.Location = new Point(20, 0);
this.Controls.Add(lblInput); this.Controls.Add(lblInput);
// 输出文件选择 // 输出文件选择
btnSelectOutput.Text = "选择..."; btnSelectOutput.Text = "选择...";
btnSelectOutput.Location = new Point(400, 70); btnSelectOutput.Location = new Point(400, 70);
btnSelectOutput.Click += (s, e) => SelectFile(txtOutput, true); btnSelectOutput.Click += (s, e) => SelectFile(txtOutput, true);
this.Controls.Add(btnSelectOutput); this.Controls.Add(btnSelectOutput);
txtOutput.Location = new Point(20, 70); txtOutput.Location = new Point(20, 70);
txtOutput.Size = new Size(370, 20); txtOutput.Size = new Size(370, 20);
this.Controls.Add(txtOutput); this.Controls.Add(txtOutput);
Label lblOutput = new Label(); Label lblOutput = new Label();
lblOutput.Text = "输出文件:"; lblOutput.Text = "输出文件:";
lblOutput.Location = new Point(20, 50); lblOutput.Location = new Point(20, 50);
this.Controls.Add(lblOutput); this.Controls.Add(lblOutput);
// 压缩类型 // 压缩类型
rbLossy.Text = "有损压缩 (JPEG)"; rbLossy.Text = "有损压缩 (JPEG)";
rbLossy.Location = new Point(20, 110); rbLossy.Location = new Point(20, 110);
rbLossy.Checked = true; rbLossy.Checked = true;
this.Controls.Add(rbLossy); this.Controls.Add(rbLossy);
rbLossless.Text = "无损压缩 (PNG)"; rbLossless.Text = "无损压缩 (PNG)";
rbLossless.Location = new Point(20, 135); rbLossless.Location = new Point(20, 135);
this.Controls.Add(rbLossless); this.Controls.Add(rbLossless);
// 质量设置 // 质量设置
tbQuality.Minimum = 1; tbQuality.Minimum = 1;
tbQuality.Maximum = 1000; tbQuality.Maximum = 1000;
tbQuality.Value = 800; tbQuality.Value = 800;
tbQuality.Location = new Point(20, 190); tbQuality.Location = new Point(20, 190);
tbQuality.Size = new Size(300, 50); tbQuality.Size = new Size(300, 50);
tbQuality.Scroll += (s, e) => lblQuality.Text = $"压缩质量: {tbQuality.Value}"; tbQuality.Scroll += (s, e) => lblQuality.Text = $"压缩质量: {tbQuality.Value}";
this.Controls.Add(tbQuality); this.Controls.Add(tbQuality);
lblQuality.Text = $"压缩质量: {tbQuality.Value}"; lblQuality.Text = $"压缩质量: {tbQuality.Value}";
lblQuality.Location = new Point(20, 170); lblQuality.Location = new Point(20, 170);
this.Controls.Add(lblQuality); this.Controls.Add(lblQuality);
// EXIF选项 // EXIF选项
cbKeepExif.Text = "保留EXIF信息"; cbKeepExif.Text = "保留EXIF信息";
cbKeepExif.Location = new Point(20, 240); cbKeepExif.Location = new Point(20, 240);
this.Controls.Add(cbKeepExif); this.Controls.Add(cbKeepExif);
// 压缩按钮 // 压缩按钮
btnCompress.Text = "开始压缩"; btnCompress.Text = "开始压缩";
btnCompress.Location = new Point(20, 280); btnCompress.Location = new Point(20, 280);
btnCompress.Size = new Size(460, 30); btnCompress.Size = new Size(460, 30);
btnCompress.Click += BtnCompress_Click; btnCompress.Click += BtnCompress_Click;
this.Controls.Add(btnCompress); this.Controls.Add(btnCompress);
// 调整窗体大小 // 调整窗体大小
this.Size = new Size(500, 370); this.Size = new Size(500, 370);
} }
private void SelectFile(TextBox target, bool isSave = false) private void SelectFile(TextBox target, bool isSave = false)
{ {
var dialog = isSave ? new SaveFileDialog() : new OpenFileDialog() as FileDialog; var dialog = isSave ? new SaveFileDialog() : new OpenFileDialog() as FileDialog;
dialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*"; dialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*";
if (dialog.ShowDialog() == DialogResult.OK) if (dialog.ShowDialog() == DialogResult.OK)
{ {
target.Text = dialog.FileName; target.Text = dialog.FileName;
} }
} }
private void BtnCompress_Click(object sender, EventArgs e) private void BtnCompress_Click(object sender, EventArgs e)
{ {
if (string.IsNullOrEmpty(txtInput.Text) || !File.Exists(txtInput.Text)) if (string.IsNullOrEmpty(txtInput.Text) || !File.Exists(txtInput.Text))
{ {
MessageBox.Show("请选择有效的输入文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("请选择有效的输入文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
if (string.IsNullOrEmpty(txtOutput.Text)) if (string.IsNullOrEmpty(txtOutput.Text))
{ {
MessageBox.Show("请指定输出文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("请指定输出文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
btnCompress.Enabled = false; btnCompress.Enabled = false;
try try
{ {
string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe"); string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe");
if (!File.Exists(toolPath)) if (!File.Exists(toolPath))
{ {
MessageBox.Show("图片压缩工具未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("图片压缩工具未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
string args = $"\"{txtInput.Text}\" \"{txtOutput.Text}\""; string args = $"\"{txtInput.Text}\" \"{txtOutput.Text}\"";
args += $" -t {(rbLossy.Checked ? "lossy" : "lossless")}"; args += $" -t {(rbLossy.Checked ? "lossy" : "lossless")}";
args += $" -q {tbQuality.Value}"; args += $" -q {tbQuality.Value}";
if (cbKeepExif.Checked) args += " -e"; if (cbKeepExif.Checked) args += " -e";
var process = new Process(); var process = new Process();
process.StartInfo.FileName = toolPath; process.StartInfo.FileName = toolPath;
process.StartInfo.Arguments = args; process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false; process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true; process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += (s, ev) => { process.OutputDataReceived += (s, ev) => {
if (!string.IsNullOrEmpty(ev.Data)) if (!string.IsNullOrEmpty(ev.Data))
Console.WriteLine(ev.Data); Console.WriteLine(ev.Data);
}; };
process.ErrorDataReceived += (s, ev) => { process.ErrorDataReceived += (s, ev) => {
if (!string.IsNullOrEmpty(ev.Data)) if (!string.IsNullOrEmpty(ev.Data))
Console.Error.WriteLine(ev.Data); Console.Error.WriteLine(ev.Data);
}; };
process.Start(); process.Start();
process.BeginOutputReadLine(); process.BeginOutputReadLine();
process.BeginErrorReadLine(); process.BeginErrorReadLine();
process.WaitForExit(); process.WaitForExit();
if (process.ExitCode == 0) if (process.ExitCode == 0)
{ {
MessageBox.Show("图片压缩完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("图片压缩完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
} }
else else
{ {
MessageBox.Show("图片压缩失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("图片压缩失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"压缩过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show($"压缩过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
finally finally
{ {
btnCompress.Enabled = true; btnCompress.Enabled = true;
progressBar.Visible = false; progressBar.Visible = false;
} }
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,16 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
namespace AppStore namespace AppStore
{ {
static class Program static class Program
{ {
[STAThread] [STAThread]
static void Main() static void Main()
{ {
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm()); Application.Run(new MainForm());
} }
} }
} }

230
README.md
View File

@@ -1,116 +1,114 @@
# Kortapp-z - Windows应用商店 --主张软件开源、免费拒绝广告 # Kortapp-z - Windows应用商店 --主张软件开源、免费,拒绝广告
小立一个flag从不接受广告不停更新 小立一个flag从不接受广告,不停更新
## 项目开源行为 ## 项目开源行为
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都会被删除 ## 项目简介
## 项目简介 一个简单的Windows应用商店应用,提供软件下载和管理功能。
提供软件管理、下载管理、内置工具使用等功能
一个简单的Windows应用商店应用提供软件下载和管理功能。
提供软件管理、下载管理、内置工具使用等功能 ## 功能特点
## 功能特点 - 简洁的软件下载界面
- 下载进度管理
- 简洁的软件下载界面 - 支持后台下载
- 下载进度管理 - 美观的应用卡片展示
- 支持后台下载 - 功能化、结构化的代码处理
- 美观的应用卡片展示
- 功能化、结构化的代码处理 ## 构建与打包
## 构建与打包 ### 系统要求
- .NET 8.0 SDK
### 系统要求 - Windows 10/11
- .NET 8.0 SDK
- Windows 10/11 ### 打包指令
### 打包指令
#### 32位版本
```bash
#### 32位版本 dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:Optimize=true /p:DebugType=None
```bash ```
dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:Optimize=true /p:DebugType=None
``` #### 64位版本
```bash
#### 64位版本 dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None
```bash ```
dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None
``` 打包后的可执行文件将包含指定的应用程序图标,输出路径为:
```
打包后的可执行文件将包含指定的应用程序图标,输出路径为: bin\Release\net8.0-windows\[platform]\publish
``` ```
bin\Release\net8.0-windows\[platform]\publish
``` ### 高级选项
- 使用`--self-contained false`生成框架依赖包(默认)
### 高级选项 - 使用`/p:Optimize=true`启用代码优化(默认)
- 使用`--self-contained false`生成框架依赖包(默认) - 使用`/p:DebugType=None`禁用调试符号生成(默认)
- 使用`/p:Optimize=true`启用代码优化(默认 - 添加`-p:PublishTrimmed=true`可减小包体积(实验性
- 使用`/p:DebugType=None`禁用调试符号生成(默认)
- 添加`-p:PublishTrimmed=true`可减小包体积(实验性) ## 项目结构
## 项目结构 ```
kortapp-z/
``` ├── MainForm.cs # 主窗体逻辑
kortapp-z/ ├── DownloadManager.cs # 下载管理
├── MainForm.cs # 主窗体逻辑 ├── AppCard.cs # 应用卡片控件
├── DownloadManager.cs # 下载管理 ├── DownloadItem.cs # 下载项控件
├── AppCard.cs # 应用卡片控件 ├── img/ # 图片资源
├── DownloadItem.cs # 下载项控 │ ├── ico/ # 图标文
── img/ # 图片资源 │ └── png/ # 应用截图
│ ├── ico/ # 图标文件 └── resource/ # 资源文件
└── png/ # 应用截图 └── aria2c.exe # 下载工具
└── resource/ # 资源文件 ```
└── aria2c.exe # 下载工具
``` ## 运行要求
## 运行要求 - .NET 8.0运行时(如果使用框架依赖发布)
- Windows 10或更高版本
- .NET 8.0运行时(如果使用框架依赖发布)
- Windows 10或更高版本 ## 许可证
## 许可证 MIT License
MIT License Copyright (c) 2025 zsyg
Copyright (c) 2025 zsyg ## 其他网站
## 其他网站 gitee镜像仓库:https://gitee.com/chr_super/kortapp-z (目前已经停止维护)
gitee镜像仓库:https://gitee.com/chr_super/kortapp-z (目前已经停止维护) ## 维护
## 维护 由于gitee我没怎么用,而且操作麻烦,gitee镜像将不会继续同步,有懂得人可以帮我搞下镜像吗,qq 3872006562,也可以b站直接私信,我会在readme中鸣谢的,谢谢各位
由于和Daye发生了矛盾,所以windowscleaner将永远不上架,我要自己努力
由于gitee我没怎么用而且操作麻烦gitee镜像将不会继续同步有懂得人可以帮我搞下镜像吗qq 3872006562也可以b站直接私信我会在readme中鸣谢的谢谢各位 提示:由于github上传文件的限制,img/png/NET.png,请改名为.NET.png,否则程序可能出现无法预料的问题
由于和Daye发生了矛盾所以windowscleaner将永远不上架我要自己努力
提示由于github上传文件的限制img/png/NET.png请改名为.NET.png否则程序可能出现无法预料的问题 每一个人都可以通过PR添加属于自己的合法软件
每一个人都可以通过PR添加属于自己的合法软件 作者邮箱:
```
作者邮箱: 3872006562@qq.com
``` ```
3872006562@qq.com
``` 作者qq号:
```
作者qq号: 3872006562
``` ```
3872006562
``` qq群
```
qq群 1043867176
``` ```
1043867176
``` b站账号
```
b站账号 Zayisynth
``` ```
Zayisynth
```

View File

@@ -1,53 +1,223 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using System.Drawing;
namespace AppStore using System.Text.Json;
{
public class SettingsUserControl : UserControl namespace AppStore
{ {
private Button btnCleanLogs; public class SettingsUserControl : UserControl
{
public SettingsUserControl() private Button btnCleanLogs;
{ private Button btnLightTheme;
this.Dock = DockStyle.Fill; private Button btnDarkTheme;
this.BackColor = Color.White;
public SettingsUserControl()
// 设置顶部内边距 {
this.Padding = new Padding(0, 30, 0, 0); this.Dock = DockStyle.Fill;
ThemeManager.ApplyTheme(this);
btnCleanLogs = new Button();
btnCleanLogs.Text = "清理日志"; // 设置顶部内边距
btnCleanLogs.Size = new Size(150, 40); this.Padding = new Padding(0, 30, 0, 0);
btnCleanLogs.Location = new Point((this.Width - 150) / 2, 50); // 调整Y坐标为50靠近顶部
btnCleanLogs.Font = new Font("Microsoft YaHei", 10); // 主题切换按钮
btnCleanLogs.Anchor = AnchorStyles.Top; // 添加顶部锚点 btnLightTheme = new Button();
btnCleanLogs.Click += (s, e) => CleanLogs(); btnLightTheme.Text = "浅色模式";
this.Controls.Add(btnCleanLogs); btnLightTheme.Size = new Size(150, 40);
} btnLightTheme.Location = new Point((this.Width - 320) / 2, 50);
btnLightTheme.Font = new Font("Microsoft YaHei", 10);
private void CleanLogs() btnLightTheme.Anchor = AnchorStyles.Top;
{ btnLightTheme.Click += (s, e) => SwitchTheme(ThemeManager.ThemeMode.Light);
try this.Controls.Add(btnLightTheme);
{
string logCleanerPath = Path.Combine("resource", "log_cleaner.exe"); btnDarkTheme = new Button();
btnDarkTheme.Text = "深色模式";
if (File.Exists(logCleanerPath)) btnDarkTheme.Size = new Size(150, 40);
{ btnDarkTheme.Location = new Point(btnLightTheme.Right + 20, 50);
Process.Start(logCleanerPath); btnDarkTheme.Font = new Font("Microsoft YaHei", 10);
MessageBox.Show("日志清理程序已启动", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); btnDarkTheme.Anchor = AnchorStyles.Top;
} btnDarkTheme.Click += (s, e) => SwitchTheme(ThemeManager.ThemeMode.Dark);
else this.Controls.Add(btnDarkTheme);
{
MessageBox.Show("日志清理程序未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); // 清理日志按钮
} btnCleanLogs = new Button();
} btnCleanLogs.Text = "清理日志";
catch (Exception ex) btnCleanLogs.Size = new Size(150, 40);
{ btnCleanLogs.Location = new Point((this.Width - 150) / 2, 110);
Logger.LogError("清理日志时出错", ex); btnCleanLogs.Font = new Font("Microsoft YaHei", 10);
MessageBox.Show($"清理日志时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 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;
}
}
}
}

View File

@@ -1,4 +1,4 @@
首先如果希望编译程序那么必须安装.NET8.0 SDK 首先,如果希望编译程序,那么必须安装.NET8.0 SDK
下载链接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
使用一下指令编译 使用一下指令编译

View File

@@ -1,4 +1,4 @@
如果希望运行那么必须安装.NET8.0 SDK 如果希望运行,那么必须安装.NET8.0 SDK
下载链接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

153
ThemeManager.cs Normal file
View File

@@ -0,0 +1,153 @@
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(250, 250, 250);
private static readonly Color LightControlBackground = Color.FromArgb(245, 245, 245);
private static readonly Color LightText = Color.FromArgb(40, 40, 40);
private static readonly Color LightButtonHover = Color.FromArgb(235, 235, 235);
private static readonly Color LightButtonActive = Color.FromArgb(225, 225, 225);
private static readonly Color LightAccent = Color.FromArgb(0, 120, 215);
private static readonly Color LightAccentLight = Color.FromArgb(0, 150, 245);
// 深色主题颜色
private static readonly Color DarkBackground = Color.FromArgb(25, 25, 25);
private static readonly Color DarkControlBackground = Color.FromArgb(40, 40, 40);
private static readonly Color DarkText = Color.FromArgb(245, 245, 245);
private static readonly Color DarkButtonHover = Color.FromArgb(55, 55, 55);
private static readonly Color DarkButtonActive = Color.FromArgb(65, 65, 65);
private static readonly Color DarkBorder = Color.FromArgb(70, 70, 70);
private static readonly Color DarkAccent = Color.FromArgb(0, 150, 245);
private static readonly Color DarkAccentLight = Color.FromArgb(0, 180, 255);
// 浅色主题边框颜色
private static readonly Color LightBorder = Color.FromArgb(200, 200, 200);
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 Color AccentColor =>
_currentTheme == ThemeMode.Light ? LightAccent : DarkAccent;
public static Color AccentLightColor =>
_currentTheme == ThemeMode.Light ? LightAccentLight : DarkAccentLight;
public static int ControlRadius => 8;
public static int FormRadius => 12;
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;
button.BackColor = ControlBackgroundColor;
button.Font = new Font(button.Font, FontStyle.Bold);
button.Padding = new Padding(10, 5, 10, 5);
}
foreach (Control childControl in control.Controls)
{
ApplyThemeToControl(childControl);
}
}
}
}

View File

@@ -1,77 +1,113 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
namespace AppStore namespace AppStore
{ {
public class ToolCard : UserControl public class ToolCard : UserControl
{ {
private PictureBox iconBox = new PictureBox(); private PictureBox iconBox = new PictureBox();
private Label nameLabel = new Label(); private Label nameLabel = new Label();
private Panel namePanel = new Panel();
public string ToolName { get; set; } = string.Empty; private Color borderColor = SystemColors.ControlDark;
public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap();
public string ToolName { get; set; } = string.Empty;
// 自定义点击事件初始化为空委托 public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap();
public event EventHandler ToolCardClicked = delegate {};
// 自定义点击事件初始化为空委托
public ToolCard() public event EventHandler ToolCardClicked = delegate {};
{
// 启用双缓冲 public ToolCard()
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | {
ControlStyles.ResizeRedraw, true); // 启用双缓冲
InitializeComponent(); this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
} ControlStyles.ResizeRedraw, true);
InitializeComponent();
private void InitializeComponent() }
{
this.Size = new Size(240, 220); private void InitializeComponent()
this.BackColor = Color.White; {
this.Padding = new Padding(5); this.Size = new Size(240, 220);
this.BorderStyle = BorderStyle.FixedSingle; this.BackColor = Color.White;
this.Padding = new Padding(5);
// 工具图标 this.BorderStyle = BorderStyle.FixedSingle;
iconBox = new PictureBox();
iconBox.Size = new Size(80, 80); // 工具图标
iconBox.Location = new Point((Width - 80) / 2, 15); iconBox = new PictureBox();
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; iconBox.Size = new Size(80, 80);
this.Controls.Add(iconBox); iconBox.Location = new Point((Width - 80) / 2, 15);
iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
// 工具名称 this.Controls.Add(iconBox);
nameLabel = new Label();
nameLabel.AutoSize = false; // 工具名称 - 使用Panel包裹Label实现边框颜色
nameLabel.Size = new Size(Width - 20, 30); namePanel = new Panel();
nameLabel.Location = new Point(10, 100); namePanel.Size = new Size(Width - 20, 30);
nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold); namePanel.Location = new Point(10, 100);
nameLabel.TextAlign = ContentAlignment.MiddleCenter; namePanel.Paint += (sender, e) => {
this.Controls.Add(nameLabel); ControlPaint.DrawBorder(e.Graphics, namePanel.ClientRectangle,
borderColor, ButtonBorderStyle.Solid);
// 打开按钮 };
var openButton = new Button();
openButton.Text = "打开工具"; nameLabel = new Label();
openButton.Size = new Size(100, 30); nameLabel.Dock = DockStyle.Fill;
openButton.Location = new Point((Width - 100) / 2, 140); nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold);
openButton.BackColor = Color.FromArgb(0, 120, 215); nameLabel.TextAlign = ContentAlignment.MiddleCenter;
openButton.ForeColor = Color.White; namePanel.Controls.Add(nameLabel);
openButton.FlatStyle = FlatStyle.Flat;
openButton.FlatAppearance.BorderSize = 0; // 初始主题设置
openButton.Cursor = Cursors.Hand; UpdateLabelTheme();
// 按钮点击直接触发ToolCardClicked事件
openButton.Click += (s, e) => { // 订阅主题变化事件
ToolCardClicked?.Invoke(this, e); ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
};
this.Controls.Add(openButton); this.Controls.Add(namePanel);
// 设置按钮悬停效果 // 打开按钮
openButton.BackColor = Color.FromArgb(0, 120, 215); var openButton = new Button();
openButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 100, 180); openButton.Text = "打开工具";
openButton.FlatAppearance.MouseDownBackColor = Color.FromArgb(0, 80, 160); openButton.Size = new Size(100, 30);
} openButton.Location = new Point((Width - 100) / 2, 140);
openButton.BackColor = Color.FromArgb(0, 120, 215);
public void UpdateDisplay() openButton.ForeColor = Color.White;
{ openButton.FlatStyle = FlatStyle.Flat;
nameLabel.Text = ToolName; openButton.FlatAppearance.BorderSize = 0;
iconBox.Image = ToolIcon; 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,64 @@
#include <windows.h> #include <windows.h>
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include <cmath> #include <cmath>
#include <d2d1.h> #include <d2d1.h>
// 高性能边框路径生成 // 高性能边框路径生成
void GenerateBorderPath(int width, int height, int radius, const char* outputPath) { void GenerateBorderPath(int width, int height, int radius, const char* outputPath) {
std::ofstream out(outputPath); std::ofstream out(outputPath);
const float pi = 3.1415926f; const float pi = 3.1415926f;
const int segments = 24; // 高分段数确保平滑 const int segments = 24; // 高分段数确保平滑
std::vector<POINTFLOAT> points; std::vector<POINTFLOAT> points;
// 优化后的圆角路径生成 // 优化后的圆角路径生成
auto addArc = [&](float startAngle, float endAngle, float cx, float cy) { auto addArc = [&](float startAngle, float endAngle, float cx, float cy) {
for (int i = 0; i <= segments; ++i) { for (int i = 0; i <= segments; ++i) {
float angle = startAngle + (endAngle - startAngle) * i / segments; float angle = startAngle + (endAngle - startAngle) * i / segments;
points.push_back({ points.push_back({
cx + radius * cosf(angle), cx + radius * cosf(angle),
cy + radius * sinf(angle) cy + radius * sinf(angle)
}); });
} }
}; };
// 左上角 // 左上角
addArc(pi, 3*pi/2, radius, radius); addArc(pi, 3*pi/2, radius, radius);
// 右上角 // 右上角
addArc(3*pi/2, 2*pi, width - radius, radius); addArc(3*pi/2, 2*pi, width - radius, radius);
// 右下角 // 右下角
addArc(0, pi/2, width - radius, height - radius); addArc(0, pi/2, width - radius, height - radius);
// 左下角 // 左下角
addArc(pi/2, pi, radius, height - radius); addArc(pi/2, pi, radius, height - radius);
// 闭合路径 // 闭合路径
points.push_back(points[0]); points.push_back(points[0]);
// 写入优化格式 // 写入优化格式
if (out.is_open()) { if (out.is_open()) {
for (const auto& p : points) { for (const auto& p : points) {
out << p.x << "," << p.y << "\n"; out << p.x << "," << p.y << "\n";
} }
} }
} }
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
int argc; int argc;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argc != 5) return 1; if (argc != 5) return 1;
int width = _wtoi(argv[1]); int width = _wtoi(argv[1]);
int height = _wtoi(argv[2]); int height = _wtoi(argv[2]);
int radius = _wtoi(argv[3]); int radius = _wtoi(argv[3]);
char outputPath[MAX_PATH]; char outputPath[MAX_PATH];
wcstombs(outputPath, argv[4], MAX_PATH); wcstombs(outputPath, argv[4], MAX_PATH);
GenerateBorderPath(width, height, radius, outputPath); GenerateBorderPath(width, height, radius, outputPath);
return 0; return 0;
} }

View File

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

View File

@@ -1,49 +1,49 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于kortapp-z</title> <title>关于kortapp-z</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<header> <header>
<h1>关于kortapp-z</h1> <h1>关于kortapp-z</h1>
<p>了解我们的项目和团队</p> <p>了解我们的项目和团队</p>
</header> </header>
<main> <main>
<section class="about-project"> <section class="about-project">
<h2>项目简介</h2> <h2>项目简介</h2>
<p>kortapp-z是一个专为Windows系统设计的实用工具集合旨在提供一站式的系统优化和管理解决方案。</p> <p>kortapp-z是一个专为Windows系统设计的实用工具集合,旨在提供一站式的系统优化和管理解决方案。</p>
<p>项目始于2025年由热爱效率工具的开发者创建。</p> <p>项目始于2025年,由热爱效率工具的开发者创建。</p>
</section> </section>
<section class="team"> <section class="team">
<h2>开发团队</h2> <h2>开发团队</h2>
<p>我们的团队由经验丰富的开发人员组成专注于创建高质量的系统工具。</p> <p>我们的团队由经验丰富的开发人员组成,专注于创建高质量的系统工具。</p>
<ul> <ul>
<li>核心开发者: 1人</li> <li>核心开发者: 1人</li>
<li>UI设计师: 1人</li> <li>UI设计师: 1人</li>
<li>测试工程师: 1人</li> <li>测试工程师: 1人</li>
</ul> </ul>
</section> </section>
<section class="contact"> <section class="contact">
<h2>联系我们</h2> <h2>联系我们</h2>
<p>如有任何问题或建议欢迎通过GitHub提交issue或pull request。</p> <p>如有任何问题或建议,欢迎通过GitHub提交issue或pull request。</p>
<a href="https://github.com/zs-yg/kortapp-z/issues" target="_blank">提交反馈</a> <a href="https://github.com/zs-yg/kortapp-z/issues" target="_blank">提交反馈</a>
</section> </section>
</main> </main>
<footer> <footer>
<nav> <nav>
<a href="index.html">首页</a> <a href="index.html">首页</a>
<a href="features.html">功能</a> <a href="features.html">功能</a>
</nav> </nav>
<p>&copy; 2025 kortapp-z 团队</p> <p>&copy; 2025 kortapp-z 团队</p>
</footer> </footer>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,49 +1,49 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>kortapp-z功能详情</title> <title>kortapp-z功能详情</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<header> <header>
<h1>kortapp-z功能详情</h1> <h1>kortapp-z功能详情</h1>
<p>探索我们强大的工具集</p> <p>探索我们强大的工具集</p>
</header> </header>
<main> <main>
<section class="feature-list"> <section class="feature-list">
<h2>核心功能</h2> <h2>核心功能</h2>
<article class="feature"> <article class="feature">
<h3>应用程序管理</h3> <h3>应用程序管理</h3>
<p>批量安装、卸载(目前没有)和更新应用程序(目前没有)管理启动项(目前没有)。</p> <p>批量安装、卸载(目前没有)和更新应用程序(目前没有),管理启动项(目前没有)。</p>
<p>优势:集中管理所有应用节省时间避免系统臃肿。</p> <p>优势:集中管理所有应用,节省时间,避免系统臃肿。</p>
</article> </article>
<article class="feature"> <article class="feature">
<h3>资源监控(之后可能在内置工具里有)</h3> <h3>资源监控(之后可能在内置工具里有)</h3>
<p>实时监控CPU、内存、磁盘和网络使用情况。</p> <p>实时监控CPU、内存、磁盘和网络使用情况。</p>
<p>优势:直观的图表展示及时发现资源瓶颈。</p> <p>优势:直观的图表展示,及时发现资源瓶颈。</p>
</article> </article>
<article class="feature"> <article class="feature">
<h3>文件管理(之后可能在内置工具里有)</h3> <h3>文件管理(之后可能在内置工具里有)</h3>
<p>高级文件搜索、批量重命名和快速文件分类。</p> <p>高级文件搜索、批量重命名和快速文件分类。</p>
<p>优势:提升文件管理效率支持正则表达式搜索。</p> <p>优势:提升文件管理效率,支持正则表达式搜索。</p>
</article> </article>
</section> </section>
</main> </main>
<footer> <footer>
<nav> <nav>
<a href="index.html">首页</a> <a href="index.html">首页</a>
<a href="about.html">关于</a> <a href="about.html">关于</a>
</nav> </nav>
<p>&copy; 2025 kortapp-z 团队</p> <p>&copy; 2025 kortapp-z 团队</p>
</footer> </footer>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

BIN
img/png/Azul_JDKs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
img/png/Cataclysm-DDA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
img/png/ClamAV.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/png/CodeBlocks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
img/png/Dev-C++.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
img/png/Final2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
img/png/FreeMove.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
img/png/NoteGen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 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/VideoCaptioner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
img/png/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
img/png/github_cli.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
img/png/gophish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
img/png/hashcat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 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

BIN
img/png/pocketbase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

BIN
img/png/powershell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 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

View File

@@ -1,18 +1,18 @@
; 脚本由 Inno Setup 脚本向导生成。 ; 脚本由 Inno Setup 脚本向导生成。
; 有关创建 Inno Setup 脚本文件的详细信息请参阅帮助文档! ; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z" #define MyAppName "kortapp-z"
#define MyAppVersion "1.1.0" #define MyAppVersion "1.3.5"
#define MyAppPublisher "zsyg" #define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z" #define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.exe" #define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + "" #define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe" #define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
[Setup] [Setup]
; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。 ; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
; (若要生成新的 GUID请在 IDE 中单击 "工具|生成 GUID"。) ; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
AppId={{8020EC01-6133-40BB-8B8B-0EB71E49696C} AppId={{8020EC01-6133-40BB-8B8B-0EB71E49696C}
AppName={#MyAppName} AppName={#MyAppName}
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}
@@ -27,7 +27,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName}
; 除 Arm 上的 x64 和 Windows 11 之外的任何平台上。 ; 除 Arm 上的 x64 和 Windows 11 之外的任何平台上。
ArchitecturesAllowed=x64compatible ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" 要求 ; "ArchitecturesInstallIn64BitMode=x64compatible" 要求
; 安装可以在 x64 或 Arm 上的 Windows 11 上以“64 位模式”完成 ; 安装可以在 x64 或 Arm 上的 Windows 11 上以“64 位模式”完成,
; 这意味着它应该使用本机 64 位 Program Files 目录和 ; 这意味着它应该使用本机 64 位 Program Files 目录和
; 注册表的 64 位视图。 ; 注册表的 64 位视图。
ArchitecturesInstallIn64BitMode=x64compatible ArchitecturesInstallIn64BitMode=x64compatible

View File

@@ -1,18 +1,18 @@
; 脚本由 Inno Setup 脚本向导生成。 ; 脚本由 Inno Setup 脚本向导生成。
; 有关创建 Inno Setup 脚本文件的详细信息请参阅帮助文档! ; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z" #define MyAppName "kortapp-z"
#define MyAppVersion "1.1.0" #define MyAppVersion "1.3.5"
#define MyAppPublisher "zsyg" #define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z" #define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.exe" #define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + "" #define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe" #define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
[Setup] [Setup]
; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。 ; 注意AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
; (若要生成新的 GUID请在 IDE 中单击 "工具|生成 GUID"。) ; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
AppId={{BF1944C3-CD0C-4119-A340-49C54961D48B} AppId={{BF1944C3-CD0C-4119-A340-49C54961D48B}
AppName={#MyAppName} AppName={#MyAppName}
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}

View File

@@ -1,65 +1,65 @@
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <chrono> #include <chrono>
namespace fs = std::filesystem; namespace fs = std::filesystem;
int main() { int main() {
try { try {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
// 定义日志目录路径 // 定义日志目录路径
fs::path logDir; fs::path logDir;
#ifdef _WIN32 #ifdef _WIN32
// Windows系统获取AppData路径 // Windows系统获取AppData路径
char* appData = nullptr; char* appData = nullptr;
size_t len = 0; size_t len = 0;
if (_dupenv_s(&appData, &len, "APPDATA") == 0 && appData != nullptr) { if (_dupenv_s(&appData, &len, "APPDATA") == 0 && appData != nullptr) {
logDir = fs::path(appData) / "zsyg" / "kortapp-z" / ".logs"; logDir = fs::path(appData) / "zsyg" / "kortapp-z" / ".logs";
free(appData); free(appData);
} else { } else {
std::cerr << "无法获取APPDATA环境变量" << std::endl; std::cerr << "无法获取APPDATA环境变量" << std::endl;
return 1; return 1;
} }
#else #else
// 非Windows系统使用默认路径 // 非Windows系统使用默认路径
logDir = fs::path(getenv("HOME")) / ".zsyg" / "kortapp-z" / ".logs"; logDir = fs::path(getenv("HOME")) / ".zsyg" / "kortapp-z" / ".logs";
#endif #endif
size_t deletedCount = 0; size_t deletedCount = 0;
size_t errorCount = 0; size_t errorCount = 0;
// 检查目录是否存在 // 检查目录是否存在
if (fs::exists(logDir) && fs::is_directory(logDir)) { if (fs::exists(logDir) && fs::is_directory(logDir)) {
// 遍历并删除所有日志文件 // 遍历并删除所有日志文件
for (const auto& entry : fs::directory_iterator(logDir)) { for (const auto& entry : fs::directory_iterator(logDir)) {
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++;
} }
} }
} else { } else {
std::cout << "日志目录不存在无需清理" << std::endl; std::cout << "日志目录不存在,无需清理" << std::endl;
return 0; return 0;
} }
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 << "日志清理完成: " << std::endl; std::cout << "日志清理完成: " << std::endl;
std::cout << "删除文件数: " << deletedCount << std::endl; std::cout << "删除文件数: " << deletedCount << std::endl;
std::cout << "错误数: " << errorCount << std::endl; std::cout << "错误数: " << errorCount << 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;
} }

156
logger.cs
View File

@@ -1,63 +1,93 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
namespace AppStore namespace AppStore
{ {
public static class Logger public static class Logger
{ {
private static readonly string LogsDirectory = Path.Combine( private static readonly string LogsDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".logs"); "zsyg", "kortapp-z", ".logs");
private static readonly object LockObject = new object(); private static readonly object LockObject = new object();
static Logger() static Logger()
{ {
try try
{ {
// 确保logs目录存在 // 确保logs目录存在
if (!Directory.Exists(LogsDirectory)) if (!Directory.Exists(LogsDirectory))
{ {
Directory.CreateDirectory(LogsDirectory); Directory.CreateDirectory(LogsDirectory);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"无法创建日志目录: {LogsDirectory}, 错误: {ex.Message}"); Console.WriteLine($"无法创建日志目录: {LogsDirectory}, 错误: {ex.Message}");
throw; throw;
} }
} }
public static void Log(string message) public static void Log(string message)
{ {
lock (LockObject) lock (LockObject)
{ {
try try
{ {
string fileName = $"{DateTime.Now:yyyyMMddHHmmss}.log"; string fileName = $"{DateTime.Now:yyyyMMddHHmmss}.log";
string filePath = Path.Combine(LogsDirectory, fileName); string filePath = Path.Combine(LogsDirectory, fileName);
using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8)) using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8))
{ {
writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}"); writer.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
// 日志记录失败时输出到控制台 // 日志记录失败时输出到控制台
Console.WriteLine($"日志记录失败: {ex.Message}"); Console.WriteLine($"日志记录失败: {ex.Message}");
} }
} }
} }
public static void LogError(string message, Exception? ex = null) public static void LogError(string message, Exception? ex = null)
{ {
string errorMessage = $"ERROR: {message}"; string errorMessage = $"ERROR: {message}";
if (ex != null) if (ex != null)
{ {
errorMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}"; errorMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
} }
Log(errorMessage); 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,83 @@
cmake_minimum_required(VERSION 3.10)
project(ImageFormatConverter)
# 设置静态编译
set(CMAKE_EXE_LINKER_FLAGS "-static")
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置MSYS2库搜索路径
set(MSYS2_LIB_DIR "C:/msys64/ucrt64/lib")
# 查找依赖库(静态版本)
find_package(PkgConfig REQUIRED)
pkg_check_modules(TIFF REQUIRED libtiff-4)
find_library(TIFF_STATIC_LIB NAMES libtiff.a PATHS ${TIFF_LIBRARY_DIRS})
pkg_check_modules(WEBP REQUIRED libwebp)
find_library(WEBP_STATIC_LIB NAMES libwebp.a PATHS ${WEBP_LIBRARY_DIRS})
pkg_check_modules(AVIF REQUIRED libavif)
find_library(AVIF_STATIC_LIB NAMES libavif.a PATHS ${AVIF_LIBRARY_DIRS})
# 使用find_package查找FLTK(静态版本)
find_package(FLTK REQUIRED)
find_library(FLTK_STATIC_LIB NAMES libfltk.a PATHS ${FLTK_LIBRARY_DIRS})
# 查找cairo导入库
find_library(CAIRO_IMPORT_LIB NAMES libcairo.dll.a PATHS "C:/msys64/ucrt64/lib")
# 查找其他依赖库
find_library(JPEG_STATIC_LIB NAMES libjpeg.a PATHS ${MSYS2_LIB_DIR})
find_library(ZLIB_STATIC_LIB NAMES libz.a PATHS ${MSYS2_LIB_DIR})
find_library(ZSTD_STATIC_LIB NAMES libzstd.a PATHS ${MSYS2_LIB_DIR})
find_library(SHARPYUV_STATIC_LIB NAMES libsharpyuv.a PATHS ${MSYS2_LIB_DIR})
find_library(JBIG_STATIC_LIB NAMES libjbig.a PATHS ${MSYS2_LIB_DIR})
find_library(LZMA_STATIC_LIB NAMES liblzma.a PATHS ${MSYS2_LIB_DIR})
find_library(LIBDEFLATE_STATIC_LIB NAMES libdeflate.a PATHS ${MSYS2_LIB_DIR})
find_library(LERC_STATIC_LIB NAMES liblerc.a PATHS ${MSYS2_LIB_DIR})
find_library(AOM_STATIC_LIB NAMES libaom.a PATHS ${MSYS2_LIB_DIR})
find_library(YUV_STATIC_LIB NAMES libyuv.a PATHS ${MSYS2_LIB_DIR})
find_library(SVTAV1_ENC_STATIC_LIB NAMES libSvtAv1Enc.a PATHS ${MSYS2_LIB_DIR})
find_library(RAV1E_STATIC_LIB NAMES librav1e.a PATHS ${MSYS2_LIB_DIR})
find_library(DAV1D_STATIC_LIB NAMES libdav1d.a PATHS ${MSYS2_LIB_DIR})
# 包含头文件目录
include_directories(include ${TIFF_INCLUDE_DIRS} ${WEBP_INCLUDE_DIRS} ${FLTK_INCLUDE_DIRS} ${AVIF_INCLUDE_DIRS} "C:/msys64/ucrt64/include/cairo")
# 收集所有源文件
file(GLOB SOURCES "src/*.cpp")
# 创建可执行文件(设置为WIN32应用程序避免控制台窗口)
add_executable(ImageFormatConverter WIN32 ${SOURCES})
# 链接静态库
target_link_libraries(ImageFormatConverter
${TIFF_STATIC_LIB}
${WEBP_STATIC_LIB}
${FLTK_STATIC_LIB}
${JPEG_STATIC_LIB}
${ZLIB_STATIC_LIB}
${ZSTD_STATIC_LIB}
${SHARPYUV_STATIC_LIB}
${JBIG_STATIC_LIB}
${LZMA_STATIC_LIB}
${LIBDEFLATE_STATIC_LIB}
${LERC_STATIC_LIB}
${AVIF_STATIC_LIB}
${AOM_STATIC_LIB}
${YUV_STATIC_LIB}
${SVTAV1_ENC_STATIC_LIB}
${RAV1E_STATIC_LIB}
${DAV1D_STATIC_LIB}
${CAIRO_IMPORT_LIB}
"C:/msys64/ucrt64/lib/libcairo.a"
-lgdi32 -lmsimg32 # 添加Windows图形库
-lstdc++ -lgcc -lwinpthread -lcomctl32 -lole32 -luuid -lws2_32 -lntdll)
# 设置输出目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToBmpConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToJpegConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToJpgConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToPngConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToTiffConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class AvifToWebpConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class BmpToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class BmpToJpegConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class BmpToJpgConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class BmpToPngConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int compression_level = 6);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class BmpToTiffConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class BmpToWebpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,25 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <stb/stb_image.h>
struct ImageData {
int width;
int height;
int channels;
std::unique_ptr<unsigned char, void(*)(void*)> pixels;
ImageData() : pixels(nullptr, stbi_image_free) {}
};
enum class ImageFormat {
PNG,
JPG,
TIFF,
WEBP,
AVIF,
UNKNOWN
};
ImageFormat get_format_from_extension(const std::string& path);

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class ConverterBase {
public:
virtual ~ConverterBase() = default;
virtual bool convert(const std::string& input,
const std::string& output) = 0;
protected:
virtual bool validate(const ImageData& data) = 0;
};

View File

@@ -0,0 +1,23 @@
#pragma once
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Choice.H>
#include <string>
class MainWindow : public Fl_Window {
public:
MainWindow(int w, int h, const char* title);
private:
Fl_Input* input_path;
Fl_Output* output_path;
Fl_Choice* format_choice;
Fl_Button* convert_btn;
static void input_file_cb(Fl_Widget* w, void* data);
static void output_file_cb(Fl_Widget* w, void* data);
static void convert_cb(Fl_Widget* w, void* data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include "common.hpp"
#include <string>
class ImageLoader {
public:
static ImageData load(const std::string& path);
static bool save_png(const std::string& path, const ImageData& data);
static bool save_jpg(const std::string& path, const ImageData& data, int quality = 90);
static void validate_image(const unsigned char* data, int width, int height);
private:
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class JpegToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class JpegToBmpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class JpegToPngConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class JpegToTiffConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class JpegToWebpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class JpgToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class JpgToBmpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class JpgToPngConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int compression_level = 6);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class JpgToTiffConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class JpgToWebpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class PngToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class PngToBmpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class PngToJpegConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class PngToJpgConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class PngToTiffConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class PngToWebpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class TiffToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class TiffToBmpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class TiffToJpegConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class TiffToJpgConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class TiffToPngConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int compression_level = 6);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class TiffToWebpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "converter_base.hpp"
#include "common.hpp"
class WebpToAvifConverter : public ConverterBase {
public:
bool convert(const std::string& input, const std::string& output) override;
protected:
bool validate(const ImageData& data) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class WebpToBmpConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class WebpToJpegConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include "common.hpp"
class WebpToJpgConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path,
int quality = 90);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class WebpToPngConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "common.hpp"
class WebpToTiffConverter {
public:
static bool convert(const std::string& input_path,
const std::string& output_path);
private:
static bool validate_input(const ImageData& data);
};

View File

@@ -0,0 +1,31 @@
#include "avif_to_bmp.hpp"
#include "common.hpp"
#include "image_loader.hpp"
#include <avif/avif.h>
#include <stb/stb_image_write.h>
bool AvifToBmpConverter::convert(const std::string& input_path,
const std::string& output_path) {
// 加载AVIF图像
ImageData data = ImageLoader::load(input_path);
if (!data.pixels) {
return false;
}
// 验证输入格式
if (!validate(data)) {
return false;
}
// 保存为BMP
return stbi_write_bmp(output_path.c_str(),
data.width,
data.height,
data.channels,
data.pixels.get());
}
bool AvifToBmpConverter::validate(const ImageData& data) {
return data.width > 0 && data.height > 0 &&
(data.channels == 3 || data.channels == 4);
}

Some files were not shown because too many files have changed in this diff Show More