Compare commits

..

90 Commits

Author SHA1 Message Date
zsyg
cff4c39e8c 添加版本号 2025-07-05 14:42:42 +08:00
zsyg
5f41f57e8d Add files via upload 2025-07-05 14:41:38 +08:00
zsyg
f2756ddbd8 添加图标 2025-07-05 14:40:40 +08:00
zsyg
d7a90ca422 添加notepad--图标 2025-07-05 14:30:46 +08:00
zsyg
7404bdfb5d 添加notepad-- 2025-07-05 14:28:40 +08:00
zsyg
5e8de310df 添加图标提取器图标 2025-07-05 14:17:32 +08:00
zsyg
f3cca9b3a1 添加图标提取器代码 2025-07-05 14:16:38 +08:00
zsyg
2154f465b7 Add files via upload 2025-07-05 14:16:00 +08:00
zsyg
99bbda4668 添加版本号 2025-07-05 10:24:08 +08:00
zsyg
d1d69da3e3 Add files via upload 2025-07-05 10:22:19 +08:00
zsyg
4c8cb807d9 添加ollama 2025-07-05 10:20:04 +08:00
zsyg
1ad64feab9 添加ollama 2025-07-05 10:19:19 +08:00
zsyg
ef7c582c50 删除视频压缩工具 2025-07-02 20:14:29 +08:00
zsyg
ee65689048 Add files via upload 2025-07-02 18:01:12 +08:00
zsyg
4ef8099054 添加自启动工具 2025-07-02 18:00:22 +08:00
zsyg
6dd8819f22 Add files via upload 2025-07-02 17:59:18 +08:00
zsyg
59900081da Create dotnet.yml 2025-07-02 17:19:33 +08:00
zsyg
2c60d0b970 Create dotnet-desktop.yml 2025-07-02 17:17:38 +08:00
zsyg
815ba41bdc 提高代码质量 2025-07-02 16:53:47 +08:00
zsyg
459c0bc9d7 Add files via upload 2025-07-02 16:05:28 +08:00
zsyg
7c78a118a9 解决非win环境下编译问题 2025-07-02 16:04:10 +08:00
zsyg
d5f944280e Add files via upload 2025-07-02 11:11:16 +08:00
zsyg
1cd722bf89 Add files via upload 2025-07-02 11:10:09 +08:00
zsyg
e34a954777 添加艺术字 2025-07-02 11:09:21 +08:00
zsyg
125bf6b0d4 移动位置 2025-07-02 10:29:36 +08:00
zsyg
983a0d5bf4 修改位置 2025-07-02 10:28:33 +08:00
zsyg
259b075541 允许自定义下载和修改编译路径 2025-07-02 10:25:17 +08:00
zsyg
d454ac0fdc 允许自定义下载路径 2025-07-02 10:10:02 +08:00
zsyg
d5c83d854c 删除没有必要的文件 2025-07-02 09:17:44 +08:00
zsyg
1c2bc713be 提高代码质量 2025-07-02 09:16:18 +08:00
zsyg
1539c665f0 提供代码质量 2025-07-02 09:15:43 +08:00
zsyg
074e55fbbc Add files via upload 2025-07-02 09:14:43 +08:00
zsyg
334fa56070 修复代码潜在问题 2025-07-02 09:12:49 +08:00
zsyg
0268e13b56 Add files via upload 2025-07-01 19:02:06 +08:00
zsyg
cba6c9eeca Update README.md 2025-07-01 11:31:02 +08:00
zsyg
70a776125a 修改密码生成器代码位置 2025-07-01 10:34:32 +08:00
zsyg
f7250dae08 修复主题系统带来的颜色 2025-07-01 10:26:26 +08:00
zsyg
ac93c8418f Add files via upload 2025-06-30 20:25:25 +08:00
zsyg
5a49714ed7 Add files via upload 2025-06-30 20:22:07 +08:00
zsyg
19056a1a8c Add files via upload 2025-06-30 20:21:19 +08:00
zsyg
7c4250f912 Add files via upload 2025-06-30 20:20:48 +08:00
zsyg
d056c24a1b Add files via upload 2025-06-30 20:20:21 +08:00
zsyg
5651e944f9 Add files via upload 2025-06-30 15:46:54 +08:00
zsyg
1dbd9968c9 Add files via upload 2025-06-30 15:46:31 +08:00
zsyg
e387d22fee Add files via upload 2025-06-30 15:44:21 +08:00
zsyg
68bd471bd2 Add files via upload 2025-06-30 15:43:54 +08:00
zsyg
53392a2ce8 Add files via upload 2025-06-29 20:23:29 +08:00
zsyg
5ecfe2da2a Add files via upload 2025-06-29 13:04:34 +08:00
zsyg
5b9532acfe Add files via upload 2025-06-29 13:03:57 +08:00
zsyg
c87d071ee0 Update README.md 2025-06-29 09:58:30 +08:00
zsyg
d54bd4c353 Add files via upload 2025-06-29 09:47:07 +08:00
zsyg
24198b2e09 Add files via upload 2025-06-29 09:46:39 +08:00
zsyg
0a0ef24497 Add files via upload 2025-06-29 09:46:05 +08:00
zsyg
96ab4bc726 添加innosetup脚本 2025-06-29 08:01:13 +08:00
zsyg
be87aaa0e6 Update README.md 2025-06-28 18:27:22 +08:00
zsyg
e8bef7e396 Update README.md 2025-06-28 18:23:41 +08:00
zsyg
7f06c9b6b1 Update README.md 2025-06-28 18:22:59 +08:00
zsyg
0137e43408 添加视频压缩代码 2025-06-28 16:10:13 +08:00
zsyg
3f88a5e5c7 上传视频压缩软件图标 2025-06-28 16:10:11 +08:00
zsyg
ba7ed20664 添加视频压缩程序 2025-06-28 16:09:12 +08:00
zsyg
336b3d3ed9 Add files via upload 2025-06-28 16:08:38 +08:00
zsyg
214a179923 Add files via upload 2025-06-28 13:23:21 +08:00
zsyg
fad8522b88 添加英文README.md 2025-06-28 12:11:31 +08:00
zsyg
337c1853c7 Create CODE_OF_CONDUCT.md 2025-06-28 12:07:38 +08:00
zsyg
cdb54b95c2 Update README.md 2025-06-28 12:04:37 +08:00
zsyg
021e89e3a4 添加系统信息查看器代码 2025-06-28 11:58:36 +08:00
zsyg
dcecf58f23 Add files via upload 2025-06-28 11:56:58 +08:00
zsyg
9b5257c67b Add files via upload 2025-06-28 11:56:26 +08:00
zsyg
90b191a939 Add files via upload 2025-06-28 11:56:04 +08:00
zsyg
4aedf9d97a Add files via upload 2025-06-28 09:05:46 +08:00
zsyg
d3667a2cc8 Add files via upload 2025-06-28 09:01:16 +08:00
zsyg
defd688323 Add files via upload 2025-06-28 09:00:49 +08:00
zsyg
6df4a9a534 添加内存锻炼器代码 2025-06-28 09:00:15 +08:00
zsyg
0efd3a9e32 Update README.md 2025-06-22 18:38:08 +08:00
zsyg
8efa2725aa Add files via upload 2025-06-22 17:46:21 +08:00
zsyg
6fc1ccc9a2 Add files via upload 2025-06-22 17:45:51 +08:00
zsyg
04b551c3c0 Add files via upload 2025-06-22 17:45:23 +08:00
zsyg
ef9a2df02c Add files via upload 2025-06-22 17:14:26 +08:00
zsyg
c25cd37755 Add files via upload 2025-06-22 12:10:39 +08:00
zsyg
6536e99eec Add files via upload 2025-06-22 12:10:19 +08:00
zsyg
0ac791653e Add files via upload 2025-06-22 12:09:14 +08:00
zsyg
45ca7909fd Add files via upload 2025-06-22 12:08:56 +08:00
zsyg
60b459c74a Create CONTRIBUTING.md 2025-06-22 11:20:48 +08:00
zsyg
295af72e4f Create SECURITY.md 2025-06-22 11:17:44 +08:00
zsyg
474f3ecfbc Update issue templates 2025-06-22 11:14:51 +08:00
zsyg
ab37a33443 Update README.md 2025-06-22 11:09:49 +08:00
zsyg
7f8a12c26a Update README.md 2025-06-22 11:03:21 +08:00
zsyg
0f50193705 Update README.md 2025-06-22 11:02:05 +08:00
zsyg
bc01dad692 Update README.md 2025-06-22 11:00:32 +08:00
zsyg
0b58ee3f2e Update README.md 2025-06-22 10:59:44 +08:00
141 changed files with 8704 additions and 1089 deletions

38
.github/ISSUE_TEMPLATE/bug反馈.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug反馈
about: Create a report to help us improve
title: ''
labels: ''
assignees: zs-yg
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

10
.github/ISSUE_TEMPLATE/功能建议.md vendored Normal file
View File

@@ -0,0 +1,10 @@
---
name: 功能建议
about: 给我们一些修改建议
title: ''
labels: ''
assignees: ''
---

View File

@@ -0,0 +1,20 @@
---
name: "新功能添加请求\U0001F970\U0001F970\U0001F970"
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

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

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

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

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

