Compare commits
8 Commits
cffa1c879f
...
6c7119ced4
Author | SHA1 | Date |
---|---|---|
![]() |
6c7119ced4 | |
![]() |
9a354ed2fc | |
![]() |
da1915c6c0 | |
![]() |
eac4f23422 | |
![]() |
167212bbf2 | |
![]() |
63e495ffa7 | |
![]() |
1b2d418d6a | |
![]() |
0a3da756bf |
|
@ -50,4 +50,9 @@
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</Shell.FlyoutFooter>
|
</Shell.FlyoutFooter>
|
||||||
|
|
||||||
|
<!-- 注册开发者页面的路由,但不在TabBar中显示 -->
|
||||||
|
<ShellContent
|
||||||
|
Title="开发者模式"
|
||||||
|
ContentTemplate="{DataTemplate page:DeveloperPage}"
|
||||||
|
Route="DeveloperPage" />
|
||||||
</Shell>
|
</Shell>
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace installer
|
||||||
Routing.RegisterRoute("DebugPage", typeof(Page.DebugPage));
|
Routing.RegisterRoute("DebugPage", typeof(Page.DebugPage));
|
||||||
Routing.RegisterRoute("PlaybackPage", typeof(Page.PlaybackPage));
|
Routing.RegisterRoute("PlaybackPage", typeof(Page.PlaybackPage));
|
||||||
Routing.RegisterRoute("LoginPage", typeof(Page.LoginPage));
|
Routing.RegisterRoute("LoginPage", typeof(Page.LoginPage));
|
||||||
|
Routing.RegisterRoute("HelpPage", typeof(Page.HelpPage));
|
||||||
|
Routing.RegisterRoute("DeveloperPage", typeof(Page.DeveloperPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNavigating(ShellNavigatingEventArgs args)
|
protected override void OnNavigating(ShellNavigatingEventArgs args)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# THUAI8 开发者指南
|
||||||
|
|
||||||
|
### 访问开发者模式
|
||||||
|
|
||||||
|
1. 打开"帮助"页面
|
||||||
|
2. 在3秒内快速连续点击页面顶部的"THUAI8 帮助"标题5次
|
||||||
|
|
||||||
|
### 配置密钥步骤
|
||||||
|
|
||||||
|
1. 在开发者模式界面输入腾讯云SecretID和SecretKey
|
||||||
|
2. 设置加密密码(仅用于本地加密,不会被保存)
|
||||||
|
3. 点击"生成加密密钥"按钮
|
||||||
|
4. 将生成的secured_key.csv文件添加为嵌入式资源
|
||||||
|
5. 重新编译项目
|
||||||
|
|
||||||
|
### 嵌入式资源配置
|
||||||
|
|
||||||
|
项目已配置自动识别Resources\Raw目录下的secured_key.csv文件作为嵌入式资源:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources\Raw\secured_key.csv" Condition="Exists('Resources\Raw\secured_key.csv')" />
|
||||||
|
</ItemGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
**添加方法**:
|
||||||
|
1. **VS界面**:创建Resources/Raw文件夹 → 添加文件 → 属性设为"嵌入式资源"
|
||||||
|
2. **手动方式**:复制文件到Resources\Raw目录 → 确认项目文件包含上述XML配置
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*注意:请妥善保管加密密码。如果忘记密码,将需要重新生成密钥。*
|
|
@ -24,115 +24,71 @@ namespace installer
|
||||||
// public static Model.Logger logger = Model.LoggerProvider.FromFile(@"E:\bin\log\123.log");
|
// public static Model.Logger logger = Model.LoggerProvider.FromFile(@"E:\bin\log\123.log");
|
||||||
public static bool ErrorTrigger_WhileDebug = true;
|
public static bool ErrorTrigger_WhileDebug = true;
|
||||||
public static bool RefreshLogs_WhileDebug = false;
|
public static bool RefreshLogs_WhileDebug = false;
|
||||||
public static string SecretID = "***";
|
public static string? SecretID = null;
|
||||||
public static string SecretKey = "***";
|
public static string? SecretKey = null;
|
||||||
|
|
||||||
public static MauiApp CreateMauiApp()
|
public static MauiApp CreateMauiApp()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 首先初始化调试工具
|
// 确保初始化调试工具
|
||||||
DebugTool.Initialize();
|
DebugTool.Initialize();
|
||||||
DebugTool.Log("开始创建MAUI应用程序");
|
DebugTool.Log("调试工具已初始化");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 无法记录日志,但尝试继续
|
||||||
|
Debug.WriteLine($"初始化调试工具失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
// 捕获UI线程同步上下文
|
var builder = MauiApp.CreateBuilder();
|
||||||
UISynchronizationContext = SynchronizationContext.Current;
|
try
|
||||||
DebugTool.Log("UI同步上下文已捕获");
|
{
|
||||||
|
builder
|
||||||
|
.UseMauiApp<App>()
|
||||||
|
.ConfigureFonts(fonts =>
|
||||||
|
{
|
||||||
|
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||||
|
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化默认空密钥
|
||||||
|
SecretID = "***";
|
||||||
|
SecretKey = "***";
|
||||||
|
|
||||||
// read SecretID & SecretKey from filePath for debug
|
|
||||||
var filePath = Debugger.IsAttached ? "D:\\Secret.csv" : Path.Combine(AppContext.BaseDirectory, "Secret.csv");
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(filePath))
|
// 首先尝试从嵌入式资源中读取密钥
|
||||||
|
try
|
||||||
{
|
{
|
||||||
DebugTool.Log($"正在读取Secret文件: {filePath}");
|
LoadSecretFromEmbeddedResource();
|
||||||
var lines = File.ReadAllLines(filePath);
|
|
||||||
if (lines.Length >= 4)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lines = lines.Select(s => s.Trim().Trim('\r', '\n')).ToArray();
|
|
||||||
using (Aes aes = Aes.Create())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
aes.Key = Convert.FromBase64String(lines[0]);
|
|
||||||
aes.IV = Convert.FromBase64String(lines[1]);
|
|
||||||
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[2])))
|
|
||||||
{
|
|
||||||
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
|
||||||
{
|
|
||||||
using (StreamReader reader = new StreamReader(crypto, Encoding.ASCII))
|
|
||||||
SecretID = reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[3])))
|
|
||||||
{
|
|
||||||
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
|
||||||
{
|
|
||||||
using (StreamReader reader = new StreamReader(crypto, Encoding.ASCII))
|
|
||||||
SecretKey = reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DebugTool.Log("Secret文件解密完成");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DebugTool.LogException(ex, "解密Secret内容");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DebugTool.LogException(ex, "初始化AES解密器");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DebugTool.LogException(ex, "处理Secret文件行");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DebugTool.Log($"Secret文件格式不正确,行数: {lines.Length}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.Log($"从嵌入式资源加载密钥失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果嵌入式资源中没有密钥,则尝试从外部文件读取
|
||||||
|
if (string.IsNullOrEmpty(SecretID) || SecretID == "***" || string.IsNullOrEmpty(SecretKey) || SecretKey == "***")
|
||||||
{
|
{
|
||||||
DebugTool.Log($"Secret文件不存在: {filePath}");
|
|
||||||
// 创建一个简单的Secret.csv用于测试
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DebugTool.Log("创建默认Secret.csv文件");
|
LoadSecretFromExternalFile();
|
||||||
using (StreamWriter sw = new StreamWriter(filePath))
|
|
||||||
{
|
|
||||||
// 使用固定密钥,仅用于测试
|
|
||||||
sw.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); // Key
|
|
||||||
sw.WriteLine("AAAAAAAAAAAAAAAAAAAAAA=="); // IV
|
|
||||||
sw.WriteLine("AAAAAAAAAAAAAAAAAAAAAA=="); // Encrypted SecretID
|
|
||||||
sw.WriteLine("AAAAAAAAAAAAAAAAAAAAAA=="); // Encrypted SecretKey
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
DebugTool.LogException(ex, "创建默认Secret.csv");
|
DebugTool.Log($"从外部文件加载密钥失败: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
DebugTool.LogException(ex, "读取Secret文件");
|
// 确保任何密钥加载问题都不会导致应用崩溃
|
||||||
|
DebugTool.LogException(ex, "密钥加载过程中发生未处理的异常");
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugTool.Log("开始配置MAUI应用程序");
|
DebugTool.Log("开始配置MAUI应用程序");
|
||||||
var builder = MauiApp.CreateBuilder();
|
|
||||||
builder
|
builder
|
||||||
.UseMauiApp<App>()
|
|
||||||
.UseMauiCommunityToolkitCore();
|
.UseMauiCommunityToolkitCore();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -206,11 +162,166 @@ namespace installer
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
DebugTool.LogException(ex, "创建MAUI应用");
|
DebugTool.LogException(ex, "MAUI应用程序初始化");
|
||||||
throw; // 重新抛出异常以便能够看到崩溃信息
|
throw; // 重新抛出异常以便能够看到崩溃信息
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LoadSecretFromEmbeddedResource()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
string resourceName = "installer.Resources.Raw.secured_key.csv";
|
||||||
|
|
||||||
|
// 列出所有嵌入资源以进行调试
|
||||||
|
var resources = assembly.GetManifestResourceNames();
|
||||||
|
DebugTool.Log($"可用资源: {string.Join(", ", resources)}");
|
||||||
|
|
||||||
|
using (Stream? stream = assembly.GetManifestResourceStream(resourceName))
|
||||||
|
{
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
string content = reader.ReadToEnd();
|
||||||
|
var lines = content.Split('\n').Select(s => s.Trim().Trim('\r')).ToArray();
|
||||||
|
|
||||||
|
if (lines.Length >= 4)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = Convert.FromBase64String(lines[0]);
|
||||||
|
aes.IV = Convert.FromBase64String(lines[1]);
|
||||||
|
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 解密SecretID
|
||||||
|
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[2])))
|
||||||
|
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
||||||
|
using (StreamReader cryptoReader = new StreamReader(crypto))
|
||||||
|
{
|
||||||
|
SecretID = cryptoReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密SecretKey
|
||||||
|
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[3])))
|
||||||
|
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
||||||
|
using (StreamReader cryptoReader = new StreamReader(crypto))
|
||||||
|
{
|
||||||
|
SecretKey = cryptoReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTool.Log("从嵌入式资源成功加载密钥");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "解密嵌入式资源密钥内容");
|
||||||
|
// 不抛出异常,使程序可以继续运行
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "初始化嵌入式资源AES解密器");
|
||||||
|
// 不抛出异常,使程序可以继续运行
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugTool.Log($"嵌入式资源密钥文件格式不正确,行数: {lines.Length}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugTool.Log("未找到嵌入式资源密钥文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "读取嵌入式资源出错");
|
||||||
|
// 不抛出异常,允许回退到其他加载方式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadSecretFromExternalFile()
|
||||||
|
{
|
||||||
|
// 保留原有的外部文件读取逻辑
|
||||||
|
// read SecretID & SecretKey from filePath for debug
|
||||||
|
var filePath = Debugger.IsAttached ? "D:\\Secret.csv" : Path.Combine(AppContext.BaseDirectory, "Secret.csv");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
{
|
||||||
|
DebugTool.Log($"正在读取Secret文件: {filePath}");
|
||||||
|
var lines = File.ReadAllLines(filePath);
|
||||||
|
if (lines.Length >= 4)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lines = lines.Select(s => s.Trim().Trim('\r', '\n')).ToArray();
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
aes.Key = Convert.FromBase64String(lines[0]);
|
||||||
|
aes.IV = Convert.FromBase64String(lines[1]);
|
||||||
|
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[2])))
|
||||||
|
{
|
||||||
|
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(crypto, Encoding.ASCII))
|
||||||
|
SecretID = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (MemoryStream memory = new MemoryStream(Convert.FromBase64String(lines[3])))
|
||||||
|
{
|
||||||
|
using (CryptoStream crypto = new CryptoStream(memory, decryptor, CryptoStreamMode.Read))
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(crypto, Encoding.ASCII))
|
||||||
|
SecretKey = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DebugTool.Log("Secret文件解密完成");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "解密Secret内容");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "初始化AES解密器");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "处理Secret文件行");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugTool.Log($"Secret文件格式不正确,行数: {lines.Length}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "读取外部Secret文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void AddViewModelService(MauiAppBuilder builder)
|
public static void AddViewModelService(MauiAppBuilder builder)
|
||||||
{
|
{
|
||||||
var a = typeof(MauiProgram).Assembly;
|
var a = typeof(MauiProgram).Assembly;
|
||||||
|
|
|
@ -36,11 +36,18 @@ namespace installer.Model
|
||||||
public Logger Log;
|
public Logger Log;
|
||||||
|
|
||||||
public LoginStatus Status = LoginStatus.offline;
|
public LoginStatus Status = LoginStatus.offline;
|
||||||
public Tencent_Cos EEsast_Cos { get; protected set; } = new Tencent_Cos("1255334966", "ap-beijing", "eesast");//服务器应该还能用吧
|
public Tencent_Cos EEsast_Cos { get; protected set; } = new Tencent_Cos("1255334966", "ap-beijing", "eesast");
|
||||||
|
|
||||||
public EEsast(Logger? _log = null)
|
public EEsast(Logger? _log = null)
|
||||||
{
|
{
|
||||||
Log = _log ?? LoggerProvider.FromConsole();
|
Log = _log ?? LoggerProvider.FromConsole();
|
||||||
Log.PartnerInfo = "[EESAST]";
|
Log.PartnerInfo = "[EESAST]";
|
||||||
|
|
||||||
|
// 使用全局密钥
|
||||||
|
if (!string.IsNullOrEmpty(MauiProgram.SecretID) && !string.IsNullOrEmpty(MauiProgram.SecretKey))
|
||||||
|
{
|
||||||
|
EEsast_Cos.UpdateSecret(MauiProgram.SecretID, MauiProgram.SecretKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public async Task LoginToEEsast(HttpClient client, string useremail = "", string userpassword = "")
|
public async Task LoginToEEsast(HttpClient client, string useremail = "", string userpassword = "")
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,25 @@ namespace installer.Model
|
||||||
.SetRegion(Region) // 设置一个默认的存储桶地域
|
.SetRegion(Region) // 设置一个默认的存储桶地域
|
||||||
.SetDebugLog(true) // 显示日志
|
.SetDebugLog(true) // 显示日志
|
||||||
.Build(); // 创建 CosXmlConfig 对象
|
.Build(); // 创建 CosXmlConfig 对象
|
||||||
QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider("***", "***", 1000);
|
|
||||||
|
// 使用全局密钥
|
||||||
|
string secretId = MauiProgram.SecretID;
|
||||||
|
string secretKey = MauiProgram.SecretKey;
|
||||||
|
|
||||||
|
// 确保密钥值有效,如果没有则使用默认值
|
||||||
|
if (string.IsNullOrEmpty(secretId) || secretId == "***")
|
||||||
|
{
|
||||||
|
secretId = "***"; // 默认值或占位符
|
||||||
|
Log.LogWarning("使用默认SecretID - 注意:这将导致API访问受限");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(secretKey) || secretKey == "***")
|
||||||
|
{
|
||||||
|
secretKey = "***"; // 默认值或占位符
|
||||||
|
Log.LogWarning("使用默认SecretKey - 注意:这将导致API访问受限");
|
||||||
|
}
|
||||||
|
|
||||||
|
QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(secretId, secretKey, 1000);
|
||||||
cosXml = new CosXmlServer(config, cosCredentialProvider);
|
cosXml = new CosXmlServer(config, cosCredentialProvider);
|
||||||
transfer = new TransferConfig()
|
transfer = new TransferConfig()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="installer.Page.DeveloperPage"
|
||||||
|
Title="开发者模式">
|
||||||
|
<ScrollView>
|
||||||
|
<VerticalStackLayout Padding="20" Spacing="20">
|
||||||
|
<Frame BorderColor="Gray" Padding="15" CornerRadius="5">
|
||||||
|
<VerticalStackLayout Spacing="10">
|
||||||
|
<Label Text="腾讯云密钥配置" FontSize="20" FontAttributes="Bold" />
|
||||||
|
<Label Text="此配置仅供开发人员使用。普通用户无需设置此项,直接使用程序内置的密钥即可。"
|
||||||
|
TextColor="Gray" />
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="10" RowSpacing="15">
|
||||||
|
<Label Text="腾讯云SecretID:" Grid.Row="0" Grid.Column="0" VerticalOptions="Center" />
|
||||||
|
<Entry x:Name="SecretIDEntry" Grid.Row="0" Grid.Column="1" IsPassword="True" />
|
||||||
|
|
||||||
|
<Label Text="腾讯云SecretKey:" Grid.Row="1" Grid.Column="0" VerticalOptions="Center" />
|
||||||
|
<Entry x:Name="SecretKeyEntry" Grid.Row="1" Grid.Column="1" IsPassword="True" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Label Text="加密密码(用于保护密钥,请务必记住此密码):" />
|
||||||
|
<Entry x:Name="EncryptionPasswordEntry" IsPassword="True" />
|
||||||
|
|
||||||
|
<Button x:Name="GenerateKeyButton" Text="生成加密密钥" Clicked="OnGenerateKeyClicked"
|
||||||
|
HorizontalOptions="Start" Margin="0,10,0,0" />
|
||||||
|
|
||||||
|
<Label x:Name="StatusLabel" TextColor="Gray" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
<Frame BorderColor="Gray" Padding="15" CornerRadius="5" IsVisible="{Binding ResourceKeyGenerated}">
|
||||||
|
<VerticalStackLayout Spacing="10">
|
||||||
|
<Label Text="嵌入式资源密钥" FontSize="18" FontAttributes="Bold" />
|
||||||
|
<Label Text="请将以下文件添加为项目的嵌入式资源。添加后,所有用户都可以使用此密钥,无需额外配置。"
|
||||||
|
TextColor="Gray" />
|
||||||
|
|
||||||
|
<Label Text="加密密钥文件路径:" />
|
||||||
|
<Label x:Name="KeyFilePathLabel" TextColor="Blue" />
|
||||||
|
|
||||||
|
<Button x:Name="OpenFolderButton" Text="打开文件位置" Clicked="OnOpenFolderClicked"
|
||||||
|
HorizontalOptions="Start" />
|
||||||
|
|
||||||
|
<Button x:Name="CopyPathButton" Text="复制路径" Clicked="OnCopyPathClicked"
|
||||||
|
HorizontalOptions="Start" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
<Frame BorderColor="Gray" Padding="15" CornerRadius="5">
|
||||||
|
<VerticalStackLayout Spacing="10">
|
||||||
|
<Label Text="使用说明" FontSize="18" FontAttributes="Bold" />
|
||||||
|
<Label Text="1. 输入腾讯云SecretID、SecretKey和用于加密的密码" />
|
||||||
|
<Label Text="2. 点击'生成加密密钥'按钮生成密钥文件" />
|
||||||
|
<Label Text="3. 将生成的密钥文件添加为项目的嵌入式资源" />
|
||||||
|
<Label Text="4. 重新编译项目后,所有用户都能无需配置地使用腾讯云服务" />
|
||||||
|
<Label Text="注意:密码仅用于加密本地文件,不会被保存或上传" TextColor="Red" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
<Button x:Name="ExitButton"
|
||||||
|
Text="退出开发者模式"
|
||||||
|
Clicked="OnExitButtonClicked"
|
||||||
|
BackgroundColor="#FFD2D2"
|
||||||
|
TextColor="Black"
|
||||||
|
Margin="0,20,0,0"
|
||||||
|
HorizontalOptions="Center" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</ContentPage>
|
|
@ -0,0 +1,226 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Maui.ApplicationModel;
|
||||||
|
using Microsoft.Maui.Storage;
|
||||||
|
|
||||||
|
namespace installer.Page
|
||||||
|
{
|
||||||
|
public partial class DeveloperPage : ContentPage
|
||||||
|
{
|
||||||
|
private bool _resourceKeyGenerated = false;
|
||||||
|
private string _encryptedKeyFilePath = string.Empty;
|
||||||
|
|
||||||
|
public bool ResourceKeyGenerated
|
||||||
|
{
|
||||||
|
get => _resourceKeyGenerated;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_resourceKeyGenerated = value;
|
||||||
|
OnPropertyChanged(nameof(ResourceKeyGenerated));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeveloperPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
BindingContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnGenerateKeyClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(SecretIDEntry.Text) ||
|
||||||
|
string.IsNullOrWhiteSpace(SecretKeyEntry.Text) ||
|
||||||
|
string.IsNullOrWhiteSpace(EncryptionPasswordEntry.Text))
|
||||||
|
{
|
||||||
|
await DisplayAlert("输入错误", "请填写所有必填字段", "确定");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示进度指示
|
||||||
|
StatusLabel.Text = "正在生成密钥...";
|
||||||
|
StatusLabel.TextColor = Colors.Blue;
|
||||||
|
GenerateKeyButton.IsEnabled = false;
|
||||||
|
|
||||||
|
// 在后台线程中执行密钥生成
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 创建安全随机密钥和IV
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
// 从密码生成密钥
|
||||||
|
using (var deriveBytes = new Rfc2898DeriveBytes(
|
||||||
|
EncryptionPasswordEntry.Text,
|
||||||
|
new byte[16], // 静态盐,在解密时需要相同
|
||||||
|
10000,
|
||||||
|
HashAlgorithmName.SHA256))
|
||||||
|
{
|
||||||
|
aes.Key = deriveBytes.GetBytes(32); // 256位密钥
|
||||||
|
aes.IV = deriveBytes.GetBytes(16); // 128位IV
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密SecretID和SecretKey
|
||||||
|
var encryptedSecretID = EncryptString(SecretIDEntry.Text, aes.Key, aes.IV);
|
||||||
|
var encryptedSecretKey = EncryptString(SecretKeyEntry.Text, aes.Key, aes.IV);
|
||||||
|
|
||||||
|
// 创建密钥文件内容
|
||||||
|
string resourceContent = $"{Convert.ToBase64String(aes.Key)}\n{Convert.ToBase64String(aes.IV)}\n{encryptedSecretID}\n{encryptedSecretKey}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 为嵌入式资源创建目录
|
||||||
|
string resourceDirPath = Path.Combine(AppContext.BaseDirectory, "Resources", "Raw");
|
||||||
|
Directory.CreateDirectory(resourceDirPath);
|
||||||
|
|
||||||
|
// 保存密钥文件
|
||||||
|
_encryptedKeyFilePath = Path.Combine(resourceDirPath, "secured_key.csv");
|
||||||
|
File.WriteAllText(_encryptedKeyFilePath, resourceContent);
|
||||||
|
|
||||||
|
// 更新UI必须在主线程中执行
|
||||||
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
KeyFilePathLabel.Text = _encryptedKeyFilePath;
|
||||||
|
ResourceKeyGenerated = true;
|
||||||
|
StatusLabel.Text = "密钥已生成,请将其添加为嵌入式资源";
|
||||||
|
StatusLabel.TextColor = Colors.Green;
|
||||||
|
GenerateKeyButton.IsEnabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "保存加密密钥文件");
|
||||||
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
await DisplayAlert("错误", $"保存密钥文件失败: {ex.Message}", "确定");
|
||||||
|
StatusLabel.Text = "保存密钥文件失败,请检查日志";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
GenerateKeyButton.IsEnabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DebugTool.LogException(ex, "生成密钥加密过程");
|
||||||
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
await DisplayAlert("错误", $"加密密钥失败: {ex.Message}", "确定");
|
||||||
|
StatusLabel.Text = "加密密钥失败,请重试";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
GenerateKeyButton.IsEnabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 处理UI线程异常
|
||||||
|
await DisplayAlert("错误", $"生成密钥时出错: {ex.Message}", "确定");
|
||||||
|
StatusLabel.Text = "密钥生成失败,请重试";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
GenerateKeyButton.IsEnabled = true;
|
||||||
|
DebugTool.LogException(ex, "生成密钥");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EncryptString(string plainText, byte[] key, byte[] iv)
|
||||||
|
{
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = key;
|
||||||
|
aes.IV = iv;
|
||||||
|
|
||||||
|
using (MemoryStream memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (CryptoStream cryptoStream = new CryptoStream(
|
||||||
|
memoryStream,
|
||||||
|
aes.CreateEncryptor(),
|
||||||
|
CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
using (StreamWriter writer = new StreamWriter(cryptoStream))
|
||||||
|
{
|
||||||
|
writer.Write(plainText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Convert.ToBase64String(memoryStream.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOpenFolderClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_encryptedKeyFilePath) && File.Exists(_encryptedKeyFilePath))
|
||||||
|
{
|
||||||
|
Process.Start("explorer.exe", $"/select,\"{_encryptedKeyFilePath}\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusLabel.Text = "密钥文件不存在,请先生成密钥";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusLabel.Text = $"无法打开文件位置: {ex.Message}";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
DebugTool.LogException(ex, "打开文件位置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnCopyPathClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_encryptedKeyFilePath))
|
||||||
|
{
|
||||||
|
await Clipboard.SetTextAsync(_encryptedKeyFilePath);
|
||||||
|
StatusLabel.Text = "路径已复制到剪贴板";
|
||||||
|
StatusLabel.TextColor = Colors.Green;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusLabel.Text = "密钥文件不存在,请先生成密钥";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusLabel.Text = $"无法复制路径: {ex.Message}";
|
||||||
|
StatusLabel.TextColor = Colors.Red;
|
||||||
|
DebugTool.LogException(ex, "复制路径");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnExitButtonClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 返回到主Shell
|
||||||
|
await Shell.Current.GoToAsync("//InstallPage");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 如果导航失败,记录错误并尝试使用其他导航方式
|
||||||
|
DebugTool.LogException(ex, "退出开发者模式");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试返回
|
||||||
|
await Shell.Current.GoToAsync("..");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,32 @@
|
||||||
x:Class="installer.Page.HelpPage"
|
x:Class="installer.Page.HelpPage"
|
||||||
Title="Help">
|
Title="Help">
|
||||||
|
|
||||||
|
<ContentPage.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<Style x:Key="DevModeHeaderStyle" TargetType="Label">
|
||||||
|
<Setter Property="FontSize" Value="10" />
|
||||||
|
<Setter Property="HorizontalOptions" Value="Center" />
|
||||||
|
<Setter Property="TextColor" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
WidthRequest="750"
|
WidthRequest="750"
|
||||||
HeightRequest="500"
|
HeightRequest="500"
|
||||||
HorizontalOptions="Center">
|
HorizontalOptions="Center">
|
||||||
<VerticalStackLayout>
|
<VerticalStackLayout Padding="10" Spacing="10">
|
||||||
|
|
||||||
|
<Label x:Name="HeaderLabel"
|
||||||
|
Text="THUAI8 帮助"
|
||||||
|
FontSize="20"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
Margin="0,0,0,10">
|
||||||
|
<Label.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer Tapped="OnHeaderTapped" />
|
||||||
|
</Label.GestureRecognizers>
|
||||||
|
</Label>
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
Text="Installer"
|
Text="Installer"
|
||||||
|
|
|
@ -1,12 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using installer.ViewModel;
|
using installer.ViewModel;
|
||||||
|
|
||||||
namespace installer.Page;
|
namespace installer.Page;
|
||||||
|
|
||||||
public partial class HelpPage : ContentPage
|
public partial class HelpPage : ContentPage
|
||||||
{
|
{
|
||||||
|
private int _tapCount = 0;
|
||||||
|
private DateTime _lastTapTime = DateTime.MinValue;
|
||||||
|
|
||||||
public HelpPage(HelpViewModel viewModel)
|
public HelpPage(HelpViewModel viewModel)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
BindingContext = viewModel;
|
BindingContext = viewModel;
|
||||||
|
|
||||||
|
// 初始化点击计数器
|
||||||
|
_tapCount = 0;
|
||||||
|
_lastTapTime = DateTime.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
|
||||||
|
// 页面显示时重置计数器
|
||||||
|
_tapCount = 0;
|
||||||
|
_lastTapTime = DateTime.MinValue;
|
||||||
|
|
||||||
|
Debug.WriteLine("帮助页面已显示,点击计数器已重置");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnHeaderTapped(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
|
||||||
|
// 检查是否在3秒内点击
|
||||||
|
if ((now - _lastTapTime).TotalSeconds <= 3)
|
||||||
|
{
|
||||||
|
_tapCount++;
|
||||||
|
Debug.WriteLine($"帮助页面标题被点击,当前点击次数: {_tapCount}");
|
||||||
|
|
||||||
|
// 给用户一些视觉反馈,但不明显
|
||||||
|
await HeaderLabel.ScaleTo(1.05, 50);
|
||||||
|
await HeaderLabel.ScaleTo(1.0, 50);
|
||||||
|
|
||||||
|
if (_tapCount >= 5)
|
||||||
|
{
|
||||||
|
_tapCount = 0;
|
||||||
|
Debug.WriteLine("触发开发者模式");
|
||||||
|
|
||||||
|
// 导航到开发者页面
|
||||||
|
await Shell.Current.GoToAsync("//DeveloperPage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 超过3秒,重置计数器
|
||||||
|
_tapCount = 1;
|
||||||
|
Debug.WriteLine("点击计数器已重置(超时)");
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastTapTime = now;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"处理标题点击错误: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -82,7 +82,7 @@
|
||||||
|
|
||||||
<Grid ColumnDefinitions="Auto, *" ColumnSpacing="15">
|
<Grid ColumnDefinitions="Auto, *" ColumnSpacing="15">
|
||||||
<Label Grid.Column="0"
|
<Label Grid.Column="0"
|
||||||
Text="{Binding PlaybackSpeed, StringFormat='速度: {0:F2}x'}"
|
Text="{Binding PlaybackSpeed, StringFormat='速度: {0:F1}x'}"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
WidthRequest="100"
|
WidthRequest="100"
|
||||||
TextColor="#2c3e50"/>
|
TextColor="#2c3e50"/>
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace installer.ViewModel
|
||||||
IP = Downloader.Data.Config.Commands.IP;
|
IP = Downloader.Data.Config.Commands.IP;
|
||||||
Port = Downloader.Data.Config.Commands.Port;
|
Port = Downloader.Data.Config.Commands.Port;
|
||||||
PlaybackFile = Downloader.Data.Config.Commands.PlaybackFile;
|
PlaybackFile = Downloader.Data.Config.Commands.PlaybackFile;
|
||||||
PlaybackSpeed = Downloader.Data.Config.Commands.PlaybackSpeed.ToString();
|
PlaybackSpeed = Downloader.Data.Config.Commands.PlaybackSpeed.ToString("F1");
|
||||||
|
|
||||||
ipChanged = false;
|
ipChanged = false;
|
||||||
portChanged = false;
|
portChanged = false;
|
||||||
|
@ -127,8 +127,15 @@ namespace installer.ViewModel
|
||||||
get => playbackSpeed;
|
get => playbackSpeed;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
playbackSpeed = value;
|
if (double.TryParse(value, out double speed))
|
||||||
if (playbackSpeed == Downloader.Data.Config.Commands.PlaybackSpeed.ToString())
|
{
|
||||||
|
playbackSpeed = speed.ToString("F1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
playbackSpeed = value;
|
||||||
|
}
|
||||||
|
if (playbackSpeed == Downloader.Data.Config.Commands.PlaybackSpeed.ToString("F1"))
|
||||||
playbackSpeedChanged = false;
|
playbackSpeedChanged = false;
|
||||||
else
|
else
|
||||||
playbackSpeedChanged = true;
|
playbackSpeedChanged = true;
|
||||||
|
|
|
@ -46,6 +46,9 @@
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.19041.0|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||||
<ApplicationDisplayVersion>1.0.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>1.0.0</ApplicationDisplayVersion>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
|
<WindowsAppSDKBootstrapAutoInitialize>true</WindowsAppSDKBootstrapAutoInitialize>
|
||||||
|
<UseRidGraph>true</UseRidGraph>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.19041.0|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||||
|
@ -131,7 +134,12 @@
|
||||||
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
|
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<WindowsAppSDKBootstrapAutoInitialize>true</WindowsAppSDKBootstrapAutoInitialize>
|
<WindowsAppSDKBootstrapAutoInitialize>true</WindowsAppSDKBootstrapAutoInitialize>
|
||||||
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources\Raw\secured_key.csv" Condition="Exists('Resources\Raw\secured_key.csv')" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue