Compare commits

..

30 Commits

Author SHA1 Message Date
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
36 changed files with 3063 additions and 1396 deletions

View File

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

View File

@@ -13,7 +13,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 +29,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已初始化
@@ -57,14 +60,29 @@ namespace AppStore
iconBox.SizeMode = PictureBoxSizeMode.StretchImage; iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Controls.Add(iconBox); this.Controls.Add(iconBox);
// 应用名称 // 应用名称 - 使用Panel包裹Label实现边框颜色
// namePanel已在构造函数中初始化
namePanel.Size = new Size(Width - 20, 40);
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, 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);
namePanel.Controls.Add(nameLabel);
// 初始主题设置
UpdateLabelTheme();
// 订阅主题变化事件
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
this.Controls.Add(namePanel);
// 下载按钮 // 下载按钮
downloadBtn = new Button(); downloadBtn = new Button();
@@ -92,6 +110,25 @@ namespace AppStore
downloadBtn.Visible = ShowDownloadButton; 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;
}
namePanel.Invalidate(); // 触发重绘
}
/// <summary> /// <summary>
/// 初始化卡片边框路径 /// 初始化卡片边框路径
/// 使用C++程序计算高性能边框路径并缓存结果 /// 使用C++程序计算高性能边框路径并缓存结果
@@ -310,8 +347,14 @@ namespace AppStore
public void UpdateDisplay() public void UpdateDisplay()
{ {
nameLabel.Text = AppName; if (nameLabel != null)
iconBox.Image = AppIcon; {
nameLabel.Text = AppName;
}
if (iconBox != null && AppIcon != null)
{
iconBox.Image = AppIcon;
}
} }
private void DownloadBtn_Click(object sender, EventArgs e) private void DownloadBtn_Click(object sender, EventArgs e)

57
AppSearch.cs Normal file
View File

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

View File

@@ -33,4 +33,8 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="ZXing.Net" Version="0.16.9" />
</ItemGroup>
</Project> </Project>

View File

@@ -23,13 +23,40 @@ namespace AppStore
cancelBtn = new Button(); cancelBtn = new Button();
InitializeComponent(); InitializeComponent();
// 监听主题变化
ThemeManager.ThemeChanged += (theme) => {
this.Invoke((MethodInvoker)delegate {
ApplyTheme();
});
};
}
private void ApplyTheme()
{
this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.White
: Color.Black;
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.Black
: Color.White;
cancelBtn.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? SystemColors.Control
: Color.FromArgb(70, 70, 70);
cancelBtn.ForeColor = ThemeManager.TextColor;
} }
private void InitializeComponent() private void InitializeComponent()
{ {
this.Size = new Size(400, 60); this.Size = new Size(400, 60);
this.BackColor = Color.White; this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.White
: Color.Black;
this.BorderStyle = BorderStyle.FixedSingle; this.BorderStyle = BorderStyle.FixedSingle;
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? Color.Black
: Color.White;
// 文件名标签 // 文件名标签
nameLabel = new Label(); nameLabel = new Label();
@@ -56,6 +83,12 @@ namespace AppStore
cancelBtn.Text = "取消"; cancelBtn.Text = "取消";
cancelBtn.Size = new Size(60, 25); cancelBtn.Size = new Size(60, 25);
cancelBtn.Location = new Point(320, 30); cancelBtn.Location = new Point(320, 30);
cancelBtn.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
? SystemColors.Control
: Color.FromArgb(70, 70, 70);
cancelBtn.ForeColor = ThemeManager.TextColor;
cancelBtn.FlatStyle = FlatStyle.Flat;
cancelBtn.FlatAppearance.BorderSize = 0;
cancelBtn.Click += CancelBtn_Click; cancelBtn.Click += CancelBtn_Click;
this.Controls.Add(cancelBtn); this.Controls.Add(cancelBtn);
} }

View File

@@ -3,6 +3,7 @@ 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;
@@ -85,36 +86,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("下载路径为空");
} }
// 尝试创建目录(如果不存在)
Directory.CreateDirectory(downloadsDir);
// 验证目录是否可写
string testFile = Path.Combine(downloadsDir, "write_test.tmp");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
}
catch (Exception ex)
{
// 回退到默认下载路径
string defaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Downloads");
downloadsDir = Marshal.PtrToStringUni(pathPtr); Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
downloadsDir = defaultPath;
Directory.CreateDirectory(downloadsDir);
} }
catch
{
throw new Exception("无法确定下载文件夹位置,请手动指定下载路径");
}
finally
{
if (pathPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
Directory.CreateDirectory(downloadsDir);
@@ -309,12 +313,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 +334,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 +354,138 @@ namespace AppStore
DownloadProgressChanged?.Invoke(item); DownloadProgressChanged?.Invoke(item);
} }
} }
private string GetDownloadPath()
{
// 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;
}
}
}
catch (Exception ex)
{
Logger.LogError("获取系统下载路径失败", ex);
}
finally
{
if (pathPtr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
// 3. 最终回退到相对路径 ~/Downloads
string relativePath = "~/Downloads";
string fallbackPath = relativePath.Replace("~",
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
fallbackPath = Path.GetFullPath(fallbackPath);
try {
Directory.CreateDirectory(fallbackPath);
// 测试路径可写性
string testFile = Path.Combine(fallbackPath, "write_test.tmp");
File.WriteAllText(testFile, "test");
File.Delete(testFile);
return fallbackPath;
}
catch {
throw new Exception($"无法使用默认下载路径: {fallbackPath}");
}
}
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 KiB

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

129
ThemeManager.cs Normal file
View File

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

@@ -8,6 +8,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 +39,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 +84,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();
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 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.3"
#define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.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.3"
#define MyAppPublisher "zsyg"
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
#define MyAppExeName "kortapp.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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,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.

34
tools/Calculator.cs Normal file
View File

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

118
tools/CalculatorForm.cs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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