View File

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

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -13,7 +19,9 @@ namespace AppStore
{ {
private PictureBox iconBox; private PictureBox iconBox;
private Label nameLabel; private Label nameLabel;
private Panel namePanel;
private Button downloadBtn; private Button downloadBtn;
private Color borderColor = SystemColors.ControlDark;
private static readonly ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath> PathCache = private static readonly ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath> PathCache =
new ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath>(); new ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath>();
@@ -27,6 +35,7 @@ namespace AppStore
// 确保关键对象不为null // 确保关键对象不为null
iconBox = new PictureBox() { SizeMode = PictureBoxSizeMode.StretchImage }; iconBox = new PictureBox() { SizeMode = PictureBoxSizeMode.StretchImage };
nameLabel = new Label() { Text = string.Empty }; nameLabel = new Label() { Text = string.Empty };
namePanel = new Panel();
downloadBtn = new Button() { Text = "下载" }; downloadBtn = new Button() { Text = "下载" };
// 确保DownloadManager已初始化 // 确保DownloadManager已初始化
@@ -50,46 +59,123 @@ namespace AppStore
InitializeBorder(); InitializeBorder();
}); });
// 应用图标 // 应用图标 - 添加null检查
iconBox = new PictureBox(); if (iconBox != null && this != null && this.Controls != null)
iconBox.Size = new Size(80, 80); {
iconBox.Location = new Point((Width - 80) / 2, 15); iconBox.Size = new Size(80, 80);
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; iconBox.Location = new Point((Width - 80) / 2, 15);
this.Controls.Add(iconBox); iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(iconBox);
}
else
{
Logger.LogWarning("iconBox或Controls为null");
}
// 应用名称 // 应用名称 - 使用Panel包裹Label实现边框颜色
if (namePanel != null)
{
namePanel.Size = new Size(Width - 20, 40);
namePanel.Location = new Point(10, 100);
namePanel.Paint += (sender, e) => {
try
{
if (e != null && e.Graphics != null && namePanel != null)
{
var rect = namePanel.ClientRectangle;
if (rect.Width > 0 && rect.Height > 0)
{
ControlPaint.DrawBorder(e.Graphics, rect,
borderColor, ButtonBorderStyle.Solid);
}
}
}
catch (Exception ex)
{
Logger.LogWarning($"绘制namePanel边框失败: {ex.Message}");
}
};
}
nameLabel = new Label(); nameLabel = new Label();
nameLabel.AutoSize = false; nameLabel.Dock = DockStyle.Fill;
nameLabel.Size = new Size(Width - 20, 40);
nameLabel.Location = new Point(10, 100);
nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold); nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold);
nameLabel.TextAlign = ContentAlignment.MiddleCenter; nameLabel.TextAlign = ContentAlignment.MiddleCenter;
this.Controls.Add(nameLabel);
if (namePanel != null && nameLabel != null)
{
namePanel.Controls.Add(nameLabel);
}
// 初始主题设置
UpdateLabelTheme();
// 订阅主题变化事件
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
if (this != null && this.Controls != null && namePanel != null)
{
this.Controls.Add(namePanel);
}
// 下载按钮 // 下载按钮 - 添加null检查
downloadBtn = new Button(); if (downloadBtn != null)
downloadBtn.Text = "下载"; {
downloadBtn.Size = new Size(100, 32); downloadBtn.Text = "下载";
downloadBtn.Location = new Point((Width - 100) / 2, 150); downloadBtn.Size = new Size(100, 32);
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); downloadBtn.Location = new Point((Width - 100) / 2, 150);
downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat;
downloadBtn.FlatAppearance.BorderSize = 0;
downloadBtn.Cursor = Cursors.Hand;
downloadBtn.Font = new Font("Microsoft YaHei", 9);
// 按钮悬停效果
downloadBtn.MouseEnter += (s, e) => {
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
};
downloadBtn.MouseLeave += (s, e) => {
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
}; downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat;
downloadBtn.Click += DownloadBtn_Click; downloadBtn.FlatAppearance.BorderSize = 0;
this.Controls.Add(downloadBtn); downloadBtn.Cursor = Cursors.Hand;
downloadBtn.Visible = ShowDownloadButton; downloadBtn.Font = new Font("Microsoft YaHei", 9);
// 按钮悬停效果 - 添加null检查
downloadBtn.MouseEnter += (s, e) => {
if (downloadBtn != null)
{
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
}
};
downloadBtn.MouseLeave += (s, e) => {
if (downloadBtn != null)
{
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
}
};
downloadBtn.Click += DownloadBtn_Click;
this.Controls.Add(downloadBtn);
downloadBtn.Visible = ShowDownloadButton;
}
}
private void UpdateLabelTheme()
{
if (ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Dark)
{
nameLabel.BackColor = Color.Black;
nameLabel.ForeColor = Color.White;
namePanel.BackColor = Color.Black;
borderColor = Color.White;
}
else
{
nameLabel.BackColor = Color.White;
nameLabel.ForeColor = Color.Black;
namePanel.BackColor = Color.White;
borderColor = SystemColors.ControlDark;
}
if (namePanel != null && !namePanel.IsDisposed)
{
namePanel.Invalidate(); // 触发重绘
}
else
{
Logger.LogWarning("namePanel为null或已释放");
}
} }
/// <summary> /// <summary>
@@ -219,22 +305,37 @@ namespace AppStore
}; };
// 启动C++程序计算路径 // 启动C++程序计算路径
using (var process = Process.Start(startInfo)) { if (startInfo != null)
process.WaitForExit(); {
using (var process = Process.Start(startInfo))
// 检查计算结果 {
if (process.ExitCode == 0 && File.Exists(tempFile)) { if (process != null)
// 读取生成的路径点 {
var lines = File.ReadAllLines(tempFile); process.WaitForExit();
PointF[] points = lines.Select(line => {
var parts = line.Split(','); // 解析坐标 // 检查计算结果
return new PointF(float.Parse(parts[0]), float.Parse(parts[1])); if (process.ExitCode == 0 && File.Exists(tempFile))
}).ToArray(); {
try
// 创建并缓存路径对象 {
path = new System.Drawing.Drawing2D.GraphicsPath(); // 读取生成的路径点
path.AddLines(points); var lines = File.ReadAllLines(tempFile);
PathCache.TryAdd(cacheKey, path); PointF[] points = lines.Select(line => {
var parts = line.Split(','); // 解析坐标
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
}).ToArray();
// 创建并缓存路径对象
path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddLines(points);
PathCache.TryAdd(cacheKey, path);
}
catch (Exception ex)
{
Logger.LogWarning($"读取路径点失败: {ex.Message}");
}
}
}
} }
} }
} catch { } catch {
@@ -310,8 +411,18 @@ namespace AppStore
public void UpdateDisplay() public void UpdateDisplay()
{ {
nameLabel.Text = AppName; if (nameLabel != null && AppName != null)
iconBox.Image = AppIcon; {
nameLabel.Text = AppName;
}
else
{
Logger.LogWarning("nameLabel或AppName为null");
}
if (iconBox != null && AppIcon != null)
{
iconBox.Image = AppIcon;
}
} }
private void DownloadBtn_Click(object sender, EventArgs e) private void DownloadBtn_Click(object sender, EventArgs e)
@@ -321,14 +432,15 @@ namespace AppStore
// 更严格的null检查 // 更严格的null检查
// 更严格的null检查包括DownloadManager.Instance和其方法 // 更严格的null检查包括DownloadManager.Instance和其方法
// 全面的null和状态检查 // 全面的null和状态检查
var downloadManager = DownloadManager.Instance;
if (sender == null || e == null || if (sender == null || e == null ||
string.IsNullOrWhiteSpace(DownloadUrl) || string.IsNullOrWhiteSpace(DownloadUrl) ||
string.IsNullOrWhiteSpace(AppName) || string.IsNullOrWhiteSpace(AppName) ||
!this.IsHandleCreated || !this.IsHandleCreated ||
this.IsDisposed || this.IsDisposed ||
DownloadManager.Instance == null || downloadManager == null ||
DownloadManager.Instance.DownloadItems == null || downloadManager.DownloadItems == null ||
DownloadManager.Instance.StartDownload == null) downloadManager.StartDownload == null)
{ {
return; return;
} }
@@ -336,7 +448,7 @@ namespace AppStore
string safeAppName = AppName ?? "未知应用"; string safeAppName = AppName ?? "未知应用";
string fileName = $"{safeAppName.Replace(" ", "_")}.exe"; string fileName = $"{safeAppName.Replace(" ", "_")}.exe";
DownloadManager.Instance.StartDownload(fileName, DownloadUrl); downloadManager.StartDownload(fileName, DownloadUrl);
string message = $"已开始下载: {safeAppName}"; string message = $"已开始下载: {safeAppName}";
this.Invoke((MethodInvoker)delegate { this.Invoke((MethodInvoker)delegate {

63
AppSearch.cs Normal file
View File

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

View File

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

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, 和 experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, 和 sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, 和 personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
3872006562.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

12
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,12 @@
# 贡献准则
你可以进行任何(确保安全的前提下)的修改
我会对贡献进行审核,速度很快的
我会定时查看PR
你可以通过Fork到自己的仓库
然后上传软件图标,修改代码
然后提PR
你就可以做出贡献了
当然你也可以进行其他形式的修改
Remeber it!整个世界都在为你而闪耀

View File

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

View File

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

196
ImageCompressorForm.cs Normal file
View File

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

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#nullable enable #nullable enable
using System; using System;
using System.Drawing; using System.Drawing;
@@ -140,11 +146,19 @@ namespace AppStore
this.StartPosition = FormStartPosition.CenterScreen; this.StartPosition = FormStartPosition.CenterScreen;
this.Icon = new Icon("img/ico/icon.ico"); // 设置窗体图标 this.Icon = new Icon("img/ico/icon.ico"); // 设置窗体图标
// 注册主题变更事件
ThemeManager.ThemeChanged += (theme) =>
{
this.Invoke((MethodInvoker)delegate {
AnimateThemeChange();
});
};
// 现代化顶部导航栏 // 现代化顶部导航栏
Panel buttonPanel = new Panel(); Panel buttonPanel = new Panel();
buttonPanel.Dock = DockStyle.Top; buttonPanel.Dock = DockStyle.Top;
buttonPanel.Height = 70; buttonPanel.Height = 70;
buttonPanel.BackColor = Color.FromArgb(240, 240, 240); buttonPanel.BackColor = ThemeManager.ControlBackgroundColor;
buttonPanel.Padding = new Padding(10, 15, 10, 0); buttonPanel.Padding = new Padding(10, 15, 10, 0);
buttonPanel.AutoScroll = true; buttonPanel.AutoScroll = true;
buttonPanel.AutoSize = true; buttonPanel.AutoSize = true;
@@ -154,11 +168,13 @@ namespace AppStore
Action<Button> styleButton = (Button btn) => { Action<Button> styleButton = (Button btn) => {
btn.FlatStyle = FlatStyle.Flat; btn.FlatStyle = FlatStyle.Flat;
btn.FlatAppearance.BorderSize = 0; btn.FlatAppearance.BorderSize = 0;
btn.BackColor = Color.Transparent; btn.BackColor = ThemeManager.ControlBackgroundColor;
btn.ForeColor = Color.FromArgb(64, 64, 64); btn.ForeColor = ThemeManager.TextColor;
btn.Font = new Font("Microsoft YaHei", 10, FontStyle.Regular); btn.Font = new Font("Microsoft YaHei", 10, FontStyle.Regular);
btn.Size = new Size(120, 40); btn.Size = new Size(120, 40);
btn.Cursor = Cursors.Hand; btn.Cursor = Cursors.Hand;
btn.FlatAppearance.MouseOverBackColor = ThemeManager.ButtonHoverColor;
btn.FlatAppearance.MouseDownBackColor = ThemeManager.ButtonActiveColor;
// 悬停效果 // 悬停效果
btn.MouseEnter += (s, e) => { btn.MouseEnter += (s, e) => {
@@ -167,7 +183,7 @@ namespace AppStore
}; };
btn.MouseLeave += (s, e) => { btn.MouseLeave += (s, e) => {
btn.ForeColor = Color.FromArgb(64, 64, 64); btn.ForeColor = ThemeManager.TextColor;
btn.Font = new Font(btn.Font, FontStyle.Regular); btn.Font = new Font(btn.Font, FontStyle.Regular);
}; };
}; };
@@ -230,7 +246,7 @@ namespace AppStore
// 现代化内容区域 // 现代化内容区域
contentPanel = new Panel(); contentPanel = new Panel();
contentPanel.Dock = DockStyle.Fill; contentPanel.Dock = DockStyle.Fill;
contentPanel.BackColor = Color.White; contentPanel.BackColor = ThemeManager.BackgroundColor;
contentPanel.Padding = new Padding(20); contentPanel.Padding = new Padding(20);
this.Controls.Add(contentPanel); this.Controls.Add(contentPanel);
@@ -238,11 +254,13 @@ namespace AppStore
Panel separator = new Panel(); Panel separator = new Panel();
separator.Dock = DockStyle.Top; separator.Dock = DockStyle.Top;
separator.Height = 1; separator.Height = 1;
separator.BackColor = Color.FromArgb(230, 230, 230); separator.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.FromArgb(230, 230, 230)
: Color.FromArgb(60, 60, 60);
contentPanel.Controls.Add(separator); contentPanel.Controls.Add(separator);
this.Controls.Add(buttonPanel); this.Controls.Add(buttonPanel);
this.BackColor = Color.White; this.BackColor = ThemeManager.BackgroundColor;
// 默认显示软件下载视图 // 默认显示软件下载视图
ShowAppsView(); ShowAppsView();
@@ -267,8 +285,13 @@ namespace AppStore
var flowPanel = new FlowLayoutPanel(); var flowPanel = new FlowLayoutPanel();
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.WrapContents = false; flowPanel.WrapContents = true;
flowPanel.FlowDirection = FlowDirection.LeftToRight; flowPanel.FlowDirection = FlowDirection.LeftToRight;
flowPanel.Padding = new Padding(15, 50, 15, 15);
flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350);
contentPanel.Controls.Add(flowPanel); contentPanel.Controls.Add(flowPanel);
// 系统清理卡片 // 系统清理卡片
@@ -300,6 +323,251 @@ namespace AppStore
} }
}; };
flowPanel.Controls.Add(cleanerCard); flowPanel.Controls.Add(cleanerCard);
// 二维码生成卡片
var qrCard = new ToolCard();
qrCard.ToolName = "二维码生成";
try
{
qrCard.ToolIcon = Image.FromFile("img/resource/png/QRcode.png");
}
catch
{
qrCard.ToolIcon = SystemIcons.Application.ToBitmap();
}
qrCard.UpdateDisplay();
qrCard.ToolCardClicked += (s, e) => {
try {
var qrForm = new QrCodeGeneratorForm();
qrForm.ShowDialog();
} catch (Exception ex) {
MessageBox.Show($"启动二维码生成工具失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
flowPanel.Controls.Add(qrCard);
// 图片压缩卡片
var imageCompressorCard = new ToolCard();
imageCompressorCard.ToolName = "图片压缩";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png","ImageCompressor.png");
if (File.Exists(iconPath))
{
imageCompressorCard.ToolIcon = Image.FromFile(iconPath);
}
else
{
imageCompressorCard.ToolIcon = SystemIcons.Application.ToBitmap();
}
}
catch
{
imageCompressorCard.ToolIcon = SystemIcons.Application.ToBitmap();
}
imageCompressorCard.UpdateDisplay();
imageCompressorCard.ToolCardClicked += (s, e) => {
try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe");
if (File.Exists(toolPath)) {
var form = new ImageCompressorForm();
form.ShowDialog();
} else {
MessageBox.Show("图片压缩工具未找到,请确保已正确安装", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} catch (Exception ex) {
MessageBox.Show($"启动图片压缩工具失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
flowPanel.Controls.Add(imageCompressorCard);
// 内存锻炼器卡片
var memoryTrainerCard = new ToolCard();
memoryTrainerCard.ToolName = "内存锻炼器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "memory_trainer.png");
if (File.Exists(iconPath))
{
memoryTrainerCard.ToolIcon = Image.FromFile(iconPath);
}
else
{
memoryTrainerCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
}
catch
{
memoryTrainerCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
memoryTrainerCard.UpdateDisplay();
memoryTrainerCard.ToolCardClicked += (s, e) => {
try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "memory_trainer.exe");
if (File.Exists(toolPath)) {
Process.Start(toolPath);
} else {
MessageBox.Show("内存锻炼器工具未找到,请确保已正确安装", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} catch (Exception ex) {
MessageBox.Show($"启动内存锻炼器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
flowPanel.Controls.Add(memoryTrainerCard);
// 系统信息查看器卡片
var systemInfoCard = new ToolCard();
systemInfoCard.ToolName = "系统信息查看器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "system_info.png");
if (File.Exists(iconPath))
{
systemInfoCard.ToolIcon = Image.FromFile(iconPath);
}
else
{
systemInfoCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
}
catch
{
systemInfoCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
systemInfoCard.UpdateDisplay();
systemInfoCard.ToolCardClicked += (s, e) => {
try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "system_info.exe");
if (File.Exists(toolPath)) {
Process.Start(toolPath);
} else {
MessageBox.Show("系统信息查看器工具未找到,请确保已正确安装", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} catch (Exception ex) {
MessageBox.Show($"启动系统信息查看器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
flowPanel.Controls.Add(systemInfoCard);
// 计算器工具卡片
var calculatorCard = new CalculatorToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Calculator.png");
if (File.Exists(iconPath))
{
calculatorCard.ToolIcon = Image.FromFile(iconPath);
}
calculatorCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载计算器图标失败", ex);
}
flowPanel.Controls.Add(calculatorCard);
// 图片查看工具卡片
var imageViewerCard = new KortAppZ.Tools.Viewer.ImageViewerToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "ImageCompressor.png");
if (File.Exists(iconPath))
{
imageViewerCard.ToolIcon = Image.FromFile(iconPath);
}
imageViewerCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载图片查看器图标失败", ex);
}
flowPanel.Controls.Add(imageViewerCard);
// 密码生成器工具卡片
var passwordGeneratorCard = new ToolCard();
passwordGeneratorCard.ToolName = "密码生成器";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "password_generator.png");
if (File.Exists(iconPath))
{
passwordGeneratorCard.ToolIcon = Image.FromFile(iconPath);
}
else
{
passwordGeneratorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
}
catch
{
passwordGeneratorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
passwordGeneratorCard.ToolCardClicked += (s, e) => {
try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "password_generator.exe");
if (File.Exists(toolPath)) {
Process.Start(toolPath);
} else {
MessageBox.Show("密码生成器工具未找到,请确保已正确安装", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} catch (Exception ex) {
MessageBox.Show($"启动密码生成器失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
passwordGeneratorCard.UpdateDisplay();
flowPanel.Controls.Add(passwordGeneratorCard);
// 自启动管理工具卡片
var selfStartingManagerCard = new SelfStartingManagerToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "Self_starting_management.png");
if (File.Exists(iconPath))
{
selfStartingManagerCard.ToolIcon = Image.FromFile(iconPath);
}
selfStartingManagerCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载自启动管理工具图标失败", ex);
}
flowPanel.Controls.Add(selfStartingManagerCard);
// 图标提取器工具卡片
var iconExtractorCard = new AppStore.Tools.IconExtractor.IconExtractorToolCard();
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "ico_extractor.png");
if (File.Exists(iconPath))
{
iconExtractorCard.ToolIcon = Image.FromFile(iconPath);
}
iconExtractorCard.UpdateDisplay();
}
catch (Exception ex)
{
Logger.LogError("加载图标提取器图标失败", ex);
}
flowPanel.Controls.Add(iconExtractorCard);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -362,11 +630,43 @@ namespace AppStore
{ {
contentPanel.Controls.Clear(); contentPanel.Controls.Clear();
// 同步创建并添加FlowLayoutPanel // 创建应用卡片面板
FlowLayoutPanel flowPanel = new FlowLayoutPanel(); FlowLayoutPanel flowPanel = new FlowLayoutPanel();
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 50, 15, 15); flowPanel.Padding = new Padding(15, 15, 15, 15);
flowPanel.WrapContents = true;
flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350);
// 创建搜索框
TextBox searchBox = new TextBox();
searchBox.Width = 300;
searchBox.Height = 32;
searchBox.Font = new Font("Microsoft YaHei", 10);
searchBox.PlaceholderText = "搜索应用...";
searchBox.BorderStyle = BorderStyle.FixedSingle;
searchBox.BackColor = Color.White;
searchBox.ForeColor = Color.FromArgb(64, 64, 64);
searchBox.Location = new Point((contentPanel.Width - searchBox.Width) / 2, 20);
// 响应窗体大小变化
contentPanel.Resize += (s, e) => {
searchBox.Location = new Point((contentPanel.Width - searchBox.Width) / 2, 20);
};
// 搜索框事件
searchBox.TextChanged += (s, e) => {
AppSearch.SearchApps(flowPanel, searchBox.Text);
};
contentPanel.Controls.Add(searchBox);
contentPanel.Controls.Add(flowPanel);
flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 60, 15, 15);
flowPanel.WrapContents = true; flowPanel.WrapContents = true;
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
@@ -417,7 +717,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"XDM", "XDM",
"https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi", "https://ghproxy.net/https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi",
"img/png/XDM.png")); "img/png/XDM.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -482,7 +782,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"Msys2", "Msys2",
"https://github.com/msys2/msys2-installer/releases/download/2025-02-21/msys2-x86_64-20250221.exe", "https://ghproxy.net/https://github.com/msys2/msys2-installer/releases/download/2025-02-21/msys2-x86_64-20250221.exe",
"img/png/MSYS2.png")); "img/png/MSYS2.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -582,7 +882,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"frp", "frp",
"https://github.com/fatedier/frp/releases/download/v0.62.1/frp_0.62.1_windows_amd64.zip", "https://ghproxy.net/https://github.com/fatedier/frp/releases/download/v0.62.1/frp_0.62.1_windows_amd64.zip",
"")); ""));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -639,6 +939,12 @@ namespace AppStore
"Firefox", "Firefox",
"https://download-ssl.firefox.com.cn/releases-sha2/full/116.0/zh-CN/Firefox-full-latest-win64.exe", "https://download-ssl.firefox.com.cn/releases-sha2/full/116.0/zh-CN/Firefox-full-latest-win64.exe",
"img/jpg/firefox.jpg")); "img/jpg/firefox.jpg"));
//这应该是为数不多的国产软件了
flowPanel.Controls.Add(CreateAppCard(
"星愿浏览器",
"https://d1.twinkstar.com/win/Twinkstar_v10.7.1000.2505_Release.exe",
"img/jpg/Twinkstar.jpg"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"Mem Reduct", "Mem Reduct",
@@ -700,6 +1006,11 @@ namespace AppStore
"https://ghproxy.net/https://github.com/vnotex/vnote/releases/download/v3.19.2/VNote-3.19.2-win64.zip", "https://ghproxy.net/https://github.com/vnotex/vnote/releases/download/v3.19.2/VNote-3.19.2-win64.zip",
"img/png/vnote.png")); "img/png/vnote.png"));
flowPanel.Controls.Add(CreateAppCard(
"notepad--",
"https://www.ghproxy.cn/https://github.com/cxasm/notepad--/releases/download/notepad-v3.3/Notepad--v3.3-plugin-Installer.exe",
"img/png/notepad--.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"PowerToys", "PowerToys",
"https://ghproxy.net/https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe", "https://ghproxy.net/https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe",
@@ -710,6 +1021,16 @@ namespace AppStore
"https://ghproxy.net/https://github.com/microsoft/terminal/releases/download/v1.22.11141.0/Microsoft.WindowsTerminal_1.22.11141.0_x64.zip", "https://ghproxy.net/https://github.com/microsoft/terminal/releases/download/v1.22.11141.0/Microsoft.WindowsTerminal_1.22.11141.0_x64.zip",
"img/png/terminal.png")); "img/png/terminal.png"));
flowPanel.Controls.Add(CreateAppCard(
"github_cli",
"https://ghproxy.cn/https://github.com/cli/cli/releases/download/v2.74.2/gh_2.74.2_windows_arm64.msi",
"img/png/github_cli.png"));
flowPanel.Controls.Add(CreateAppCard(
"ReactOS",
"https://ghproxy.cn/https://github.com/reactos/reactos/releases/download/0.4.15-release/ReactOS-0.4.15-release-1-gdbb43bbaeb2-x86-iso.zip",
"img/png/ReactOS.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"typescript", "typescript",
"https://ghproxy.net/https://github.com/microsoft/TypeScript/releases/download/v5.8.3/typescript-5.8.3.tgz", "https://ghproxy.net/https://github.com/microsoft/TypeScript/releases/download/v5.8.3/typescript-5.8.3.tgz",
@@ -875,6 +1196,11 @@ namespace AppStore
"https://ghproxy.net/https://github.com/cloudreve/cloudreve/releases/download/3.8.3/cloudreve_3.8.3_windows_amd64.zip", "https://ghproxy.net/https://github.com/cloudreve/cloudreve/releases/download/3.8.3/cloudreve_3.8.3_windows_amd64.zip",
"img/png/cloudreve.png")); "img/png/cloudreve.png"));
flowPanel.Controls.Add(CreateAppCard(
"ollama",
"https://www.ghproxy.cn/https://github.com/ollama/ollama/releases/download/v0.9.5/OllamaSetup.exe",
"img/png/ollama.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"SeelenUI", "SeelenUI",
"https://ghproxy.net/https://github.com/eythaann/Seelen-UI/releases/download/v2.3.8/Seelen.UI_2.3.8_x64-setup.exe", "https://ghproxy.net/https://github.com/eythaann/Seelen-UI/releases/download/v2.3.8/Seelen.UI_2.3.8_x64-setup.exe",
@@ -979,6 +1305,9 @@ namespace AppStore
// 初始化窗体组件 // 初始化窗体组件
InitializeComponent(); InitializeComponent();
// 应用主题
ThemeManager.ApplyTheme(this);
// 订阅下载管理器事件 // 订阅下载管理器事件
DownloadManager.Instance.DownloadAdded += OnDownloadAdded; // 下载添加事件 DownloadManager.Instance.DownloadAdded += OnDownloadAdded; // 下载添加事件
DownloadManager.Instance.DownloadProgressChanged += OnDownloadProgressChanged; // 下载进度变化事件 DownloadManager.Instance.DownloadProgressChanged += OnDownloadProgressChanged; // 下载进度变化事件
@@ -1060,5 +1389,69 @@ namespace AppStore
Logger.Log($"下载完成: {item.FileName}, 状态: {item.Status}"); // 记录日志 Logger.Log($"下载完成: {item.FileName}, 状态: {item.Status}"); // 记录日志
item.UpdateDisplay(); // 更新UI显示 item.UpdateDisplay(); // 更新UI显示
} }
/// <summary>
/// 主题切换动画效果
/// </summary>
private void AnimateThemeChange()
{
const int animationSteps = 10;
const int animationInterval = 30;
var timer = new System.Windows.Forms.Timer { Interval = animationInterval };
int step = 0;
// 保存当前和目标颜色
var originalBackColor = this.BackColor;
var targetBackColor = ThemeManager.BackgroundColor;
var originalForeColor = this.ForeColor;
var targetForeColor = ThemeManager.TextColor;
timer.Tick += (s, e) => {
if (step >= animationSteps)
{
timer.Stop();
timer.Dispose();
// 确保最终颜色准确
ThemeManager.ApplyTheme(this);
return;
}
// 计算插值比例
float ratio = (float)step / animationSteps;
step++;
// 插值计算新颜色
var newBackColor = Color.FromArgb(
(int)(originalBackColor.R + (targetBackColor.R - originalBackColor.R) * ratio),
(int)(originalBackColor.G + (targetBackColor.G - originalBackColor.G) * ratio),
(int)(originalBackColor.B + (targetBackColor.B - originalBackColor.B) * ratio));
var newForeColor = Color.FromArgb(
(int)(originalForeColor.R + (targetForeColor.R - originalForeColor.R) * ratio),
(int)(originalForeColor.G + (targetForeColor.G - originalForeColor.G) * ratio),
(int)(originalForeColor.B + (targetForeColor.B - originalForeColor.B) * ratio));
// 应用新颜色
this.Invoke((MethodInvoker)delegate {
this.BackColor = newBackColor;
this.ForeColor = newForeColor;
foreach (Control control in this.Controls)
{
control.BackColor = newBackColor;
control.ForeColor = newForeColor;
// 特殊处理按钮的悬停状态
if (control is Button button)
{
button.FlatAppearance.MouseOverBackColor = ThemeManager.ButtonHoverColor;
button.FlatAppearance.MouseDownBackColor = ThemeManager.ButtonActiveColor;
}
}
});
};
timer.Start();
}
} }
} }

