Compare commits

...

41 Commits

Author SHA1 Message Date
zsyg
0d9ec0ad44 修改版本号 2025-07-05 16:44:38 +08:00
zsyg
122ada92d9 修改版本号 2025-07-05 16:44:11 +08:00
zsyg
d571729c89 修改版本号 2025-07-05 16:43:38 +08:00
zsyg
351e1e97c3 修改布局 2025-07-05 16:43:14 +08:00
zsyg
cff4c39e8c 添加版本号 2025-07-05 14:42:42 +08:00
zsyg
5f41f57e8d Add files via upload 2025-07-05 14:41:38 +08:00
zsyg
f2756ddbd8 添加图标 2025-07-05 14:40:40 +08:00
zsyg
d7a90ca422 添加notepad--图标 2025-07-05 14:30:46 +08:00
zsyg
7404bdfb5d 添加notepad-- 2025-07-05 14:28:40 +08:00
zsyg
5e8de310df 添加图标提取器图标 2025-07-05 14:17:32 +08:00
zsyg
f3cca9b3a1 添加图标提取器代码 2025-07-05 14:16:38 +08:00
zsyg
2154f465b7 Add files via upload 2025-07-05 14:16:00 +08:00
zsyg
99bbda4668 添加版本号 2025-07-05 10:24:08 +08:00
zsyg
d1d69da3e3 Add files via upload 2025-07-05 10:22:19 +08:00
zsyg
4c8cb807d9 添加ollama 2025-07-05 10:20:04 +08:00
zsyg
1ad64feab9 添加ollama 2025-07-05 10:19:19 +08:00
zsyg
ef7c582c50 删除视频压缩工具 2025-07-02 20:14:29 +08:00
zsyg
ee65689048 Add files via upload 2025-07-02 18:01:12 +08:00
zsyg
4ef8099054 添加自启动工具 2025-07-02 18:00:22 +08:00
zsyg
6dd8819f22 Add files via upload 2025-07-02 17:59:18 +08:00
zsyg
59900081da Create dotnet.yml 2025-07-02 17:19:33 +08:00
zsyg
2c60d0b970 Create dotnet-desktop.yml 2025-07-02 17:17:38 +08:00
zsyg
815ba41bdc 提高代码质量 2025-07-02 16:53:47 +08:00
zsyg
459c0bc9d7 Add files via upload 2025-07-02 16:05:28 +08:00
zsyg
7c78a118a9 解决非win环境下编译问题 2025-07-02 16:04:10 +08:00
zsyg
d5f944280e Add files via upload 2025-07-02 11:11:16 +08:00
zsyg
1cd722bf89 Add files via upload 2025-07-02 11:10:09 +08:00
zsyg
e34a954777 添加艺术字 2025-07-02 11:09:21 +08:00
zsyg
125bf6b0d4 移动位置 2025-07-02 10:29:36 +08:00
zsyg
983a0d5bf4 修改位置 2025-07-02 10:28:33 +08:00
zsyg
259b075541 允许自定义下载和修改编译路径 2025-07-02 10:25:17 +08:00
zsyg
d454ac0fdc 允许自定义下载路径 2025-07-02 10:10:02 +08:00
zsyg
d5c83d854c 删除没有必要的文件 2025-07-02 09:17:44 +08:00
zsyg
1c2bc713be 提高代码质量 2025-07-02 09:16:18 +08:00
zsyg
1539c665f0 提供代码质量 2025-07-02 09:15:43 +08:00
zsyg
074e55fbbc Add files via upload 2025-07-02 09:14:43 +08:00
zsyg
334fa56070 修复代码潜在问题 2025-07-02 09:12:49 +08:00
zsyg
0268e13b56 Add files via upload 2025-07-01 19:02:06 +08:00
zsyg
cba6c9eeca Update README.md 2025-07-01 11:31:02 +08:00
zsyg
70a776125a 修改密码生成器代码位置 2025-07-01 10:34:32 +08:00
zsyg
f7250dae08 修复主题系统带来的颜色 2025-07-01 10:26:26 +08:00
87 changed files with 4681 additions and 2701 deletions

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

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

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

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

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -51,7 +57,7 @@ namespace AppStore
// 初始化并添加应用信息 // 初始化并添加应用信息
infoLabel = new Label(); infoLabel = new Label();
infoLabel.Text = "kortapp-z\n版本: 1.1.1\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件"; infoLabel.Text = "kortapp-z\n版本: 1.2.0\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;

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)
downloadBtn = new Button(); {
downloadBtn.Text = "下载"; namePanel.Controls.Add(nameLabel);
downloadBtn.Size = new Size(100, 32); }
downloadBtn.Location = new Point((Width - 100) / 2, 150);
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
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) => { UpdateLabelTheme();
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
};
downloadBtn.MouseLeave += (s, e) => { // 订阅主题变化事件
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
if (this != null && this.Controls != null && namePanel != null)
{
this.Controls.Add(namePanel);
}
// 下载按钮 - 添加null检查
if (downloadBtn != null)
{
downloadBtn.Text = "下载";
downloadBtn.Size = new Size(100, 32);
downloadBtn.Location = new Point((Width - 100) / 2, 150);
downloadBtn.BackColor = Color.FromArgb(0, 120, 215); downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
}; downloadBtn.ForeColor = Color.White;
downloadBtn.FlatStyle = FlatStyle.Flat;
downloadBtn.FlatAppearance.BorderSize = 0;
downloadBtn.Cursor = Cursors.Hand;
downloadBtn.Font = new Font("Microsoft YaHei", 9);
downloadBtn.Click += DownloadBtn_Click; // 按钮悬停效果 - 添加null检查
this.Controls.Add(downloadBtn); downloadBtn.MouseEnter += (s, e) => {
downloadBtn.Visible = ShowDownloadButton; 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 != null)
{
process.WaitForExit();
// 检查计算结果 // 检查计算结果
if (process.ExitCode == 0 && File.Exists(tempFile)) { if (process.ExitCode == 0 && File.Exists(tempFile))
// 读取生成的路径点 {
var lines = File.ReadAllLines(tempFile); try
PointF[] points = lines.Select(line => { {
var parts = line.Split(','); // 解析坐标 // 读取生成的路径点
return new PointF(float.Parse(parts[0]), float.Parse(parts[1])); var lines = File.ReadAllLines(tempFile);
}).ToArray(); 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 = new System.Drawing.Drawing2D.GraphicsPath();
path.AddLines(points); path.AddLines(points);
PathCache.TryAdd(cacheKey, path); 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

@@ -12,6 +12,7 @@
<WarningsAsErrors>CS8618</WarningsAsErrors> <WarningsAsErrors>CS8618</WarningsAsErrors>
<ApplicationIcon>img\ico\icon.ico</ApplicationIcon> <ApplicationIcon>img\ico\icon.ico</ApplicationIcon>
<Platforms>x86;x64</Platforms> <Platforms>x86;x64</Platforms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -33,4 +34,8 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="ZXing.Net" Version="0.16.9" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;

View File

@@ -1,8 +1,15 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@@ -41,7 +48,7 @@ namespace AppStore
private ProcessResult GetProcessResult(Process? process) private ProcessResult GetProcessResult(Process? process)
{ {
var result = new ProcessResult(); var result = new ProcessResult();
if (process == null) return result; if (process == null || process.StartInfo == null) return result;
try try
{ {
@@ -85,36 +92,39 @@ namespace AppStore
private void DownloadFile(DownloadItem downloadItem, string fileName, string url) private void DownloadFile(DownloadItem downloadItem, string fileName, string url)
{ {
string downloadsDir = string.Empty;
try try
{ {
// 设置下载目录为用户文件夹中的Downloads // 获取并验证下载路径
// 获取系统下载文件夹路径 downloadsDir = GetDownloadPath();
// 获取系统下载文件夹路径
string downloadsDir;
IntPtr pathPtr = IntPtr.Zero;
try try
{ {
// 使用SHGetKnownFolderPath API获取下载文件夹 // 检查路径是否有效
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B"); if (string.IsNullOrWhiteSpace(downloadsDir))
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) != 0)
{ {
throw new Exception("无法获取下载文件夹路径"); throw new Exception("下载路径为空");
} }
downloadsDir = Marshal.PtrToStringUni(pathPtr); // 尝试创建目录(如果不存在)
Directory.CreateDirectory(downloadsDir);
// 验证目录是否可写
string testFile = Path.Combine(downloadsDir, "write_test.tmp");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
} }
catch catch (Exception ex)
{ {
throw new Exception("无法确定下载文件夹位置,请手动指定下载路径"); // 回退到默认下载路径
string defaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Downloads");
Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
downloadsDir = defaultPath;
Directory.CreateDirectory(downloadsDir);
} }
finally
{
if (pathPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
Directory.CreateDirectory(downloadsDir);
@@ -309,12 +319,20 @@ namespace AppStore
currentProcess.BeginErrorReadLine(); currentProcess.BeginErrorReadLine();
progressTimer.Start(); progressTimer.Start();
} }
catch (Exception ex) catch (Exception ex)
{ {
downloadItem.Status = $"下载错误: {ex.Message}"; string errorDetails = $"下载错误: {ex.Message}\n";
DownloadCompleted?.Invoke(downloadItem); errorDetails += $"目标路径: {downloadsDir}\n";
errorDetails += $"URL: {url}";
} downloadItem.Status = $"下载失败: {ex.Message}";
Logger.LogError(errorDetails, ex);
MessageBox.Show($"下载失败:\n{errorDetails}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
DownloadCompleted?.Invoke(downloadItem);
}
} }
public void CancelDownload(DownloadItem item) public void CancelDownload(DownloadItem item)
@@ -322,7 +340,7 @@ namespace AppStore
try try
{ {
var process = currentProcess; var process = currentProcess;
if (process == null || process.HasExited || process.StartInfo == null) if (process?.StartInfo == null || process.HasExited)
{ {
item.Status = "已取消"; item.Status = "已取消";
DownloadProgressChanged?.Invoke(item); DownloadProgressChanged?.Invoke(item);
@@ -342,5 +360,155 @@ namespace AppStore
DownloadProgressChanged?.Invoke(item); DownloadProgressChanged?.Invoke(item);
} }
} }
private string GetDownloadPath()
{
string fallbackPath = string.Empty;
// 1. 优先读取用户设置的下载路径
try
{
string jsonPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path", "download_path.json");
Logger.Log($"尝试读取下载路径配置文件: {jsonPath}");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
Logger.Log($"配置文件内容: {jsonString}");
var jsonData = JsonSerializer.Deserialize<JsonElement>(jsonString);
string customPath = jsonData.GetProperty("DownloadPath").GetString()?.Trim();
if (!string.IsNullOrWhiteSpace(customPath))
{
Logger.Log($"读取到自定义路径: {customPath}");
// 处理路径格式
customPath = customPath.Replace(@"\\", @"\");
try
{
// 处理路径中的环境变量和特殊字符
customPath = Environment.ExpandEnvironmentVariables(customPath);
customPath = Path.GetFullPath(customPath);
Logger.Log($"标准化后的路径: {customPath}");
// 确保路径以目录分隔符结尾
if (!customPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
customPath += Path.DirectorySeparatorChar;
}
// 验证驱动器是否存在
string drive = Path.GetPathRoot(customPath);
if (!Directory.Exists(drive))
{
Logger.LogError($"驱动器不存在: {drive}");
throw new Exception($"驱动器 {drive} 不存在");
}
// 验证路径
if (!Directory.Exists(customPath))
{
Logger.Log($"创建目录: {customPath}");
Directory.CreateDirectory(customPath);
}
// 更严格的路径可写性测试
string testFile = Path.Combine(customPath, $"write_test_{Guid.NewGuid()}.tmp");
Logger.Log($"测试路径可写性: {testFile}");
try
{
File.WriteAllText(testFile, DateTime.Now.ToString());
string content = File.ReadAllText(testFile);
File.Delete(testFile);
Logger.Log($"路径验证成功: {customPath}");
return customPath.TrimEnd(Path.DirectorySeparatorChar);
}
catch (Exception ex)
{
Logger.LogError($"路径不可写: {customPath}", ex);
throw new Exception($"路径不可写: {customPath}");
}
}
catch (Exception ex)
{
Logger.LogError($"路径处理失败: {customPath}", ex);
throw;
}
}
}
else
{
Logger.Log("未找到下载路径配置文件");
}
}
catch (Exception ex)
{
Logger.LogError("读取自定义下载路径失败", ex);
}
// 2. 回退到系统默认下载路径
IntPtr pathPtr = IntPtr.Zero;
try
{
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B");
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) == 0)
{
string? defaultPath = Marshal.PtrToStringUni(pathPtr);
if (!string.IsNullOrEmpty(defaultPath))
{
Directory.CreateDirectory(defaultPath);
return defaultPath;
}
else
{
Logger.LogWarning("获取到的系统下载路径为空");
}
}
}
catch (Exception ex)
{
Logger.LogError("获取系统下载路径失败", ex);
}
finally
{
if (pathPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
// 3. 最终回退到相对路径 ~/Downloads
string relativePath = "~/Downloads";
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ??
Environment.GetFolderPath(Environment.SpecialFolder.Desktop) ??
AppDomain.CurrentDomain.BaseDirectory;
if (!string.IsNullOrEmpty(userProfile))
{
fallbackPath = relativePath.Replace("~", userProfile);
if (!string.IsNullOrEmpty(fallbackPath))
{
fallbackPath = Path.GetFullPath(fallbackPath);
}
}
try {
Directory.CreateDirectory(fallbackPath);
// 测试路径可写性
string testFile = Path.Combine(fallbackPath, "write_test.tmp");
if (!string.IsNullOrEmpty(testFile))
{
File.WriteAllText(testFile, "test");
File.Delete(testFile);
}
return fallbackPath;
}
catch {
throw new Exception($"无法使用默认下载路径: {fallbackPath}");
}
}
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 KiB

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#nullable enable #nullable enable
using System; using System;
using System.Drawing; using System.Drawing;
@@ -242,6 +248,7 @@ namespace AppStore
contentPanel.Dock = DockStyle.Fill; contentPanel.Dock = DockStyle.Fill;
contentPanel.BackColor = ThemeManager.BackgroundColor; contentPanel.BackColor = ThemeManager.BackgroundColor;
contentPanel.Padding = new Padding(20); contentPanel.Padding = new Padding(20);
contentPanel.AutoScroll = true;
this.Controls.Add(contentPanel); this.Controls.Add(contentPanel);
// 添加分隔线 // 添加分隔线
@@ -458,44 +465,6 @@ namespace AppStore
}; };
flowPanel.Controls.Add(systemInfoCard); flowPanel.Controls.Add(systemInfoCard);
// 视频压缩工具卡片
var videoCompressorCard = new ToolCard();
videoCompressorCard.ToolName = "视频压缩工具";
try
{
string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png", "video_compressor.png");
if (File.Exists(iconPath))
{
videoCompressorCard.ToolIcon = Image.FromFile(iconPath);
}
else
{
videoCompressorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
}
catch
{
videoCompressorCard.ToolIcon = SystemIcons.Shield.ToBitmap();
}
videoCompressorCard.UpdateDisplay();
videoCompressorCard.ToolCardClicked += (s, e) => {
try {
string toolPath = Path.Combine(Application.StartupPath, "resource", "video_compressor.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(videoCompressorCard);
// 计算器工具卡片 // 计算器工具卡片
var calculatorCard = new CalculatorToolCard(); var calculatorCard = new CalculatorToolCard();
try try
@@ -566,6 +535,40 @@ namespace AppStore
passwordGeneratorCard.UpdateDisplay(); passwordGeneratorCard.UpdateDisplay();
flowPanel.Controls.Add(passwordGeneratorCard); 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)
{ {
@@ -633,11 +636,11 @@ namespace AppStore
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 15, 15, 15); flowPanel.Padding = new Padding(15, 15, 15, 15);
flowPanel.WrapContents = true; flowPanel.WrapContents = false;
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 5000);
// 创建搜索框 // 创建搜索框
TextBox searchBox = new TextBox(); TextBox searchBox = new TextBox();
@@ -665,11 +668,11 @@ namespace AppStore
flowPanel.Dock = DockStyle.Fill; flowPanel.Dock = DockStyle.Fill;
flowPanel.AutoScroll = true; flowPanel.AutoScroll = true;
flowPanel.Padding = new Padding(15, 60, 15, 15); flowPanel.Padding = new Padding(15, 60, 15, 15);
flowPanel.WrapContents = true; flowPanel.WrapContents = false;
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 5000);
contentPanel.Controls.Add(flowPanel); contentPanel.Controls.Add(flowPanel);
// 添加窗体关闭事件处理 // 添加窗体关闭事件处理
@@ -707,7 +710,7 @@ namespace AppStore
flowPanel.Margin = new Padding(0); flowPanel.Margin = new Padding(0);
flowPanel.AutoSize = true; flowPanel.AutoSize = true;
flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; flowPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowPanel.AutoScrollMinSize = new Size(0, 3350); flowPanel.AutoScrollMinSize = new Size(0, 3550);
contentPanel.Controls.Add(flowPanel); contentPanel.Controls.Add(flowPanel);
// 添加所有应用卡片并恢复位置 // 添加所有应用卡片并恢复位置
@@ -715,7 +718,7 @@ namespace AppStore
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
"XDM", "XDM",
"https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi", "https://ghproxy.net/https://github.com/subhra74/xdm/releases/download/7.2.11/xdm-setup.msi",
"img/png/XDM.png")); "img/png/XDM.png"));
flowPanel.Controls.Add(CreateAppCard( flowPanel.Controls.Add(CreateAppCard(
@@ -780,7 +783,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(
@@ -880,7 +883,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(
@@ -1004,6 +1007,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",
@@ -1014,6 +1022,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",
@@ -1179,6 +1197,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",

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;

View File

@@ -5,14 +5,12 @@
## 项目开源行为 ## 项目开源行为
1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。 1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
2. 项目文档开源,允许任何人使用、修改、分发、商用,但必须注明原作者。 2. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
3. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者 3. 项目的任何衍生品包括但不限于网站、APP、插件等必须遵循以上开源协议
4. 项目的任何衍生品包括但不限于网站、APP、插件等必须遵循以上开源协议 4. 项目不接受任何形式的广告,不得在任何地方投放广告
5. 项目不接受任何形式的广告,不得在任何地方投放广告。 5. 项目不接受任何形式的捐赠、赞助
6. 项目不接受任何形式的捐赠。 6. 项目可以进行PR欢迎任何形式的PR不提交issue也可以
7. 项目不接受任何形式的赞助。 7. 项目可以PR一些你自己的项目如果star数量不到1k都会被删除
8. 项目可以进行PR欢迎任何形式的PR不提交issue也可以
9. 本项目可以PR一些你自己的项目如果star数量不到1k都会被删除
## 项目简介 ## 项目简介

View File

@@ -1,8 +1,15 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using System.Drawing; using System.Drawing;
using System.Text.Json;
namespace AppStore namespace AppStore
{ {
@@ -49,7 +56,43 @@ namespace AppStore
btnCleanLogs.Click += (s, e) => CleanLogs(); btnCleanLogs.Click += (s, e) => CleanLogs();
this.Controls.Add(btnCleanLogs); 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; ThemeManager.ThemeChanged += OnThemeChanged;
LoadDownloadPath(txtDownloadPath);
} }
private void SwitchTheme(ThemeManager.ThemeMode theme) private void SwitchTheme(ThemeManager.ThemeMode theme)
@@ -84,5 +127,103 @@ namespace AppStore
MessageBox.Show($"清理日志时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show($"清理日志时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
private void BrowseDownloadPath(TextBox txtBox)
{
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
{
dialog.Description = "选择下载路径";
if (dialog.ShowDialog() == DialogResult.OK)
{
txtBox.Text = dialog.SelectedPath;
}
}
}
private void SaveDownloadPath(string path)
{
try
{
// 验证路径
if (string.IsNullOrWhiteSpace(path))
{
MessageBox.Show("下载路径不能为空", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 尝试创建目录(如果不存在)
try
{
Directory.CreateDirectory(path);
// 验证目录是否可写
string testFile = Path.Combine(path, "write_test.tmp");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
}
catch
{
MessageBox.Show($"无法访问路径: {path}\n请确保路径存在且有写入权限", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 保存路径
string dlPathDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path");
if (!Directory.Exists(dlPathDir))
{
Directory.CreateDirectory(dlPathDir);
}
string jsonPath = Path.Combine(dlPathDir, "download_path.json");
var jsonData = new { DownloadPath = path };
string jsonString = JsonSerializer.Serialize(jsonData);
File.WriteAllText(jsonPath, jsonString);
MessageBox.Show($"下载路径已保存到:\n{path}", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
Logger.LogError("保存下载路径时出错", ex);
MessageBox.Show($"保存下载路径时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadDownloadPath(TextBox txtBox)
{
// 默认下载路径为用户文件夹下的Downloads
string defaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Downloads");
try
{
string jsonPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"zsyg", "kortapp-z", ".date", "dl_path", "download_path.json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
var jsonData = JsonSerializer.Deserialize<JsonElement>(jsonString);
string customPath = jsonData.GetProperty("DownloadPath").GetString() ?? "";
// 如果自定义路径有效则显示,否则显示默认路径
txtBox.Text = !string.IsNullOrWhiteSpace(customPath) ? customPath : defaultPath;
}
else
{
txtBox.Text = defaultPath;
}
}
catch (Exception ex)
{
Logger.LogError("加载下载路径时出错", ex);
txtBox.Text = defaultPath;
}
}
} }
} }

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -42,6 +48,8 @@ namespace AppStore
try try
{ {
var dir = Path.GetDirectoryName(ThemeConfigPath); var dir = Path.GetDirectoryName(ThemeConfigPath);
if (dir == null) return;
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
{ {
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@@ -8,6 +14,8 @@ namespace AppStore
{ {
private PictureBox iconBox = new PictureBox(); private PictureBox iconBox = new PictureBox();
private Label nameLabel = new Label(); private Label nameLabel = new Label();
private Panel namePanel = new Panel();
private Color borderColor = SystemColors.ControlDark;
public string ToolName { get; set; } = string.Empty; public string ToolName { get; set; } = string.Empty;
public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap(); public Image ToolIcon { get; set; } = SystemIcons.Shield.ToBitmap();
@@ -37,14 +45,28 @@ namespace AppStore
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(iconBox); this.Controls.Add(iconBox);
// 工具名称 // 工具名称 - 使用Panel包裹Label实现边框颜色
namePanel = new Panel();
namePanel.Size = new Size(Width - 20, 30);
namePanel.Location = new Point(10, 100);
namePanel.Paint += (sender, e) => {
ControlPaint.DrawBorder(e.Graphics, namePanel.ClientRectangle,
borderColor, ButtonBorderStyle.Solid);
};
nameLabel = new Label(); nameLabel = new Label();
nameLabel.AutoSize = false; nameLabel.Dock = DockStyle.Fill;
nameLabel.Size = new Size(Width - 20, 30);
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); namePanel.Controls.Add(nameLabel);
// 初始主题设置
UpdateLabelTheme();
// 订阅主题变化事件
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
this.Controls.Add(namePanel);
// 打开按钮 // 打开按钮
var openButton = new Button(); var openButton = new Button();
@@ -68,10 +90,30 @@ namespace AppStore
openButton.FlatAppearance.MouseDownBackColor = Color.FromArgb(0, 80, 160); 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() public void UpdateDisplay()
{ {
nameLabel.Text = ToolName; nameLabel.Text = ToolName;
iconBox.Image = ToolIcon; iconBox.Image = ToolIcon;
UpdateLabelTheme();
} }
} }
} }

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#include <windows.h> #include <windows.h>
#include <vector> #include <vector>
#include <fstream> #include <fstream>

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <vector> #include <vector>

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: 608 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 946 KiB

View File

@@ -2,10 +2,10 @@
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档! ; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z" #define MyAppName "kortapp-z"
#define MyAppVersion "1.1.1" #define MyAppVersion "1.2.0"
#define MyAppPublisher "zsyg" #define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z" #define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.exe" #define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + "" #define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe" #define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt

View File

@@ -2,10 +2,10 @@
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档! ; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
#define MyAppName "kortapp-z" #define MyAppName "kortapp-z"
#define MyAppVersion "1.1.1" #define MyAppVersion "1.2.0"
#define MyAppPublisher "zsyg" #define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z" #define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.exe" #define MyAppExeName "kortapp-z.exe"
#define MyAppAssocName MyAppName + "" #define MyAppAssocName MyAppName + ""
#define MyAppAssocExt ".exe" #define MyAppAssocExt ".exe"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <chrono> #include <chrono>

View File

@@ -1,3 +1,9 @@
// _ _
//| | _____ _ __| |_ __ _ _ __ _ __ ____
//| |/ / _ \| '__| __/ _` | '_ \| '_ \ ____|_ /
//| | (_) | | | || (_| | |_) | |_) |_____/ /
//|_|\_\___/|_| \__\__,_| .__/| .__/ /___|
// |_| |_|
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@@ -59,5 +65,15 @@ namespace AppStore
} }
Log(errorMessage); Log(errorMessage);
} }
public static void LogWarning(string message, Exception? ex = null)
{
string warningMessage = $"WARNING: {message}";
if (ex != null)
{
warningMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
}
Log(warningMessage);
}
} }
} }

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,7 +50,10 @@ namespace KortAppZ.Tools.Viewer
} }
currentImage = ImageFileHandler.LoadImage(filePath); currentImage = ImageFileHandler.LoadImage(filePath);
pictureBox.Image = currentImage; if (pictureBox != null)
{
pictureBox.Image = currentImage;
}
this.Text = $"图片查看 - {Path.GetFileName(filePath)}"; this.Text = $"图片查看 - {Path.GetFileName(filePath)}";
} }
catch (Exception ex) catch (Exception ex)