View File

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

View File

@@ -5,24 +5,17 @@
## 项目开源行为 ## 项目开源行为
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都会被删除
## 项目简介 ## 项目简介
![image](https://gitee.com/chr_super/kortapp-z/raw/master/img/pngimage.png)
这个图片可能有点问题因为它是gitee仓库中的图片
这是为了加快图片渲染速度但是它好像只能渲染一半在gitee是正常的
这个我没什么办法了
一个简单的Windows应用商店应用提供软件下载和管理功能。 一个简单的Windows应用商店应用提供软件下载和管理功能。
提供软件管理、下载管理、内置工具使用等功能
## 功能特点 ## 功能特点
@@ -30,6 +23,7 @@
- 下载进度管理 - 下载进度管理
- 支持后台下载 - 支持后台下载
- 美观的应用卡片展示 - 美观的应用卡片展示
- 功能化、结构化的代码处理
## 构建与打包 ## 构建与打包
@@ -39,14 +33,15 @@
### 打包指令 ### 打包指令
#### 32位版本 #### 32位版本
```bash ```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:Optimize=true /p:DebugType=None
``` ```
#### 64位版本 #### 64位版本
```bash ```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None
``` ```
打包后的可执行文件将包含指定的应用程序图标,输出路径为: 打包后的可执行文件将包含指定的应用程序图标,输出路径为:
@@ -55,13 +50,15 @@ bin\Release\net8.0-windows\[platform]\publish
``` ```
### 高级选项 ### 高级选项
- 添加`--self-contained true`生成独立包(体积较大 - 使用`--self-contained false`生成框架依赖包(默认
- 使用`/p:Optimize=true`启用代码优化(默认)
- 使用`/p:DebugType=None`禁用调试符号生成(默认)
- 添加`-p:PublishTrimmed=true`可减小包体积(实验性) - 添加`-p:PublishTrimmed=true`可减小包体积(实验性)
## 项目结构 ## 项目结构
``` ```
AppStore/ kortapp-z/
├── MainForm.cs # 主窗体逻辑 ├── MainForm.cs # 主窗体逻辑
├── DownloadManager.cs # 下载管理 ├── DownloadManager.cs # 下载管理
├── AppCard.cs # 应用卡片控件 ├── AppCard.cs # 应用卡片控件
@@ -82,18 +79,30 @@ AppStore/
MIT License MIT License
Copyright (c) 2025 kortapp-z项目组 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将永远不上架我要自己努力
提示由于github上传文件的限制img/png/NET.png请改名为.NET.png否则程序可能出现无法预料的问题 提示由于github上传文件的限制img/png/NET.png请改名为.NET.png否则程序可能出现无法预料的问题
每一个人都可以通过PR添加属于自己的合法软件 每一个人都可以通过PR添加属于自己的合法软件
作者邮箱:
```
3872006562@qq.com
```
作者qq号:
```
3872006562
```
qq群 qq群
``` ```
1043867176 1043867176

135
README/de-DE_README.md Normal file
View File

@@ -0,0 +1,135 @@
# Kortapp-z - Windows App Store
**Open-Source, kostenlos, werbefrei**
Grundsatz: Keine Werbung, kontinuierliche Updates
## Open-Source-Richtlinien
1. **Open-Source-Code**: Nutzung, Modifikation, Verbreitung und kommerzielle Nutzung erlaubt, mit Pflicht zur Nennung des Originalautors.
2. **Open-Source-Dokumentation**: Gleiche Bedingungen wie für Code.
3. **Open-Source-Ressourcen** (Icons, Screenshots): Gleiche Bedingungen.
4. Alle abgeleiteten Produkte (Websites, Apps, Erweiterungen etc.) müssen diese Lizenz einhalten.
5. **Keine Werbung** in keiner Form.
6. **Keine Spenden** werden angenommen.
7. **Keine Sponsoring** wird angenommen.
8. **Pull Requests (PR) sind willkommen**, auch ohne vorheriges Issue.
9. Eigene Projekte können per PR eingereicht werden, aber solche mit unter **1000 Stars** werden gelöscht.
## Projektbeschreibung
Eine einfache Windows-App zum Herunterladen und Verwalten von Software.
**Hauptfunktionen:**
- Softwareverwaltung
- Download-Manager
- Integrierte Tools
## Hauptmerkmale
- Minimalistische Download-Oberfläche
- Download-Fortschrittsverfolgung
- Hintergrund-Downloads
- Visuell ansprechende App-Karten
- Sauberer und strukturierter Code
## Build und Packaging
### Systemanforderungen
- **.NET 8.0 SDK**
- **Windows 10/11**
### Build-Befehle
#### 32-Bit-Version
```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true
```
#### 64-Bit-Version
```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
```
Erstellte Dateien werden unter folgendem Pfad abgelegt:
```
bin\Release\net8.0-windows\[Plattform]\publish
```
### Zusätzliche Optionen
- `--self-contained true` eigenständige Builds (größere Dateien)
- `-p:PublishTrimmed=true` Größenreduzierung (experimentell)
## Projektstruktur
```
kortapp-z/
├── MainForm.cs # Hauptfenster-Logik
├── DownloadManager.cs # Download-Manager
├── AppCard.cs # App-Karte
├── DownloadItem.cs # Download-Element
├── img/ # Grafiken
│ ├── ico/ # Icons
│ └── png/ # Screenshots
└── resource/ # Ressourcen
└── aria2c.exe # Download-Tool
```
## Laufzeitanforderungen
- **.NET 8.0 Runtime** (bei Framework-abhängiger Build)
- **Windows 10 oder neuer**
## Lizenz
**MIT License**
Copyright (c) 2025 zsyg
## Andere Plattformen
**Gitee-Mirror**: [https://gitee.com/chr_super/kortapp-z](https://gitee.com/chr_super/kortapp-z) (nicht mehr gewartet)
## Wartung
Der Gitee-Repository wird nicht mehr synchronisiert. Hilfe beim Mirroring ist willkommen:
- **QQ**: 3872006562
- **Bilibili (Direktnachricht)**: Zayisynth
**Wichtig:**
- Aufgrund eines Konflikts mit Daye wird **windowscleaner** nie hinzugefügt.
- Benennen Sie `img/png/NET.png` in `.NET.png` um, um Fehler zu vermeiden (GitHub-Beschränkungen).
## Mitwirken
Jeder kann legale Software per Pull Request vorschlagen.
## Kontakt
📧 **Email**:
```
3872006562@qq.com
```
📱 **QQ**:
```
3872006562
```
👥 **QQ-Gruppe**:
```
1043867176
```
🎥 **Bilibili**:
```
Zayisynth
```
---
### Wie sollte die deutsche README benannt werden?
Empfohlene Optionen:
1. **de-DE_README.md** (IETF-Standard)
2. **README_de.md** (kürzere Variante)
3. **DE_README.md** (explizite Sprachkennung)
Am besten halten Sie sich an das Format `[Sprachcode]_README.md`, wie bei anderen Übersetzungen.

115
README/en-US_README.md Normal file
View File

@@ -0,0 +1,115 @@
# Kortapp-z - Windows App Store -- Advocating for Open Source, Free Software, and No Ads
Setting a small flag: Never accept ads, continuously update.
## Open Source Practices
1. The project code is open source, allowing anyone to use, modify, distribute, or commercialize it, but the original author must be credited.
2. The project documentation is open source, allowing anyone to use, modify, distribute, or commercialize it, but the original author must be credited.
3. Project resources such as icons and screenshots are open source, allowing anyone to use, modify, distribute, or commercialize them, but the original author must be credited.
4. Any derivatives of the project (including but not limited to websites, apps, plugins, etc.) must adhere to the above open-source license.
5. The project does not accept any form of advertising, and ads must not be placed anywhere.
6. The project does not accept any form of donations.
7. The project does not accept any form of sponsorship.
8. The project welcomes PRs (Pull Requests) in any form. Submitting issues is not required.
9. You can submit PRs for your own projects. However, if the star count is below 1k, they will be deleted.
## Project Overview
A simple Windows app store application that provides software download and management features.
Offers software management, download management, built-in tools, and more.
## Features
- Clean software download interface
- Download progress management
- Supports background downloads
- Beautiful app card display
- Functional and structured code handling
## Build and Packaging
### System Requirements
- .NET 8.0 SDK
- Windows 10/11
### Packaging Commands
#### 32-bit Version
```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true
```
#### 64-bit Version
```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
```
The packaged executable will include the specified application icon and be output to:
```
bin\Release\net8.0-windows\[platform]\publish
```
### Advanced Options
- Add `--self-contained true` to generate a standalone package (larger size).
- Add `-p:PublishTrimmed=true` to reduce package size (experimental).
## Project Structure
```
kortapp-z/
├── MainForm.cs # Main form logic
├── DownloadManager.cs # Download management
├── AppCard.cs # App card control
├── DownloadItem.cs # Download item control
├── img/ # Image resources
│ ├── ico/ # Icon files
│ └── png/ # App screenshots
└── resource/ # Resource files
└── aria2c.exe # Download tool
```
## Runtime Requirements
- .NET 8.0 runtime (if using framework-dependent deployment)
- Windows 10 or later
## License
MIT License
Copyright (c) 2025 zsyg
## Other Websites
Gitee Mirror Repository: https://gitee.com/chr_super/kortapp-z (Currently no longer maintained)
## Maintenance
Since I rarely use Gitee and find it cumbersome to operate, the Gitee mirror will no longer be synced. If anyone knows how to set up a mirror, please help. Contact me via QQ: 3872006562 or DM me on Bilibili. I will acknowledge your help in the README. Thank you!
Due to a conflict with Daye, Windowscleaner will never be listed. I will strive on my own.
Note: Due to GitHub file upload restrictions, rename `img/png/NET.png` to `.NET.png`; otherwise, the program may encounter unexpected issues.
Anyone can add their own legal software via PR.
Author's Email:
```
3872006562@qq.com
```
Author's QQ:
```
3872006562
```
QQ Group:
```
1043867176
```
Bilibili Account:
```
Zayisynth
```

123
README/fr-FR_README.md Normal file
View File

@@ -0,0 +1,123 @@
# Kortapp-z - Microsoft Store -- Logiciel open source, gratuit et sans publicité
Engagement : aucune publicité acceptée, mises à jour continues
## Politique open source
1. Code open source : utilisation, modification, distribution et usage commercial autorisés, avec mention de l'auteur original obligatoire.
2. Documentation open source : mêmes conditions que le code.
3. Ressources graphiques (icônes, captures d'écran) open source : mêmes conditions.
4. Tout produit dérivé (sites web, apps, extensions...) doit respecter cette licence.
5. Aucune publicité n'est acceptée, sous aucune forme.
6. Aucun don n'est accepté.
7. Aucun sponsoring n'est accepté.
8. Les Pull Requests (PR) sont bienvenues, même sans issue préalable.
9. Vous pouvez soumettre vos projets via PR, mais ceux avec moins de 1k stars seront supprimés.
## Présentation
Une application simple de boutique Windows pour télécharger et gérer des logiciels.
Fonctionnalités :
- Gestion de logiciels
- Gestion de téléchargements
- Outils intégrés
## Fonctionnalités clés
- Interface épurée pour le téléchargement
- Gestion de la progression des téléchargements
- Téléchargements en arrière-plan
- Présentation sous forme de cartes visuelles
- Code structuré et modulaire
## Compilation et packaging
### Prérequis
- SDK .NET 8.0
- Windows 10/11
### Commandes
#### Version 32-bit
```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true
```
#### Version 64-bit
```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
```
Fichiers générés :
```
bin\Release\net8.0-windows\[platform]\publish
```
### Options avancées
- `--self-contained true` : package autonome (taille plus importante)
- `-p:PublishTrimmed=true` : réduction de taille (expérimental)
## Structure du projet
```
kortapp-z/
├── MainForm.cs # Logique de la fenêtre principale
├── DownloadManager.cs # Gestion des téléchargements
├── AppCard.cs # Contrôle des cartes d'applications
├── DownloadItem.cs # Contrôle des éléments de téléchargement
├── img/ # Ressources visuelles
│ ├── ico/ # Icônes
│ └── png/ # Captures d'écran
└── resource/ # Fichiers ressources
└── aria2c.exe # Outil de téléchargement
```
## Prérequis d'exécution
- Runtime .NET 8.0 (si version dépendante du framework)
- Windows 10 ou supérieur
## Licence
MIT License
Copyright (c) 2025 zsyg
## Autres plateformes
Dépôt miroir Gitee : https://gitee.com/chr_super/kortapp-z (maintenance arrêtée)
## Maintenance
Le dépôt Gitee n'étant plus maintenu par manque d'utilisation, toute aide pour la synchronisation est bienvenue.
Contact : QQ 3872006562 ou message privé sur Bilibili (mention dans le README en remerciement).
Note : Suite à un conflit avec Daye, l'application windowscleaner ne sera jamais publiée ici.
Important : Renommez `img/png/NET.png` en `.NET.png` pour éviter des problèmes (limitations GitHub).
## Contributions
Tout utilisateur peut ajouter des logiciels légaux via PR.
## Contacts
Email :
```
3872006562@qq.com
```
QQ :
```
3872006562
```
Groupe QQ :
```
1043867176
```
Compte Bilibili :
```
Zayisynth
```

135
README/ru-RU_README.md Normal file
View File

@@ -0,0 +1,135 @@
# Kortapp-z — Магазин приложений Windows
**Открытое ПО, бесплатно, без рекламы**
Обещание: никакой рекламы, постоянные обновления
## Политика открытого кода
1. **Открытый исходный код**: можно использовать, изменять, распространять и применять в коммерческих целях с обязательным указанием автора.
2. **Открытая документация**: те же условия, что и для кода.
3. **Открытые ресурсы** (иконки, скриншоты): те же условия.
4. Все производные продукты (сайты, приложения, расширения и т. д.) должны соблюдать эту лицензию.
5. **Никакой рекламы** — ни в какой форме.
6. **Не принимаются пожертвования**.
7. **Не принимается спонсорская поддержка**.
8. **Pull Requests (PR) приветствуются**, даже без предварительного issue.
9. Можно предлагать свои проекты через PR, но те, у которых меньше **1000 звёзд**, будут удалены.
## Описание проекта
Простое приложение для Windows, позволяющее скачивать и управлять программами.
**Основные функции:**
- Управление установленными приложениями
- Менеджер загрузок
- Встроенные инструменты
## Ключевые особенности
- Минималистичный интерфейс загрузки
- Отслеживание прогресса загрузки
- Фоновые загрузки
- Красивые карточки приложений
- Чистый и структурированный код
## Сборка и упаковка
### Системные требования
- **.NET 8.0 SDK**
- **Windows 10/11**
### Команды сборки
#### 32-битная версия
```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true
```
#### 64-битная версия
```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
```
Собранные файлы появятся в:
```
bin\Release\net8.0-windows\[платформа]\publish
```
### Дополнительные опции
- `--self-contained true` — автономная сборка (больший размер)
- `-p:PublishTrimmed=true` — уменьшение размера (экспериментально)
## Структура проекта
```
kortapp-z/
├── MainForm.cs # Логика главного окна
├── DownloadManager.cs # Менеджер загрузок
├── AppCard.cs # Карточка приложения
├── DownloadItem.cs # Элемент загрузки
├── img/ # Графика
│ ├── ico/ # Иконки
│ └── png/ # Скриншоты
└── resource/ # Ресурсы
└── aria2c.exe # Утилита загрузки
```
## Требования для запуска
- **.NET 8.0 Runtime** (если используется зависимая сборка)
- **Windows 10 или новее**
## Лицензия
**MIT License**
Copyright (c) 2025 zsyg
## Другие платформы
**Зеркало на Gitee**: [https://gitee.com/chr_super/kortapp-z](https://gitee.com/chr_super/kortapp-z) (поддержка прекращена)
## Поддержка
Репозиторий на Gitee больше не синхронизируется. Если кто-то может помочь с зеркалированием — пишите:
- **QQ**: 3872006562
- **Bilibili (личные сообщения)**: Zayisynth
**Важно**:
- Из-за конфликта с Daye приложение **windowscleaner** никогда не будет добавлено.
- Переименуйте `img/png/NET.png` в `.NET.png`, иначе возможны ошибки (ограничения GitHub).
## Участие
Любой может предложить легальное ПО через Pull Request.
## Контакты
📧 **Email**:
```
3872006562@qq.com
```
📱 **QQ**:
```
3872006562
```
👥 **QQ-группа**:
```
1043867176
```
🎥 **Bilibili**:
```
Zayisynth
```
---
### Как назвать файл README на русском?
Рекомендуемые варианты:
1. **ru-RU_README.md** (стандартный IETF-код)
2. **README_ru.md** (более короткий вариант)
3. **RUS_README.md** (если предпочитаете явное обозначение)
Лучше всего придерживаться формата `[язык]_README.md`, как в других переводах.

113
README/zh-TW_README.md Normal file
View File

@@ -0,0 +1,113 @@
# Kortapp-z - Windows應用商店 --主張軟體開源、免費,拒絕廣告
小立一個flag從不接受廣告不停更新
## 專案開源行為
1. 專案程式碼開源,允許任何人使用、修改、分發、商用,但必須註明原作者。
2. 專案文件開源,允許任何人使用、修改、分發、商用,但必須註明原作者。
3. 專案圖示、截圖等資源開源,允許任何人使用、修改、分發、商用,但必須註明原作者。
4. 專案的任何衍生品包括但不限於網站、APP、外掛等必須遵循以上開源協議。
5. 專案不接受任何形式的廣告,不得在任何地方投放廣告。
6. 專案不接受任何形式的捐贈。
7. 專案不接受任何形式的贊助。
8. 專案可以進行PR歡迎任何形式的PR不提交issue也可以
9. 本專案可以PR一些你自己的專案如果star數量不到1k都會被刪除
## 專案簡介
一個簡單的Windows應用商店應用提供軟體下載和管理功能。
提供軟體管理、下載管理、內建工具使用等功能
## 功能特點
- 簡潔的軟體下載介面
- 下載進度管理
- 支援後台下載
- 美觀的應用卡片展示
- 功能化、結構化的程式碼處理
## 構建與打包
### 系統要求
- .NET 8.0 SDK
- Windows 10/11
### 打包指令
#### 32位版本
```bash
dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true
```
#### 64位版本
```bash
dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true
```
打包後的可執行文件將包含指定的應用程式圖示,輸出路徑為:
```
bin\Release\net8.0-windows\[platform]\publish
```
### 進階選項
- 添加`--self-contained true`可生成獨立包(體積較大)
- 添加`-p:PublishTrimmed=true`可減小包體積(實驗性)
## 專案結構
```
kortapp-z/
├── MainForm.cs # 主視窗邏輯
├── DownloadManager.cs # 下載管理
├── AppCard.cs # 應用卡片控制項
├── DownloadItem.cs # 下載項控制項
├── img/ # 圖片資源
│ ├── ico/ # 圖示文件
│ └└── png/ # 應用截圖
└└── resource/ # 資源文件
└└── aria2c.exe # 下載工具
```
## 執行要求
- .NET 8.0執行時(如果使用框架依賴發布)
- Windows 10或更高版本
## 授權許可
MIT License
Copyright (c) 2025 zsyg
## 其他網站
gitee鏡像倉庫:https://gitee.com/chr_super/kortapp-z (目前已停止維護)
## 維護
由於gitee我沒怎麼用而且操作麻煩gitee鏡像將不會繼續同步有懂得人可以幫我搞下鏡像嗎qq 3872006562也可以b站直接私信我會在readme中鳴謝的謝謝各位
由於和Daye發生了矛盾所以windowscleaner將永遠不上架我要自己努力
提示由於github上傳文件的限制img/png/NET.png請改名為.NET.png否則程式可能出現無法預料的問題
每一個人都可以通過PR添加屬於自己的合法軟體
作者郵箱:
```
3872006562@qq.com
```
作者qq號:
```
3872006562
```
qq群
```
1043867176
```
b站帳號
```
Zayisynth
```

7
SECURITY.md Normal file
View File

@@ -0,0 +1,7 @@
# 安全准则
# 安全提醒
应用商店的软件我们无法保证绝对性的安全
在下载和使用、安装之前请为该软件做足充足的功课
如果出现系统无法打开,我们无法负责,只能悲哀

View File

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

12
TXT/Build.txt Normal file
View File

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

7
TXT/Run.txt Normal file
View File

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

135
ThemeManager.cs Normal file
View File

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

View File

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

View File

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

View File

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

BIN
img/jpg/Twinkstar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

BIN
img/png/ReactOS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
img/png/github_cli.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
img/png/ollama.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

BIN
img/resource/png/QRcode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 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: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

64
inno/innosetup_x64.iss Normal file
View File

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

56
inno/innosetup_x86.iss Normal file
View File

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

View File

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

142
logger.cs
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
# 编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -Iinclude -static
LDFLAGS = -static -mwindows -Wl,--subsystem,windows \
-L"C:/msys64/ucrt64/lib" \
-L"C:/msys64/ucrt64/x86_64-w64-mingw32/lib" \
-lws2_32 -lole32 -loleaut32 -luuid \
-Wl,-Bstatic -lstdc++ -lgcc -lwinpthread
# 目录设置
SRC_DIR = src
OBJ_DIR = obj
BUILD_DIR = build
INCLUDE_DIR = include
# 源文件和目标文件
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
EXEC = $(BUILD_DIR)/system_info.exe
# 默认目标
all: $(EXEC)
# 链接目标文件生成可执行文件
$(EXEC): $(OBJS) | $(BUILD_DIR)
$(CC) $(LDFLAGS) -o $@ $^
# 编译源文件生成目标文件
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
# 创建必要的目录
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# 清理生成的文件
clean:
rm -rf $(OBJ_DIR) $(BUILD_DIR)
.PHONY: all clean

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <windows.h>
#include "system_info.h"
void update_main_window(HWND hWnd, SystemInfo* sysInfo);
void toggle_fullscreen(HWND hWnd);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif // MAIN_WINDOW_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,135 @@
#include "window_utils.h"
#include "main_window.h"
#include <tchar.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#define IDC_MAIN_BUTTON 1001
// 全局变量存储系统信息
static SystemInfo* g_sysInfo = NULL;
BOOL register_window_class(HINSTANCE hInstance) {
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MainWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(240, 240, 240));
wcex.lpszMenuName = NULL;
wcex.lpszClassName = _T("SystemInfoWindowClass");
wcex.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
return RegisterClassEx(&wcex);
}
int create_main_window(HINSTANCE hInstance, SystemInfo* sysInfo, UINT codePage) {
// 设置控制台编码
if (codePage == 65001) {
SetConsoleOutputCP(65001);
_setmode(_fileno(stdout), _O_U16TEXT);
}
// 设置窗口标题为宽字符
LPCWSTR windowTitle = L"系统信息查看器";
g_sysInfo = sysInfo;
if (!register_window_class(hInstance)) {
return 0;
}
HWND hWnd = CreateWindowW(
L"SystemInfoWindowClass",
windowTitle,
WS_POPUP | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL, NULL, hInstance, sysInfo);
if (!hWnd) {
return 0;
}
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
// 初始更新窗口内容
update_main_window(hWnd, sysInfo);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE: {
// 安全初始化系统信息指针
if (lParam) {
g_sysInfo = (SystemInfo*)((CREATESTRUCT*)lParam)->lpCreateParams;
}
if (!g_sysInfo) {
MessageBoxW(hWnd, L"系统信息初始化失败", L"错误", MB_ICONERROR);
return -1;
}
// 创建显示系统信息的按钮
CreateWindowW(L"BUTTON", L"刷新信息",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_BORDER,
10, 10, 150, 30,
hWnd, (HMENU)IDC_MAIN_BUTTON,
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
break;
}
case WM_SIZE: {
// 窗口大小变化时更新布局
update_main_window(hWnd, g_sysInfo);
break;
}
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_MAIN_BUTTON) {
// 刷新系统信息
init_system_info(g_sysInfo);
update_main_window(hWnd, g_sysInfo);
}
break;
}
case WM_KEYDOWN: {
// F11键切换全屏
if (wParam == VK_F11) {
toggle_fullscreen(hWnd);
update_main_window(hWnd, g_sysInfo);
}
break;
}
case WM_NCCALCSIZE:
if (wParam) {
return 0;
}
break;
case WM_ERASEBKGND: {
RECT rc;
GetClientRect(hWnd, &rc);
FillRect((HDC)wParam, &rc, (HBRUSH)GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND));
return 1;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

22
others/C/memory/Makefile Normal file
View File

@@ -0,0 +1,22 @@
CC = gcc
CFLAGS = -Wall -Wextra -I./include
LDFLAGS = -Wl,-subsystem,windows -municode -luser32 -lgdi32 -lcomctl32 -lpsapi
SRC = src/main.c src/memory_ops.c src/ui.c src/init.c \
src/utils.c src/config.c src/benchmark.c src/log.c \
src/error.c src/version.c
OBJ = $(SRC:.c=.o)
TARGET = memory_trainer.exe
all: $(TARGET)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJ) $(LDFLAGS)
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
clean:
del /Q $(subst /,\,$(OBJ)) $(TARGET)
.PHONY: all clean

View File

@@ -0,0 +1,19 @@
#ifndef BENCHMARK_H
#define BENCHMARK_H
#include <stddef.h>
// 内存性能测试结果
typedef struct {
double allocation_time; // 分配时间(ms)
double fill_time; // 填充时间(ms)
double free_time; // 释放时间(ms)
} BenchmarkResult;
// 运行内存性能测试
BenchmarkResult run_memory_benchmark(size_t size_mb);
// 打印测试结果(支持中文)
void print_benchmark_result(const BenchmarkResult* result);
#endif // BENCHMARK_H

View File

@@ -0,0 +1,22 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <stddef.h>
// 内存训练器配置
typedef struct {
size_t default_memory_size; // 默认内存大小(MB)
size_t max_memory_size; // 最大内存大小(MB)
int fill_mode; // 默认填充模式
} Config;
// 初始化配置
void init_config();
// 获取当前配置
Config* get_config();
// 保存配置到文件
void save_config();
#endif // CONFIG_H

View File

@@ -0,0 +1,9 @@
#ifndef ERROR_H
#define ERROR_H
#include "../include/memory_trainer.h"
// 获取错误描述
const wchar_t* get_error_message(ErrorCode code);
#endif // ERROR_H

View File

@@ -0,0 +1,21 @@
#ifndef LOG_H
#define LOG_H
#include <stdarg.h>
#include <stddef.h>
// 日志级别
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR
} LogLevel;
// 初始化日志系统
void init_logger();
// 记录日志(支持中文)
void log_message(LogLevel level, const wchar_t* format, ...);
#endif // LOG_H

View File

@@ -0,0 +1,54 @@
#ifndef MEMORY_TRAINER_H
#define MEMORY_TRAINER_H
#include <stddef.h>
#include <windows.h>
// 错误代码定义
typedef enum {
ERR_SUCCESS = 0,
ERR_ALLOCATION,
ERR_INVALID_PARAM,
ERR_SYSTEM
} ErrorCode;
// 进度回调函数类型
typedef void (*ProgressCallback)(int percent);
// 内存操作函数声明
void* allocate_memory(size_t size);
void fill_memory(void* ptr, size_t size, int mode, ProgressCallback callback);
void free_memory(void* ptr, size_t size);
// 错误处理函数
void report_error(ErrorCode code, const wchar_t* message);
// 全局窗口句柄(外部声明)
extern HWND g_hMainWnd;
// 进度回调函数
void update_progress(int percent);
// UI控件ID定义
#define IDC_RUN_TEST 1001
#define IDC_PROGRESS 1002
#define IDC_RETAIN_MEM 1003
#define IDC_FILL_ZERO 1004
#define IDC_FILL_RANDOM 1005
// 窗口过程函数声明
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// UI函数声明
void init_main_window_ui(HWND hWnd);
LRESULT handle_ui_message(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// 初始化函数声明
void initialize_application(HINSTANCE hInstance);
HINSTANCE get_app_instance(void);
// 版本信息函数
const wchar_t* get_version_string(void);
const wchar_t* get_build_date(void);
#endif // MEMORY_TRAINER_H

View File

@@ -0,0 +1,15 @@
#ifndef UTILS_H
#define UTILS_H
#include <stddef.h>
// 字节转换工具MB转字节
size_t mb_to_bytes(size_t mb);
// 获取当前时间戳(毫秒)
long long get_timestamp();
// 打印调试信息(支持中文)
void debug_print(const wchar_t* message);
#endif // UTILS_H

Binary file not shown.

View File

@@ -0,0 +1,40 @@
#include "../include/benchmark.h"
#include "../include/utils.h"
#include "../include/memory_trainer.h"
#include <windows.h>
#include <stdio.h>
// 运行内存性能测试
BenchmarkResult run_memory_benchmark(size_t size_mb) {
BenchmarkResult result = {0};
LARGE_INTEGER freq, start, end;
QueryPerformanceFrequency(&freq);
// 测试内存分配
QueryPerformanceCounter(&start);
void* ptr = allocate_memory(size_mb);
QueryPerformanceCounter(&end);
result.allocation_time = (end.QuadPart - start.QuadPart) * 1000.0 / freq.QuadPart;
// 测试内存填充
QueryPerformanceCounter(&start);
fill_memory(ptr, mb_to_bytes(size_mb), 0, NULL);
QueryPerformanceCounter(&end);
result.fill_time = (end.QuadPart - start.QuadPart) * 1000.0 / freq.QuadPart;
// 测试内存释放
QueryPerformanceCounter(&start);
free_memory(ptr, size_mb);
QueryPerformanceCounter(&end);
result.free_time = (end.QuadPart - start.QuadPart) * 1000.0 / freq.QuadPart;
return result;
}
// 打印测试结果(支持中文)
void print_benchmark_result(const BenchmarkResult* result) {
wprintf(L"=== 内存性能测试结果 ===\n");
wprintf(L"分配时间: %.2f 毫秒\n", result->allocation_time);
wprintf(L"填充时间: %.2f 毫秒\n", result->fill_time);
wprintf(L"释放时间: %.2f 毫秒\n", result->free_time);
}

View File

@@ -0,0 +1,29 @@
#include "../include/config.h"
#include <stdio.h>
#include <stdlib.h>
// 全局配置实例
static Config app_config = {
.default_memory_size = 100, // 默认100MB
.max_memory_size = 8192, // 最大8GB
.fill_mode = 0 // 默认填充0
};
// 初始化配置
void init_config() {
// TODO: 从文件加载配置
}
// 获取当前配置
Config* get_config() {
return &app_config;
}
// 保存配置到文件
void save_config() {
FILE* fp = fopen("config.bin", "wb");
if (fp) {
fwrite(&app_config, sizeof(Config), 1, fp);
fclose(fp);
}
}

View File

@@ -0,0 +1,21 @@
#include "../include/memory_trainer.h"
#include "../include/log.h"
#include <windows.h>
// 错误代码定义
const wchar_t* error_messages[] = {
L"操作成功",
L"内存分配失败",
L"无效参数",
L"系统错误"
};
// 获取错误描述
const wchar_t* get_error_message(ErrorCode code) {
if (code < 0 || code >= sizeof(error_messages)/sizeof(error_messages[0])) {
return L"未知错误";
}
return error_messages[code];
}

View File

@@ -0,0 +1,24 @@
#include "../include/memory_trainer.h"
#include "../include/log.h"
#include <windows.h>
// 初始化应用程序
void initialize_application(HINSTANCE hInstance) {
// 初始化日志系统
init_logger();
// 注册窗口类(Unicode版本)
WNDCLASSW wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MemoryTrainer";
RegisterClassW(&wc);
// 记录初始化完成
log_message(LOG_INFO, L"应用程序初始化完成");
}
// 获取应用程序实例句柄
HINSTANCE get_app_instance(void) {
return GetModuleHandleW(NULL);
}

59
others/C/memory/src/log.c Normal file
View File

@@ -0,0 +1,59 @@
#include "../include/log.h"
#include "../include/memory_trainer.h"
#include "../include/error.h"
#include <windows.h>
#include <stdio.h>
#include <time.h>
static FILE* log_file = NULL;
static LogLevel current_level = LOG_INFO;
// 初始化日志系统
void init_logger() {
log_file = _wfopen(L"memory_trainer.log", L"a, ccs=UTF-8");
if (!log_file) {
log_file = stdout;
}
}
// 记录日志(支持中文)
void log_message(LogLevel level, const wchar_t* format, ...) {
if (level < current_level) return;
// 获取当前时间
time_t now;
time(&now);
struct tm* tm_info = localtime(&now);
wchar_t time_buf[20];
wcsftime(time_buf, 20, L"%Y-%m-%d %H:%M:%S", tm_info);
// 格式化日志消息
va_list args;
va_start(args, format);
fwprintf(log_file, L"[%s] ", time_buf);
vfwprintf(log_file, format, args);
fwprintf(log_file, L"\n");
va_end(args);
fflush(log_file);
}
// 报告错误并显示给用户
void report_error(ErrorCode code, const wchar_t* message) {
// 获取标准错误描述
const wchar_t* error_desc = get_error_message(code);
// 构建完整错误消息
wchar_t full_msg[512];
wsprintfW(full_msg, L"%s: %s", error_desc, message);
// 记录到日志
log_message(LOG_ERROR, L"错误代码 %d: %s", code, full_msg);
// 显示给用户
if (g_hMainWnd) {
MessageBoxW(g_hMainWnd, full_msg, L"内存锻炼器 - 错误", MB_ICONERROR);
} else {
OutputDebugStringW(full_msg);
}
}

219
others/C/memory/src/main.c Normal file
View File

@@ -0,0 +1,219 @@
#define UNICODE
#define _UNICODE
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <psapi.h>
#include "../include/memory_trainer.h"
// 全局变量
HWND g_hMainWnd;
HWND hSizeEdit, hProgressBar, hRetainCheck, hRunButton;
HFONT hFont;
// 初始化现代UI
void InitModernUI(HWND hWnd) {
// 创建字体
hFont = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, DEFAULT_PITCH, L"微软雅黑");
// 内存大小输入
CreateWindowW(L"STATIC", L"内存大小 (MB):",
WS_VISIBLE | WS_CHILD | SS_CENTERIMAGE,
20, 20, 120, 30, hWnd, NULL, NULL, NULL);
hSizeEdit = CreateWindowW(L"EDIT", L"100",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
150, 20, 120, 30, hWnd, NULL, NULL, NULL);
// 填充模式选择
CreateWindowW(L"STATIC", L"填充模式:",
WS_VISIBLE | WS_CHILD | SS_CENTERIMAGE,
20, 70, 120, 30, hWnd, NULL, NULL, NULL);
HWND hFillZero = CreateWindowW(L"BUTTON", L"填充0",
WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP,
150, 70, 80, 30, hWnd, (HMENU)IDC_FILL_ZERO, NULL, NULL);
CreateWindowW(L"BUTTON", L"随机填充",
WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
240, 70, 100, 30, hWnd, (HMENU)IDC_FILL_RANDOM, NULL, NULL);
SendMessage(hFillZero, BM_SETCHECK, BST_CHECKED, 0);
// 进度条
hProgressBar = CreateWindowW(PROGRESS_CLASSW, NULL,
WS_VISIBLE | WS_CHILD | PBS_SMOOTH,
20, 120, 360, 30, hWnd, NULL, NULL, NULL);
SendMessage(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
// 保持内存选项
hRetainCheck = CreateWindowW(L"BUTTON", L"保持内存内容",
WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX,
20, 170, 150, 30, hWnd, NULL, NULL, NULL);
// 执行按钮
hRunButton = CreateWindowW(L"BUTTON", L"开始测试",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
200, 170, 180, 40, hWnd, (HMENU)IDC_RUN_TEST, NULL, NULL);
// 设置字体
SendMessage(hSizeEdit, WM_SETFONT, (WPARAM)hFont, TRUE);
SendMessage(hRetainCheck, WM_SETFONT, (WPARAM)hFont, TRUE);
SendMessage(hRunButton, WM_SETFONT, (WPARAM)hFont, TRUE);
}
// 主窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE:
InitModernUI(hWnd);
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_RUN_TEST) {
wchar_t sizeText[32];
GetWindowTextW(hSizeEdit, sizeText, 32);
int sizeMB = _wtoi(sizeText);
// 获取系统内存信息
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus);
// 验证输入
if (sizeMB <= 0) {
MessageBoxW(hWnd, L"请输入有效的内存大小(大于0)", L"错误", MB_ICONERROR);
return 0;
}
// 检查是否超过系统可用内存的75%
DWORDLONG maxRecommended = memStatus.ullAvailPhys * 3 / 4;
if ((DWORDLONG)sizeMB * 1024 * 1024 > maxRecommended) {
wchar_t warning[256];
wsprintfW(warning, L"请求的内存大小(%dMB)超过推荐值(%.1fMB)\n可能造成系统不稳定,是否继续?",
sizeMB, (float)maxRecommended/1024/1024);
if (MessageBoxW(hWnd, warning, L"警告", MB_YESNO | MB_ICONWARNING) != IDYES) {
return 0;
}
}
// 获取填充模式
int mode = 0;
if (SendMessage(GetDlgItem(hWnd, IDC_FILL_RANDOM), BM_GETCHECK, 0, 0) == BST_CHECKED) {
mode = 1;
}
// MB转字节
size_t bytes = 0;
if (sizeMB > 0 && sizeMB < 1024*1024) { // 限制最大1TB
bytes = (size_t)sizeMB * 1024 * 1024;
void* ptr = allocate_memory(bytes);
if (!ptr) {
// 错误信息已由report_error记录直接返回
return 0;
}
// 显示分配成功
SendMessage(hProgressBar, PBM_SETPOS, 10, 0);
// 进度回调函数
void progress_callback(int percent) {
PostMessage(g_hMainWnd, WM_APP, percent, 0);
}
// 填充内存
fill_memory(ptr, bytes, mode, progress_callback);
// 检查填充是否完成
if (SendMessage(hProgressBar, PBM_GETPOS, 0, 0) < 100) {
// 错误信息已由fill_memory中的report_error记录
}
// 确保完成
SendMessage(hProgressBar, PBM_SETPOS, 100, 0);
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
wchar_t msg[256];
wsprintfW(msg, L"成功填充 %d MB 内存\n实际使用: %.1f GB",
sizeMB,
(float)pmc.PrivateUsage / 1024.0f / 1024.0f / 1024.0f);
MessageBoxW(hWnd, msg, L"操作成功", MB_OK);
if (SendMessage(hRetainCheck, BM_GETCHECK, 0, 0) != BST_CHECKED) {
free_memory(ptr, sizeMB);
}
} else {
MessageBoxW(hWnd, L"请输入有效的内存大小", L"错误", MB_ICONERROR);
}
}
break;
case WM_APP:
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)wParam, 0);
break;
case WM_DESTROY:
DeleteObject(hFont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
// 应用程序入口
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(pCmdLine);
// 调试输出
OutputDebugStringW(L"内存锻炼器启动...\n");
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
wchar_t debugMsg[256];
wsprintfW(debugMsg, L"处理器数量: %lu\n", (unsigned long)sysInfo.dwNumberOfProcessors);
OutputDebugStringW(debugMsg);
wsprintfW(debugMsg, L"页面大小: %lu\n", (unsigned long)sysInfo.dwPageSize);
OutputDebugStringW(debugMsg);
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus);
wsprintfW(debugMsg, L"总物理内存: %.1f GB\n", (float)memStatus.ullTotalPhys/1024/1024/1024);
OutputDebugStringW(debugMsg);
// 注册窗口类
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = L"MemoryTrainerModern";
RegisterClassEx(&wc);
// 创建主窗口
g_hMainWnd = CreateWindowEx(0, L"MemoryTrainerModern", L"内存锻炼器",
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 450, 300,
NULL, NULL, hInstance, NULL);
ShowWindow(g_hMainWnd, nCmdShow);
UpdateWindow(g_hMainWnd);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}

View File

@@ -0,0 +1,145 @@
#include "../include/memory_trainer.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
// 分配指定大小的内存
void* allocate_memory(size_t size_bytes) {
if (size_bytes == 0) {
report_error(ERR_INVALID_PARAM, L"无效的内存大小");
return NULL;
}
// 获取系统内存状态
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus);
// 统一debugMsg变量声明
wchar_t debugMsg[512];
// 详细调试输出
wsprintfW(debugMsg, L"开始内存分配...\n请求大小: %zu bytes (%.2f MB)\n系统总内存: %.2f GB\n可用内存: %.2f GB\n",
size_bytes,
(double)size_bytes / (1024 * 1024),
(double)memStatus.ullTotalPhys / (1024 * 1024 * 1024),
(double)memStatus.ullAvailPhys / (1024 * 1024 * 1024));
OutputDebugStringW(debugMsg);
// 分配并锁定内存(调整为系统页面大小的整数倍)
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
size_t actual_size = ((size_bytes + sysInfo.dwPageSize - 1) / sysInfo.dwPageSize) * sysInfo.dwPageSize;
wsprintfW(debugMsg, L"实际分配大小: %zu bytes (%.2f MB), 页面大小: %lu\n",
actual_size,
(double)actual_size / (1024 * 1024),
sysInfo.dwPageSize);
OutputDebugStringW(debugMsg);
void* ptr = VirtualAlloc(NULL, actual_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!ptr) {
DWORD err = GetLastError();
wsprintfW(debugMsg, L"内存分配失败,错误代码: %lu, 请求大小: %zu\n", err, actual_size);
OutputDebugStringW(debugMsg);
LPWSTR errMsg = NULL;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, 0, (LPWSTR)&errMsg, 0, NULL);
wchar_t userMsg[512];
wsprintfW(userMsg, L"内存分配失败(错误 %lu): %s", err, errMsg ? errMsg : L"未知错误");
report_error(ERR_ALLOCATION, userMsg);
if (errMsg) LocalFree(errMsg);
return NULL;
}
wsprintfW(debugMsg, L"成功分配 %zu 字节内存,地址: %p\n", actual_size, ptr);
OutputDebugStringW(debugMsg);
// 检查系统内存锁定限制
SIZE_T minLock, maxLock;
GetProcessWorkingSetSize(GetCurrentProcess(), &minLock, &maxLock);
wsprintfW(debugMsg, L"进程工作集大小: 最小=%zu KB, 最大=%zu KB\n",
minLock/1024, maxLock/1024);
OutputDebugStringW(debugMsg);
// 尝试锁定内存(限制锁定大小为工作集最大值的50%)
size_t lock_size = min(actual_size, maxLock/2);
if (!VirtualLock(ptr, lock_size)) {
DWORD err = GetLastError();
wsprintfW(debugMsg, L"内存锁定失败,错误代码: %lu, 锁定大小: %zu MB\n",
err, lock_size/1024/1024);
OutputDebugStringW(debugMsg);
// 使用系统错误代码继续执行
report_error(ERR_SYSTEM, L"内存锁定部分失败,继续运行");
} else {
wsprintfW(debugMsg, L"成功锁定 %zu MB 内存\n", lock_size/1024/1024);
OutputDebugStringW(debugMsg);
}
OutputDebugStringW(L"内存锁定成功\n");
// 写入所有页确保实际分配
memset(ptr, 0, actual_size);
return ptr;
}
// 填充内存(带进度回调)
void fill_memory(void* ptr, size_t size_bytes, int mode,
ProgressCallback progress_callback) {
if (!ptr) {
report_error(ERR_INVALID_PARAM, L"空指针");
return;
}
// 验证内存范围
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(ptr, &mbi, sizeof(mbi)) == 0) {
report_error(ERR_SYSTEM, L"内存查询失败");
return;
}
const size_t block_size = 1024 * 1024; // 1MB块
size_t filled = 0;
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
if (mode == 0) { // 填充0
memset(ptr, 0, size_bytes);
if (progress_callback) progress_callback(100);
} else { // 填充随机值
srand((unsigned)time(NULL));
uint32_t* block = (uint32_t*)malloc(block_size);
while (filled < size_bytes) {
size_t remaining = size_bytes - filled;
size_t current_block = remaining > block_size ? block_size : remaining;
// 批量填充随机值
for (size_t i = 0; i < current_block/sizeof(uint32_t); i++) {
block[i] = rand();
}
memcpy((char*)ptr + filled, block, current_block);
filled += current_block;
if (progress_callback) {
int percent = (int)(filled * 100 / size_bytes);
progress_callback(percent);
}
}
free(block);
}
}
// 释放内存
void free_memory(void* ptr, size_t size_bytes) {
if (ptr) {
size_t actual_size = size_bytes + 4096;
VirtualUnlock(ptr, actual_size);
VirtualFree(ptr, 0, MEM_RELEASE);
}
}

67
others/C/memory/src/ui.c Normal file
View File

@@ -0,0 +1,67 @@
#include "../include/memory_trainer.h"
#include <windows.h>
#include <commctrl.h>
// 进度回调函数
void update_progress(int percent) {
HWND hProgress = GetDlgItem(g_hMainWnd, IDC_PROGRESS);
if (hProgress) {
SendMessage(hProgress, PBM_SETPOS, percent, 0);
}
}
// 初始化主窗口UI
void init_main_window_ui(HWND hWnd) {
g_hMainWnd = hWnd;
// 创建内存大小输入框
CreateWindowW(L"STATIC", L"内存大小(MB):",
WS_VISIBLE | WS_CHILD,
20, 20, 100, 20, hWnd, NULL, NULL, NULL);
CreateWindowW(L"EDIT", L"100",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
130, 20, 80, 20, hWnd, NULL, NULL, NULL);
// 创建填充模式单选按钮
CreateWindowW(L"STATIC", L"填充模式:",
WS_VISIBLE | WS_CHILD,
20, 50, 100, 20, hWnd, NULL, NULL, NULL);
CreateWindowW(L"BUTTON", L"填充0",
WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP,
130, 50, 80, 20, hWnd, NULL, NULL, NULL);
CreateWindowW(L"BUTTON", L"填充随机",
WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON,
220, 50, 80, 20, hWnd, NULL, NULL, NULL);
// 创建内存保持选项(更显眼的位置)
CreateWindowW(L"BUTTON", L"✔ 保持内存",
WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX,
130, 80, 100, 20, hWnd, (HMENU)IDC_RETAIN_MEM, NULL, NULL);
// 创建进度条(更显眼的位置和大小)
CreateWindowW(PROGRESS_CLASSW, NULL,
WS_VISIBLE | WS_CHILD | PBS_SMOOTH,
20, 110, 360, 25, hWnd, (HMENU)IDC_PROGRESS, NULL, NULL);
SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
// 创建操作按钮
CreateWindowW(L"BUTTON", L"执行内存测试",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
20, 140, 280, 30, hWnd, (HMENU)IDC_RUN_TEST, NULL, NULL);
}
// 处理UI消息
LRESULT handle_ui_message(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE:
init_main_window_ui(hWnd);
break;
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}

View File

@@ -0,0 +1,23 @@
#include "../include/utils.h"
#include <windows.h>
#include <stdio.h>
// MB转字节
size_t mb_to_bytes(size_t mb) {
return mb * 1024 * 1024;
}
// 获取当前时间戳(毫秒)
long long get_timestamp() {
SYSTEMTIME st;
GetSystemTime(&st);
return (long long)st.wMilliseconds +
st.wSecond * 1000LL +
st.wMinute * 60000LL +
st.wHour * 3600000LL;
}
// 打印调试信息(支持中文)
void debug_print(const wchar_t* message) {
wprintf(L"[DEBUG] %s\n", message);
}

View File

@@ -0,0 +1,11 @@
#include "../include/memory_trainer.h"
// 获取版本信息
const wchar_t* get_version_string(void) {
return L"内存锻炼器 v1.0.0";
}
// 获取构建日期
const wchar_t* get_build_date(void) {
return L"2025-06-01";
}

View File

@@ -0,0 +1,34 @@
# 编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -O2 -Iinclude
LDFLAGS = -lavcodec -lavformat -lavutil -lswscale -lgdi32 -lcomdlg32 -mwindows
# 目录设置
OBJ_DIR = obj
BUILD_DIR = build
# 源文件和目标文件
SRC = $(wildcard src/*.c)
OBJ = $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(SRC))
EXEC = $(BUILD_DIR)/video_compressor.exe
# 创建目录
$(shell mkdir -p $(OBJ_DIR))
$(shell mkdir -p $(BUILD_DIR))
# 默认目标
all: $(EXEC)
# 链接可执行文件
$(EXEC): $(OBJ)
$(CC) -o $@ $^ $(LDFLAGS)
# 编译规则
$(OBJ_DIR)/%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理规则
clean:
del /Q $(OBJ_DIR)\*.o $(EXEC)
.PHONY: all clean

View File

@@ -0,0 +1,65 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <stdbool.h>
/**
* 加载配置文件
* @param config_file 配置文件路径
* @return true表示成功false表示失败
*/
bool config_load(const char* config_file);
/**
* 保存配置文件
* @param config_file 配置文件路径
* @return true表示成功false表示失败
*/
bool config_save(const char* config_file);
/**
* 获取字符串配置值
* @param key 配置键
* @param default_value 默认值
* @return 配置值
*/
const char* config_get_string(const char* key, const char* default_value);
/**
* 获取整数配置值
* @param key 配置键
* @param default_value 默认值
* @return 配置值
*/
int config_get_int(const char* key, int default_value);
/**
* 获取布尔配置值
* @param key 配置键
* @param default_value 默认值
* @return 配置值
*/
bool config_get_bool(const char* key, bool default_value);
/**
* 设置字符串配置值
* @param key 配置键
* @param value 配置值
*/
void config_set_string(const char* key, const char* value);
/**
* 设置整数配置值
* @param key 配置键
* @param value 配置值
*/
void config_set_int(const char* key, int value);
/**
* 设置布尔配置值
* @param key 配置键
* @param value 配置值
*/
void config_set_bool(const char* key, bool value);
#endif // CONFIG_H

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