增加热更功能
This commit is contained in:
parent
4a5dd2418b
commit
235021cf90
4
.gitignore
vendored
4
.gitignore
vendored
@ -3,3 +3,7 @@ dujiaoka-master/
|
|||||||
Vmianqian/
|
Vmianqian/
|
||||||
.vs
|
.vs
|
||||||
.idea
|
.idea
|
||||||
|
bin_proxycheck/
|
||||||
|
bin/Debug/*
|
||||||
|
bin/Output/*
|
||||||
|
bin/Release/*
|
||||||
|
|||||||
451
AlipayMonitor.cs
451
AlipayMonitor.cs
@ -159,6 +159,29 @@ public sealed class AlipayMonitorOptions
|
|||||||
/// 轮询时默认回查最近多少天的账单。
|
/// 轮询时默认回查最近多少天的账单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int QueryDays { get; set; } = 1;
|
public int QueryDays { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可选:支付宝账号。
|
||||||
|
/// 仅用于 WebView2 登录页自动填充,不参与后台 HttpClient 直登。
|
||||||
|
/// </summary>
|
||||||
|
public string Account { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可选:支付宝密码。
|
||||||
|
/// 仅用于 WebView2 登录页自动填充,不参与后台 HttpClient 直登。
|
||||||
|
/// </summary>
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用自动填充账号密码。
|
||||||
|
/// 启用后,程序会在 WebView2 登录页尝试:
|
||||||
|
/// 1. 切换到密码登录
|
||||||
|
/// 2. 自动填入账号
|
||||||
|
/// 3. 自动填入密码
|
||||||
|
/// 4. 自动点击登录按钮
|
||||||
|
/// 若遇到滑块、短信、人机校验,仍需用户手动处理。
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableAutoFill { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1450,7 +1473,7 @@ public sealed class AlipayLoginForm : Form
|
|||||||
private string _detectedCtoken = string.Empty;
|
private string _detectedCtoken = string.Empty;
|
||||||
private bool _loginEventRaised;
|
private bool _loginEventRaised;
|
||||||
private bool _navigatedToBillPage;
|
private bool _navigatedToBillPage;
|
||||||
private bool _waitingSecurityVerify;
|
private bool _autoFillAttempted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当检测到登录成功并提取到 Cookie 后触发。
|
/// 当检测到登录成功并提取到 Cookie 后触发。
|
||||||
@ -1506,7 +1529,9 @@ public sealed class AlipayLoginForm : Form
|
|||||||
_webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
_webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
||||||
_webView.CoreWebView2.WebResourceRequested += CoreWebView2_WebResourceRequested;
|
_webView.CoreWebView2.WebResourceRequested += CoreWebView2_WebResourceRequested;
|
||||||
|
|
||||||
_statusLabel.Text = "请使用支付宝扫码登录。";
|
_statusLabel.Text = _options.EnableAutoFill
|
||||||
|
? "正在打开支付宝登录页,将自动尝试账号密码登录;若出现验证,请手动完成。"
|
||||||
|
: "请使用支付宝扫码登录。";
|
||||||
_webView.CoreWebView2.Navigate(_options.LoginUrl);
|
_webView.CoreWebView2.Navigate(_options.LoginUrl);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -1581,7 +1606,6 @@ public sealed class AlipayLoginForm : Form
|
|||||||
|
|
||||||
if (IsSecurityVerifyUrl(currentUrl))
|
if (IsSecurityVerifyUrl(currentUrl))
|
||||||
{
|
{
|
||||||
_waitingSecurityVerify = true;
|
|
||||||
_statusLabel.Text = "支付宝触发安全校验,请在当前页面完成验证,完成后程序会自动继续。";
|
_statusLabel.Text = "支付宝触发安全校验,请在当前页面完成验证,完成后程序会自动继续。";
|
||||||
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
||||||
{
|
{
|
||||||
@ -1593,7 +1617,6 @@ public sealed class AlipayLoginForm : Form
|
|||||||
|
|
||||||
if (IsBillReadyUrl(currentUrl))
|
if (IsBillReadyUrl(currentUrl))
|
||||||
{
|
{
|
||||||
_waitingSecurityVerify = false;
|
|
||||||
await TryExtractCookiesAndRaiseAsync(currentUrl);
|
await TryExtractCookiesAndRaiseAsync(currentUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1641,12 +1664,7 @@ public sealed class AlipayLoginForm : Form
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
var result = await core.ExecuteScriptAsync(script);
|
var result = await core.ExecuteScriptAsync(script);
|
||||||
if (string.IsNullOrWhiteSpace(result))
|
var json = ExtractWebViewJsonPayload(result);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = JsonSerializer.Deserialize<string>(result);
|
|
||||||
if (string.IsNullOrWhiteSpace(json))
|
if (string.IsNullOrWhiteSpace(json))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -1661,14 +1679,14 @@ public sealed class AlipayLoginForm : Form
|
|||||||
|
|
||||||
if (IsSecurityVerifyUrl(currentUrl))
|
if (IsSecurityVerifyUrl(currentUrl))
|
||||||
{
|
{
|
||||||
_waitingSecurityVerify = true;
|
|
||||||
_statusLabel.Text = "支付宝触发安全校验,请在当前页面完成验证,完成后程序会自动继续。";
|
_statusLabel.Text = "支付宝触发安全校验,请在当前页面完成验证,完成后程序会自动继续。";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await TryAutoFillLoginAsync(currentUrl);
|
||||||
|
|
||||||
if (IsBillReadyUrl(currentUrl))
|
if (IsBillReadyUrl(currentUrl))
|
||||||
{
|
{
|
||||||
_waitingSecurityVerify = false;
|
|
||||||
await TryExtractCookiesAndRaiseAsync(currentUrl);
|
await TryExtractCookiesAndRaiseAsync(currentUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1692,6 +1710,415 @@ public sealed class AlipayLoginForm : Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在 WebView2 登录页尝试自动填充账号密码并点击登录。
|
||||||
|
/// 注意:
|
||||||
|
/// 1. 支付宝登录页 DOM 可能随版本变化,本方法采用“多选择器 + 页面文本”策略尽量兼容;
|
||||||
|
/// 2. 若页面出现滑块、短信、人机验证,本方法不会强行绕过,只会提示用户手动接管;
|
||||||
|
/// 3. 为避免重复点击,本方法整个登录流程只自动尝试一次。
|
||||||
|
/// </summary>
|
||||||
|
private async Task TryAutoFillLoginAsync(string currentUrl)
|
||||||
|
{
|
||||||
|
if (_autoFillAttempted ||
|
||||||
|
!_options.EnableAutoFill ||
|
||||||
|
_webView.CoreWebView2 == null ||
|
||||||
|
IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_options.Account) || string.IsNullOrWhiteSpace(_options.Password))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsBillReadyUrl(currentUrl) || IsSecurityVerifyUrl(currentUrl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var submitted = false;
|
||||||
|
var partialFilled = false;
|
||||||
|
var switchedToPasswordLogin = false;
|
||||||
|
|
||||||
|
for (var attempt = 1; attempt <= 10; attempt++)
|
||||||
|
{
|
||||||
|
if (_webView.CoreWebView2 == null || IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var script = $$"""
|
||||||
|
(async () => {
|
||||||
|
const result = {
|
||||||
|
switched: false,
|
||||||
|
accountFilled: false,
|
||||||
|
passwordFilled: false,
|
||||||
|
clicked: false,
|
||||||
|
hasAccountInput: false,
|
||||||
|
hasPasswordInput: false,
|
||||||
|
pageText: document.body ? document.body.innerText : "",
|
||||||
|
title: document.title || "",
|
||||||
|
href: location.href
|
||||||
|
};
|
||||||
|
|
||||||
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
function setNativeValue(element, value) {
|
||||||
|
if (!element) return false;
|
||||||
|
|
||||||
|
const prototype = Object.getPrototypeOf(element);
|
||||||
|
const descriptor = prototype
|
||||||
|
? Object.getOwnPropertyDescriptor(prototype, 'value')
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (descriptor && descriptor.set) {
|
||||||
|
descriptor.set.call(element, value);
|
||||||
|
} else {
|
||||||
|
element.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
element.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVisible(element) {
|
||||||
|
if (!element) return false;
|
||||||
|
const style = window.getComputedStyle(element);
|
||||||
|
return style.display !== 'none' &&
|
||||||
|
style.visibility !== 'hidden' &&
|
||||||
|
style.opacity !== '0' &&
|
||||||
|
!!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeText(element) {
|
||||||
|
return (element?.innerText || element?.textContent || element?.value || '')
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerMouseClick(element) {
|
||||||
|
if (!element || !isVisible(element)) return false;
|
||||||
|
|
||||||
|
try { element.scrollIntoView({ block: 'center', inline: 'center' }); } catch {}
|
||||||
|
try { element.focus(); } catch {}
|
||||||
|
|
||||||
|
for (const eventName of ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click']) {
|
||||||
|
try {
|
||||||
|
element.dispatchEvent(new MouseEvent(eventName, {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
composed: true,
|
||||||
|
view: window
|
||||||
|
}));
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try { element.click(); } catch {}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickElementOrAncestors(element, maxDepth = 4) {
|
||||||
|
let current = element;
|
||||||
|
let depth = 0;
|
||||||
|
|
||||||
|
while (current && depth <= maxDepth) {
|
||||||
|
if (triggerMouseClick(current)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parentElement;
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPasswordLoginTrigger() {
|
||||||
|
const keywords = [
|
||||||
|
'账号密码',
|
||||||
|
'账号密码登录',
|
||||||
|
'账密登录',
|
||||||
|
'密码登录',
|
||||||
|
'切换到密码登录',
|
||||||
|
'手机密码登录'
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectors = [
|
||||||
|
'[data-status="show_login"]',
|
||||||
|
'[data-role*="password"]',
|
||||||
|
'[data-role*="login"]',
|
||||||
|
'[class*="password"]',
|
||||||
|
'[class*="login"]',
|
||||||
|
'[class*="tab"]',
|
||||||
|
'[class*="switch"]',
|
||||||
|
'[id*="password"]',
|
||||||
|
'[id*="login"]',
|
||||||
|
'[id*="tab"]',
|
||||||
|
'a',
|
||||||
|
'button',
|
||||||
|
'div',
|
||||||
|
'span',
|
||||||
|
'li'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const selector of selectors) {
|
||||||
|
const elements = [...document.querySelectorAll(selector)];
|
||||||
|
const found = elements.find(el => {
|
||||||
|
if (!isVisible(el)) return false;
|
||||||
|
const text = normalizeText(el);
|
||||||
|
return keywords.some(keyword => text.includes(keyword));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFirstVisible(selectors) {
|
||||||
|
for (const selector of selectors) {
|
||||||
|
const list = [...document.querySelectorAll(selector)];
|
||||||
|
const found = list.find(isVisible);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLoginButton() {
|
||||||
|
const selectors = [
|
||||||
|
'button[type="submit"]',
|
||||||
|
'input[type="submit"]',
|
||||||
|
'button',
|
||||||
|
'input[type="button"]',
|
||||||
|
'a',
|
||||||
|
'div'
|
||||||
|
];
|
||||||
|
|
||||||
|
const keywords = ['登录', '立即登录', '确认登录', '下一步'];
|
||||||
|
|
||||||
|
for (const selector of selectors) {
|
||||||
|
const elements = [...document.querySelectorAll(selector)];
|
||||||
|
const found = elements.find(el => {
|
||||||
|
if (!isVisible(el)) return false;
|
||||||
|
|
||||||
|
const text = normalizeText(el);
|
||||||
|
if (!keywords.some(keyword => text.includes(keyword))) return false;
|
||||||
|
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
return rect.width >= 40 && rect.height >= 24;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordTrigger = findPasswordLoginTrigger();
|
||||||
|
if (passwordTrigger) {
|
||||||
|
result.switched = clickElementOrAncestors(passwordTrigger, 5);
|
||||||
|
await sleep(350);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountSelectors = [
|
||||||
|
'input[name="loginId"]',
|
||||||
|
'input[name="account"]',
|
||||||
|
'input[name="username"]',
|
||||||
|
'input[name="userName"]',
|
||||||
|
'input[name="user-id"]',
|
||||||
|
'input[id*="loginId"]',
|
||||||
|
'input[id*="account"]',
|
||||||
|
'input[id*="user"]',
|
||||||
|
'input[placeholder*="账号"]',
|
||||||
|
'input[placeholder*="手机号"]',
|
||||||
|
'input[placeholder*="邮箱"]',
|
||||||
|
'input[autocomplete="username"]',
|
||||||
|
'input[type="text"]',
|
||||||
|
'input:not([type])'
|
||||||
|
];
|
||||||
|
|
||||||
|
const passwordSelectors = [
|
||||||
|
'input[name="password"]',
|
||||||
|
'input[id*="password"]',
|
||||||
|
'input[placeholder*="密码"]',
|
||||||
|
'input[type="password"]',
|
||||||
|
'input[autocomplete="current-password"]'
|
||||||
|
];
|
||||||
|
|
||||||
|
const accountInput = findFirstVisible(accountSelectors);
|
||||||
|
const passwordInput = findFirstVisible(passwordSelectors);
|
||||||
|
|
||||||
|
result.hasAccountInput = !!accountInput;
|
||||||
|
result.hasPasswordInput = !!passwordInput;
|
||||||
|
|
||||||
|
if (accountInput) {
|
||||||
|
accountInput.focus();
|
||||||
|
result.accountFilled = setNativeValue(accountInput, {{JsonSerializer.Serialize(_options.Account)}});
|
||||||
|
try { accountInput.blur(); } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordInput) {
|
||||||
|
passwordInput.focus();
|
||||||
|
result.passwordFilled = setNativeValue(passwordInput, {{JsonSerializer.Serialize(_options.Password)}});
|
||||||
|
try { passwordInput.blur(); } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.accountFilled && result.passwordFilled) {
|
||||||
|
await sleep(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
let loginButton = findLoginButton();
|
||||||
|
|
||||||
|
if (loginButton && result.accountFilled && result.passwordFilled) {
|
||||||
|
result.clicked = clickElementOrAncestors(loginButton, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.clicked && passwordInput && result.accountFilled && result.passwordFilled) {
|
||||||
|
try {
|
||||||
|
passwordInput.focus();
|
||||||
|
passwordInput.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter', code: 'Enter' }));
|
||||||
|
passwordInput.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, key: 'Enter', code: 'Enter' }));
|
||||||
|
passwordInput.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, key: 'Enter', code: 'Enter' }));
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.clicked && result.accountFilled && result.passwordFilled) {
|
||||||
|
loginButton = findLoginButton();
|
||||||
|
const form = passwordInput?.form || accountInput?.form || loginButton?.closest?.('form') || null;
|
||||||
|
if (form) {
|
||||||
|
try {
|
||||||
|
if (typeof form.requestSubmit === 'function') {
|
||||||
|
form.requestSubmit();
|
||||||
|
} else {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
result.clicked = true;
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.href = location.href;
|
||||||
|
result.pageText = document.body ? document.body.innerText : "";
|
||||||
|
return JSON.stringify(result);
|
||||||
|
})();
|
||||||
|
""";
|
||||||
|
|
||||||
|
var raw = await _webView.CoreWebView2.ExecuteScriptAsync(script);
|
||||||
|
var json = ExtractWebViewJsonPayload(raw);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(json))
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
var switched = root.TryGetProperty("switched", out var switchedProp) && switchedProp.GetBoolean();
|
||||||
|
var accountFilled = root.TryGetProperty("accountFilled", out var accountProp) && accountProp.GetBoolean();
|
||||||
|
var passwordFilled = root.TryGetProperty("passwordFilled", out var passwordProp) && passwordProp.GetBoolean();
|
||||||
|
var clicked = root.TryGetProperty("clicked", out var clickedProp) && clickedProp.GetBoolean();
|
||||||
|
var hasAccountInput = root.TryGetProperty("hasAccountInput", out var hasAccountProp) && hasAccountProp.GetBoolean();
|
||||||
|
var hasPasswordInput = root.TryGetProperty("hasPasswordInput", out var hasPasswordProp) && hasPasswordProp.GetBoolean();
|
||||||
|
|
||||||
|
switchedToPasswordLogin |= switched;
|
||||||
|
partialFilled |= accountFilled || passwordFilled;
|
||||||
|
|
||||||
|
if (clicked && accountFilled && passwordFilled)
|
||||||
|
{
|
||||||
|
submitted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((switched || hasAccountInput || hasPasswordInput) && attempt < 10)
|
||||||
|
{
|
||||||
|
_statusLabel.Text = $"已识别支付宝登录页,正在自动切换账密登录并填充信息(第 {attempt}/10 次尝试)……";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submitted)
|
||||||
|
{
|
||||||
|
await Task.Delay(1200);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(800);
|
||||||
|
}
|
||||||
|
|
||||||
|
_autoFillAttempted = true;
|
||||||
|
|
||||||
|
if (submitted)
|
||||||
|
{
|
||||||
|
_statusLabel.Text = "已自动点击账密登录、填充账号密码并提交,若出现验证请手动完成。";
|
||||||
|
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
||||||
|
{
|
||||||
|
StatusCode = "AutoFillSubmitted",
|
||||||
|
Message = "支付宝登录页已自动切换账密登录、填充账号密码并提交登录。"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (partialFilled || switchedToPasswordLogin)
|
||||||
|
{
|
||||||
|
_statusLabel.Text = "已尝试切换账密登录并部分填充,请确认页面后手动继续。";
|
||||||
|
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
||||||
|
{
|
||||||
|
StatusCode = "AutoFillPartial",
|
||||||
|
Message = "支付宝登录页已尝试切换账密登录并部分自动填充,请人工确认并继续。"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_statusLabel.Text = "当前页面未识别到账密登录表单,请手动切换并登录。";
|
||||||
|
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
||||||
|
{
|
||||||
|
StatusCode = "AutoFillSkipped",
|
||||||
|
Message = "当前支付宝页面未识别到密码登录表单,已切换为人工登录模式。"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke(this, new AlipayStatusChangedEventArgs
|
||||||
|
{
|
||||||
|
StatusCode = "Warn",
|
||||||
|
Message = $"自动填充支付宝登录信息失败,已切换为人工登录:{ex.Message}",
|
||||||
|
Exception = ex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ExtractWebViewJsonPayload(string? scriptResult)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(scriptResult))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(scriptResult);
|
||||||
|
return doc.RootElement.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => doc.RootElement.GetString() ?? string.Empty,
|
||||||
|
JsonValueKind.Object => doc.RootElement.GetRawText(),
|
||||||
|
JsonValueKind.Array => doc.RootElement.GetRawText(),
|
||||||
|
JsonValueKind.True => "true",
|
||||||
|
JsonValueKind.False => "false",
|
||||||
|
JsonValueKind.Number => doc.RootElement.ToString(),
|
||||||
|
_ => string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return scriptResult.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> TryNavigateToBillPageBeforeExtractAsync(string currentUrl)
|
private async Task<bool> TryNavigateToBillPageBeforeExtractAsync(string currentUrl)
|
||||||
{
|
{
|
||||||
if (_webView.CoreWebView2 == null || _navigatedToBillPage)
|
if (_webView.CoreWebView2 == null || _navigatedToBillPage)
|
||||||
|
|||||||
283
Form1.Designer.cs
generated
283
Form1.Designer.cs
generated
@ -40,6 +40,7 @@ namespace Vmianqian
|
|||||||
homeSummaryCard = new System.Windows.Forms.Panel();
|
homeSummaryCard = new System.Windows.Forms.Panel();
|
||||||
lblSummaryTitle = new AntdUI.Label();
|
lblSummaryTitle = new AntdUI.Label();
|
||||||
lblSummaryDesc = new AntdUI.Label();
|
lblSummaryDesc = new AntdUI.Label();
|
||||||
|
btnCheckUpdate = new AntdUI.Button();
|
||||||
homeConfigCard = new System.Windows.Forms.Panel();
|
homeConfigCard = new System.Windows.Forms.Panel();
|
||||||
btnSaveConfig = new AntdUI.Button();
|
btnSaveConfig = new AntdUI.Button();
|
||||||
btnHeartbeatCheck = new AntdUI.Button();
|
btnHeartbeatCheck = new AntdUI.Button();
|
||||||
@ -50,7 +51,11 @@ namespace Vmianqian
|
|||||||
lblApiKeyTitle = new AntdUI.Label();
|
lblApiKeyTitle = new AntdUI.Label();
|
||||||
txtApiKey = new Input();
|
txtApiKey = new Input();
|
||||||
homeMemberCard = new System.Windows.Forms.Panel();
|
homeMemberCard = new System.Windows.Forms.Panel();
|
||||||
lblMemberPlaceholder = new AntdUI.Label();
|
btnActivate = new AntdUI.Button();
|
||||||
|
txtActivationCode = new Input();
|
||||||
|
lblActivationCodeTitle = new AntdUI.Label();
|
||||||
|
btnCopyDeviceCode = new AntdUI.Button();
|
||||||
|
label3 = new AntdUI.Label();
|
||||||
homeLogCard = new System.Windows.Forms.Panel();
|
homeLogCard = new System.Windows.Forms.Panel();
|
||||||
btnClearLog = new AntdUI.Button();
|
btnClearLog = new AntdUI.Button();
|
||||||
txtLog = new TextBox();
|
txtLog = new TextBox();
|
||||||
@ -107,6 +112,14 @@ namespace Vmianqian
|
|||||||
txtNotifyEmail = new Input();
|
txtNotifyEmail = new Input();
|
||||||
lblEmailAuthCodeTitle = new AntdUI.Label();
|
lblEmailAuthCodeTitle = new AntdUI.Label();
|
||||||
txtEmailAuthCode = new Input();
|
txtEmailAuthCode = new Input();
|
||||||
|
settingsAlipayCard = new System.Windows.Forms.Panel();
|
||||||
|
btnAlipayAccountSave = new AntdUI.Button();
|
||||||
|
chkAlipayAutoFill = new Switch();
|
||||||
|
lblAlipayAutoFillTitle = new AntdUI.Label();
|
||||||
|
lblAlipayAccountTitle = new AntdUI.Label();
|
||||||
|
txtAlipayAccount = new Input();
|
||||||
|
lblAlipayPasswordTitle = new AntdUI.Label();
|
||||||
|
txtAlipayPassword = new Input();
|
||||||
titlebar = new PageHeader();
|
titlebar = new PageHeader();
|
||||||
lblTopNotice = new AntdUI.Label();
|
lblTopNotice = new AntdUI.Label();
|
||||||
lblAlipayStatusValue = new AntdUI.Label();
|
lblAlipayStatusValue = new AntdUI.Label();
|
||||||
@ -116,10 +129,10 @@ namespace Vmianqian
|
|||||||
buttonCollapse = new AntdUI.Button();
|
buttonCollapse = new AntdUI.Button();
|
||||||
menu = new AntdUI.Menu();
|
menu = new AntdUI.Menu();
|
||||||
contentHost = new System.Windows.Forms.Panel();
|
contentHost = new System.Windows.Forms.Panel();
|
||||||
pageAlipay = new System.Windows.Forms.Panel();
|
|
||||||
pageWechat = new System.Windows.Forms.Panel();
|
|
||||||
pageHome = new System.Windows.Forms.Panel();
|
pageHome = new System.Windows.Forms.Panel();
|
||||||
pageSettings = new System.Windows.Forms.Panel();
|
pageSettings = new System.Windows.Forms.Panel();
|
||||||
|
pageAlipay = new System.Windows.Forms.Panel();
|
||||||
|
pageWechat = new System.Windows.Forms.Panel();
|
||||||
homeSummaryCard.SuspendLayout();
|
homeSummaryCard.SuspendLayout();
|
||||||
homeConfigCard.SuspendLayout();
|
homeConfigCard.SuspendLayout();
|
||||||
homeMemberCard.SuspendLayout();
|
homeMemberCard.SuspendLayout();
|
||||||
@ -134,13 +147,14 @@ namespace Vmianqian
|
|||||||
alipayLogCard.SuspendLayout();
|
alipayLogCard.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)gridAlipayLogs).BeginInit();
|
((System.ComponentModel.ISupportInitialize)gridAlipayLogs).BeginInit();
|
||||||
settingsEmailCard.SuspendLayout();
|
settingsEmailCard.SuspendLayout();
|
||||||
|
settingsAlipayCard.SuspendLayout();
|
||||||
titlebar.SuspendLayout();
|
titlebar.SuspendLayout();
|
||||||
bottomBar.SuspendLayout();
|
bottomBar.SuspendLayout();
|
||||||
contentHost.SuspendLayout();
|
contentHost.SuspendLayout();
|
||||||
pageAlipay.SuspendLayout();
|
|
||||||
pageWechat.SuspendLayout();
|
|
||||||
pageHome.SuspendLayout();
|
pageHome.SuspendLayout();
|
||||||
pageSettings.SuspendLayout();
|
pageSettings.SuspendLayout();
|
||||||
|
pageAlipay.SuspendLayout();
|
||||||
|
pageWechat.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
// homeSummaryCard
|
// homeSummaryCard
|
||||||
@ -148,6 +162,7 @@ namespace Vmianqian
|
|||||||
homeSummaryCard.BackColor = Color.White;
|
homeSummaryCard.BackColor = Color.White;
|
||||||
homeSummaryCard.Controls.Add(lblSummaryTitle);
|
homeSummaryCard.Controls.Add(lblSummaryTitle);
|
||||||
homeSummaryCard.Controls.Add(lblSummaryDesc);
|
homeSummaryCard.Controls.Add(lblSummaryDesc);
|
||||||
|
homeSummaryCard.Controls.Add(btnCheckUpdate);
|
||||||
homeSummaryCard.Location = new Point(20, 23);
|
homeSummaryCard.Location = new Point(20, 23);
|
||||||
homeSummaryCard.Name = "homeSummaryCard";
|
homeSummaryCard.Name = "homeSummaryCard";
|
||||||
homeSummaryCard.Size = new Size(960, 100);
|
homeSummaryCard.Size = new Size(960, 100);
|
||||||
@ -171,7 +186,20 @@ namespace Vmianqian
|
|||||||
lblSummaryDesc.Name = "lblSummaryDesc";
|
lblSummaryDesc.Name = "lblSummaryDesc";
|
||||||
lblSummaryDesc.Size = new Size(899, 32);
|
lblSummaryDesc.Size = new Size(899, 32);
|
||||||
lblSummaryDesc.TabIndex = 1;
|
lblSummaryDesc.TabIndex = 1;
|
||||||
lblSummaryDesc.Text = "当前阶段先打通“PC端本地监听 -> V免签服务端回调 -> 心跳检测”链路,整体布局按 AntdUI Demo 风格复刻。";
|
lblSummaryDesc.Text = "这是v0.0.2版本的软件";
|
||||||
|
//
|
||||||
|
// btnCheckUpdate
|
||||||
|
//
|
||||||
|
btnCheckUpdate.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||||||
|
btnCheckUpdate.BorderWidth = 2F;
|
||||||
|
btnCheckUpdate.Ghost = true;
|
||||||
|
btnCheckUpdate.Location = new Point(844, 24);
|
||||||
|
btnCheckUpdate.Name = "btnCheckUpdate";
|
||||||
|
btnCheckUpdate.Size = new Size(92, 36);
|
||||||
|
btnCheckUpdate.TabIndex = 2;
|
||||||
|
btnCheckUpdate.Text = "检查更新";
|
||||||
|
btnCheckUpdate.Type = TTypeMini.Primary;
|
||||||
|
btnCheckUpdate.Click += btnCheckUpdate_Click;
|
||||||
//
|
//
|
||||||
// homeConfigCard
|
// homeConfigCard
|
||||||
//
|
//
|
||||||
@ -265,21 +293,64 @@ namespace Vmianqian
|
|||||||
// homeMemberCard
|
// homeMemberCard
|
||||||
//
|
//
|
||||||
homeMemberCard.BackColor = Color.White;
|
homeMemberCard.BackColor = Color.White;
|
||||||
homeMemberCard.Controls.Add(lblMemberPlaceholder);
|
homeMemberCard.Controls.Add(btnActivate);
|
||||||
|
homeMemberCard.Controls.Add(txtActivationCode);
|
||||||
|
homeMemberCard.Controls.Add(lblActivationCodeTitle);
|
||||||
|
homeMemberCard.Controls.Add(btnCopyDeviceCode);
|
||||||
|
homeMemberCard.Controls.Add(label3);
|
||||||
homeMemberCard.Location = new Point(510, 141);
|
homeMemberCard.Location = new Point(510, 141);
|
||||||
homeMemberCard.Name = "homeMemberCard";
|
homeMemberCard.Name = "homeMemberCard";
|
||||||
homeMemberCard.Size = new Size(470, 280);
|
homeMemberCard.Size = new Size(470, 280);
|
||||||
homeMemberCard.TabIndex = 1;
|
homeMemberCard.TabIndex = 1;
|
||||||
homeMemberCard.Tag = "home-member";
|
homeMemberCard.Tag = "home-member";
|
||||||
//
|
//
|
||||||
// lblMemberPlaceholder
|
// btnActivate
|
||||||
//
|
//
|
||||||
lblMemberPlaceholder.ForeColor = Color.DimGray;
|
btnActivate.Location = new Point(358, 64);
|
||||||
lblMemberPlaceholder.Location = new Point(24, 20);
|
btnActivate.Name = "btnActivate";
|
||||||
lblMemberPlaceholder.Name = "lblMemberPlaceholder";
|
btnActivate.Size = new Size(92, 40);
|
||||||
lblMemberPlaceholder.Size = new Size(429, 52);
|
btnActivate.TabIndex = 0;
|
||||||
lblMemberPlaceholder.TabIndex = 0;
|
btnActivate.Text = "激活";
|
||||||
lblMemberPlaceholder.Text = "预留区域:后续用于会员登录、设备绑定、解绑与状态展示。";
|
btnActivate.Type = TTypeMini.Primary;
|
||||||
|
btnActivate.Click += btnActivate_Click;
|
||||||
|
//
|
||||||
|
// txtActivationCode
|
||||||
|
//
|
||||||
|
txtActivationCode.Location = new Point(74, 64);
|
||||||
|
txtActivationCode.Name = "txtActivationCode";
|
||||||
|
txtActivationCode.PlaceholderText = "请输入激活码";
|
||||||
|
txtActivationCode.Size = new Size(276, 40);
|
||||||
|
txtActivationCode.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// lblActivationCodeTitle
|
||||||
|
//
|
||||||
|
lblActivationCodeTitle.AutoSizeMode = TAutoSize.Auto;
|
||||||
|
lblActivationCodeTitle.Location = new Point(21, 76);
|
||||||
|
lblActivationCodeTitle.Name = "lblActivationCodeTitle";
|
||||||
|
lblActivationCodeTitle.Size = new Size(36, 16);
|
||||||
|
lblActivationCodeTitle.TabIndex = 2;
|
||||||
|
lblActivationCodeTitle.Text = "激活码";
|
||||||
|
//
|
||||||
|
// btnCopyDeviceCode
|
||||||
|
//
|
||||||
|
btnCopyDeviceCode.BorderWidth = 2F;
|
||||||
|
btnCopyDeviceCode.Ghost = true;
|
||||||
|
btnCopyDeviceCode.Location = new Point(358, 18);
|
||||||
|
btnCopyDeviceCode.Name = "btnCopyDeviceCode";
|
||||||
|
btnCopyDeviceCode.Size = new Size(92, 36);
|
||||||
|
btnCopyDeviceCode.TabIndex = 3;
|
||||||
|
btnCopyDeviceCode.Text = "复制";
|
||||||
|
btnCopyDeviceCode.Type = TTypeMini.Primary;
|
||||||
|
btnCopyDeviceCode.Click += btnCopyDeviceCode_Click;
|
||||||
|
//
|
||||||
|
// label3
|
||||||
|
//
|
||||||
|
label3.ForeColor = Color.FromArgb(38, 38, 38);
|
||||||
|
label3.Location = new Point(21, 20);
|
||||||
|
label3.Name = "label3";
|
||||||
|
label3.Size = new Size(329, 36);
|
||||||
|
label3.TabIndex = 4;
|
||||||
|
label3.Text = "设备号:读取中...";
|
||||||
//
|
//
|
||||||
// homeLogCard
|
// homeLogCard
|
||||||
//
|
//
|
||||||
@ -872,6 +943,83 @@ namespace Vmianqian
|
|||||||
txtEmailAuthCode.Size = new Size(250, 55);
|
txtEmailAuthCode.Size = new Size(250, 55);
|
||||||
txtEmailAuthCode.TabIndex = 11;
|
txtEmailAuthCode.TabIndex = 11;
|
||||||
//
|
//
|
||||||
|
// settingsAlipayCard
|
||||||
|
//
|
||||||
|
settingsAlipayCard.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
|
||||||
|
settingsAlipayCard.BackColor = Color.White;
|
||||||
|
settingsAlipayCard.Controls.Add(btnAlipayAccountSave);
|
||||||
|
settingsAlipayCard.Controls.Add(chkAlipayAutoFill);
|
||||||
|
settingsAlipayCard.Controls.Add(lblAlipayAutoFillTitle);
|
||||||
|
settingsAlipayCard.Controls.Add(lblAlipayAccountTitle);
|
||||||
|
settingsAlipayCard.Controls.Add(txtAlipayAccount);
|
||||||
|
settingsAlipayCard.Controls.Add(lblAlipayPasswordTitle);
|
||||||
|
settingsAlipayCard.Controls.Add(txtAlipayPassword);
|
||||||
|
settingsAlipayCard.Location = new Point(624, 23);
|
||||||
|
settingsAlipayCard.Name = "settingsAlipayCard";
|
||||||
|
settingsAlipayCard.Size = new Size(361, 395);
|
||||||
|
settingsAlipayCard.TabIndex = 0;
|
||||||
|
settingsAlipayCard.Tag = "settings-listen";
|
||||||
|
//
|
||||||
|
// btnAlipayAccountSave
|
||||||
|
//
|
||||||
|
btnAlipayAccountSave.Location = new Point(24, 325);
|
||||||
|
btnAlipayAccountSave.Name = "btnAlipayAccountSave";
|
||||||
|
btnAlipayAccountSave.Size = new Size(120, 42);
|
||||||
|
btnAlipayAccountSave.TabIndex = 0;
|
||||||
|
btnAlipayAccountSave.Text = "保存支付宝配置";
|
||||||
|
btnAlipayAccountSave.Type = TTypeMini.Primary;
|
||||||
|
btnAlipayAccountSave.Click += btnAlipayAccountSave_Click;
|
||||||
|
//
|
||||||
|
// chkAlipayAutoFill
|
||||||
|
//
|
||||||
|
chkAlipayAutoFill.Location = new Point(24, 274);
|
||||||
|
chkAlipayAutoFill.Name = "chkAlipayAutoFill";
|
||||||
|
chkAlipayAutoFill.Size = new Size(62, 32);
|
||||||
|
chkAlipayAutoFill.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// lblAlipayAutoFillTitle
|
||||||
|
//
|
||||||
|
lblAlipayAutoFillTitle.AutoSizeMode = TAutoSize.Auto;
|
||||||
|
lblAlipayAutoFillTitle.Location = new Point(95, 280);
|
||||||
|
lblAlipayAutoFillTitle.Name = "lblAlipayAutoFillTitle";
|
||||||
|
lblAlipayAutoFillTitle.Size = new Size(144, 16);
|
||||||
|
lblAlipayAutoFillTitle.TabIndex = 2;
|
||||||
|
lblAlipayAutoFillTitle.Text = "启用账号密码自动填充登录";
|
||||||
|
//
|
||||||
|
// lblAlipayAccountTitle
|
||||||
|
//
|
||||||
|
lblAlipayAccountTitle.AutoSizeMode = TAutoSize.Auto;
|
||||||
|
lblAlipayAccountTitle.Location = new Point(24, 21);
|
||||||
|
lblAlipayAccountTitle.Name = "lblAlipayAccountTitle";
|
||||||
|
lblAlipayAccountTitle.Size = new Size(60, 16);
|
||||||
|
lblAlipayAccountTitle.TabIndex = 3;
|
||||||
|
lblAlipayAccountTitle.Text = "支付宝账号";
|
||||||
|
//
|
||||||
|
// txtAlipayAccount
|
||||||
|
//
|
||||||
|
txtAlipayAccount.Location = new Point(24, 49);
|
||||||
|
txtAlipayAccount.Name = "txtAlipayAccount";
|
||||||
|
txtAlipayAccount.PlaceholderText = "请输入支付宝登录账号";
|
||||||
|
txtAlipayAccount.Size = new Size(250, 55);
|
||||||
|
txtAlipayAccount.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// lblAlipayPasswordTitle
|
||||||
|
//
|
||||||
|
lblAlipayPasswordTitle.AutoSizeMode = TAutoSize.Auto;
|
||||||
|
lblAlipayPasswordTitle.Location = new Point(24, 116);
|
||||||
|
lblAlipayPasswordTitle.Name = "lblAlipayPasswordTitle";
|
||||||
|
lblAlipayPasswordTitle.Size = new Size(60, 16);
|
||||||
|
lblAlipayPasswordTitle.TabIndex = 5;
|
||||||
|
lblAlipayPasswordTitle.Text = "支付宝密码";
|
||||||
|
//
|
||||||
|
// txtAlipayPassword
|
||||||
|
//
|
||||||
|
txtAlipayPassword.Location = new Point(24, 144);
|
||||||
|
txtAlipayPassword.Name = "txtAlipayPassword";
|
||||||
|
txtAlipayPassword.PlaceholderText = "请输入支付宝登录密码";
|
||||||
|
txtAlipayPassword.Size = new Size(250, 55);
|
||||||
|
txtAlipayPassword.TabIndex = 6;
|
||||||
|
//
|
||||||
// titlebar
|
// titlebar
|
||||||
//
|
//
|
||||||
titlebar.Controls.Add(lblTopNotice);
|
titlebar.Controls.Add(lblTopNotice);
|
||||||
@ -879,6 +1027,7 @@ namespace Vmianqian
|
|||||||
titlebar.Controls.Add(lblWechatStatusValue);
|
titlebar.Controls.Add(lblWechatStatusValue);
|
||||||
titlebar.DividerShow = true;
|
titlebar.DividerShow = true;
|
||||||
titlebar.Dock = DockStyle.Top;
|
titlebar.Dock = DockStyle.Top;
|
||||||
|
titlebar.Icon = Properties.Resources.logo;
|
||||||
titlebar.Location = new Point(0, 0);
|
titlebar.Location = new Point(0, 0);
|
||||||
titlebar.Name = "titlebar";
|
titlebar.Name = "titlebar";
|
||||||
titlebar.ShowButton = true;
|
titlebar.ShowButton = true;
|
||||||
@ -977,16 +1126,44 @@ namespace Vmianqian
|
|||||||
// contentHost
|
// contentHost
|
||||||
//
|
//
|
||||||
contentHost.BackColor = Color.White;
|
contentHost.BackColor = Color.White;
|
||||||
contentHost.Controls.Add(pageAlipay);
|
|
||||||
contentHost.Controls.Add(pageWechat);
|
|
||||||
contentHost.Controls.Add(pageHome);
|
contentHost.Controls.Add(pageHome);
|
||||||
contentHost.Controls.Add(pageSettings);
|
contentHost.Controls.Add(pageSettings);
|
||||||
|
contentHost.Controls.Add(pageAlipay);
|
||||||
|
contentHost.Controls.Add(pageWechat);
|
||||||
contentHost.Dock = DockStyle.Fill;
|
contentHost.Dock = DockStyle.Fill;
|
||||||
contentHost.Location = new Point(192, 44);
|
contentHost.Location = new Point(192, 44);
|
||||||
contentHost.Name = "contentHost";
|
contentHost.Name = "contentHost";
|
||||||
contentHost.Size = new Size(1008, 916);
|
contentHost.Size = new Size(1008, 916);
|
||||||
contentHost.TabIndex = 0;
|
contentHost.TabIndex = 0;
|
||||||
//
|
//
|
||||||
|
// pageHome
|
||||||
|
//
|
||||||
|
pageHome.AutoScroll = true;
|
||||||
|
pageHome.BackColor = Color.FromArgb(245, 247, 250);
|
||||||
|
pageHome.Controls.Add(homeLogCard);
|
||||||
|
pageHome.Controls.Add(homeSummaryCard);
|
||||||
|
pageHome.Controls.Add(homeMemberCard);
|
||||||
|
pageHome.Controls.Add(homeConfigCard);
|
||||||
|
pageHome.Dock = DockStyle.Fill;
|
||||||
|
pageHome.Location = new Point(0, 0);
|
||||||
|
pageHome.Name = "pageHome";
|
||||||
|
pageHome.Padding = new Padding(20);
|
||||||
|
pageHome.Size = new Size(1008, 916);
|
||||||
|
pageHome.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// pageSettings
|
||||||
|
//
|
||||||
|
pageSettings.AutoScroll = true;
|
||||||
|
pageSettings.BackColor = Color.FromArgb(245, 247, 250);
|
||||||
|
pageSettings.Controls.Add(settingsAlipayCard);
|
||||||
|
pageSettings.Controls.Add(settingsEmailCard);
|
||||||
|
pageSettings.Dock = DockStyle.Fill;
|
||||||
|
pageSettings.Location = new Point(0, 0);
|
||||||
|
pageSettings.Name = "pageSettings";
|
||||||
|
pageSettings.Padding = new Padding(20);
|
||||||
|
pageSettings.Size = new Size(1008, 916);
|
||||||
|
pageSettings.TabIndex = 0;
|
||||||
|
//
|
||||||
// pageAlipay
|
// pageAlipay
|
||||||
//
|
//
|
||||||
pageAlipay.AutoScroll = true;
|
pageAlipay.AutoScroll = true;
|
||||||
@ -1012,33 +1189,6 @@ namespace Vmianqian
|
|||||||
pageWechat.Size = new Size(1008, 916);
|
pageWechat.Size = new Size(1008, 916);
|
||||||
pageWechat.TabIndex = 2;
|
pageWechat.TabIndex = 2;
|
||||||
//
|
//
|
||||||
// pageHome
|
|
||||||
//
|
|
||||||
pageHome.AutoScroll = true;
|
|
||||||
pageHome.BackColor = Color.FromArgb(245, 247, 250);
|
|
||||||
pageHome.Controls.Add(homeLogCard);
|
|
||||||
pageHome.Controls.Add(homeSummaryCard);
|
|
||||||
pageHome.Controls.Add(homeMemberCard);
|
|
||||||
pageHome.Controls.Add(homeConfigCard);
|
|
||||||
pageHome.Dock = DockStyle.Fill;
|
|
||||||
pageHome.Location = new Point(0, 0);
|
|
||||||
pageHome.Name = "pageHome";
|
|
||||||
pageHome.Padding = new Padding(20);
|
|
||||||
pageHome.Size = new Size(1008, 916);
|
|
||||||
pageHome.TabIndex = 3;
|
|
||||||
//
|
|
||||||
// pageSettings
|
|
||||||
//
|
|
||||||
pageSettings.AutoScroll = true;
|
|
||||||
pageSettings.BackColor = Color.FromArgb(245, 247, 250);
|
|
||||||
pageSettings.Controls.Add(settingsEmailCard);
|
|
||||||
pageSettings.Dock = DockStyle.Fill;
|
|
||||||
pageSettings.Location = new Point(0, 0);
|
|
||||||
pageSettings.Name = "pageSettings";
|
|
||||||
pageSettings.Padding = new Padding(20);
|
|
||||||
pageSettings.Size = new Size(1008, 916);
|
|
||||||
pageSettings.TabIndex = 0;
|
|
||||||
//
|
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(7F, 17F);
|
AutoScaleDimensions = new SizeF(7F, 17F);
|
||||||
@ -1061,6 +1211,7 @@ namespace Vmianqian
|
|||||||
homeConfigCard.ResumeLayout(false);
|
homeConfigCard.ResumeLayout(false);
|
||||||
homeConfigCard.PerformLayout();
|
homeConfigCard.PerformLayout();
|
||||||
homeMemberCard.ResumeLayout(false);
|
homeMemberCard.ResumeLayout(false);
|
||||||
|
homeMemberCard.PerformLayout();
|
||||||
homeLogCard.ResumeLayout(false);
|
homeLogCard.ResumeLayout(false);
|
||||||
homeLogCard.PerformLayout();
|
homeLogCard.PerformLayout();
|
||||||
wechatHookCard.ResumeLayout(false);
|
wechatHookCard.ResumeLayout(false);
|
||||||
@ -1076,17 +1227,20 @@ namespace Vmianqian
|
|||||||
((System.ComponentModel.ISupportInitialize)gridAlipayLogs).EndInit();
|
((System.ComponentModel.ISupportInitialize)gridAlipayLogs).EndInit();
|
||||||
settingsEmailCard.ResumeLayout(false);
|
settingsEmailCard.ResumeLayout(false);
|
||||||
settingsEmailCard.PerformLayout();
|
settingsEmailCard.PerformLayout();
|
||||||
|
settingsAlipayCard.ResumeLayout(false);
|
||||||
|
settingsAlipayCard.PerformLayout();
|
||||||
titlebar.ResumeLayout(false);
|
titlebar.ResumeLayout(false);
|
||||||
bottomBar.ResumeLayout(false);
|
bottomBar.ResumeLayout(false);
|
||||||
contentHost.ResumeLayout(false);
|
contentHost.ResumeLayout(false);
|
||||||
pageAlipay.ResumeLayout(false);
|
|
||||||
pageWechat.ResumeLayout(false);
|
|
||||||
pageHome.ResumeLayout(false);
|
pageHome.ResumeLayout(false);
|
||||||
pageSettings.ResumeLayout(false);
|
pageSettings.ResumeLayout(false);
|
||||||
|
pageAlipay.ResumeLayout(false);
|
||||||
|
pageWechat.ResumeLayout(false);
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WinPanel homeSummaryCard = null!;
|
private WinPanel homeSummaryCard = null!;
|
||||||
|
private AntdUI.Button btnCheckUpdate = null!;
|
||||||
private WinPanel homeConfigCard = null!;
|
private WinPanel homeConfigCard = null!;
|
||||||
private WinPanel homeMemberCard = null!;
|
private WinPanel homeMemberCard = null!;
|
||||||
private WinPanel homeLogCard = null!;
|
private WinPanel homeLogCard = null!;
|
||||||
@ -1103,20 +1257,33 @@ namespace Vmianqian
|
|||||||
private NumericUpDown numAlipayInterval = null!;
|
private NumericUpDown numAlipayInterval = null!;
|
||||||
private AntdUI.Label lblAlipayDesc = null!;
|
private AntdUI.Label lblAlipayDesc = null!;
|
||||||
private WinPanel alipayLogCard = null!;
|
private WinPanel alipayLogCard = null!;
|
||||||
|
private WinPanel settingsAlipayCard = null!;
|
||||||
|
private AntdUI.Button btnAlipayAccountSave = null!;
|
||||||
|
private Switch chkAlipayAutoFill = null!;
|
||||||
|
private AntdUI.Label lblAlipayAutoFillTitle = null!;
|
||||||
|
private AntdUI.Label lblAlipayAccountTitle = null!;
|
||||||
|
private Input txtAlipayAccount = null!;
|
||||||
|
private AntdUI.Label lblAlipayPasswordTitle = null!;
|
||||||
|
private Input txtAlipayPassword = null!;
|
||||||
private WinPanel settingsEmailCard = null!;
|
private WinPanel settingsEmailCard = null!;
|
||||||
private AntdUI.Label label1 = null!;
|
private AntdUI.Label label1 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
|
private AntdUI.Button btnActivate = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
|
private Input txtActivationCode = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
|
private AntdUI.Label lblActivationCodeTitle = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
|
private AntdUI.Button btnCopyDeviceCode = null!;
|
||||||
private DataGridViewTextBoxColumn 订单状态;
|
private AntdUI.Label label3 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn1 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn2 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn3 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn4 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
|
private DataGridViewTextBoxColumn 订单状态 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn5 = null!;
|
||||||
private DataGridViewTextBoxColumn 状态;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn6 = null!;
|
||||||
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn8 = null!;
|
||||||
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn9 = null!;
|
||||||
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn7 = null!;
|
||||||
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn10 = null!;
|
||||||
|
private DataGridViewTextBoxColumn 状态 = null!;
|
||||||
|
private DataGridViewTextBoxColumn dataGridViewTextBoxColumn11 = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
788
Form1.cs
788
Form1.cs
@ -5,8 +5,11 @@ using MimeKit;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Management;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -38,8 +41,13 @@ namespace Vmianqian
|
|||||||
private readonly string _pendingOrdersFilePath = Path.Combine(AppContext.BaseDirectory, "pending-orders.client.json");
|
private readonly string _pendingOrdersFilePath = Path.Combine(AppContext.BaseDirectory, "pending-orders.client.json");
|
||||||
private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true, PropertyNameCaseInsensitive = true };
|
private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true, PropertyNameCaseInsensitive = true };
|
||||||
private readonly HttpClient _httpClient = new();
|
private readonly HttpClient _httpClient = new();
|
||||||
|
private readonly object _pendingOrdersFileLock = new();
|
||||||
|
private bool _isUpdating;
|
||||||
private readonly System.Windows.Forms.Timer _runtimeTimer = new();
|
private readonly System.Windows.Forms.Timer _runtimeTimer = new();
|
||||||
private readonly System.Windows.Forms.Timer _heartbeatTimer = new();
|
private readonly System.Windows.Forms.Timer _heartbeatTimer = new();
|
||||||
|
private readonly string _deviceCode;
|
||||||
|
private const string UpgradeCheckApiBaseUrl = "https://api.yunzer.cn";
|
||||||
|
private const string UpgradeProductCode = "Vmianqian";
|
||||||
|
|
||||||
private DateTime _appStartTime = DateTime.Now;
|
private DateTime _appStartTime = DateTime.Now;
|
||||||
private HttpListener? _httpListener;
|
private HttpListener? _httpListener;
|
||||||
@ -63,6 +71,8 @@ namespace Vmianqian
|
|||||||
private bool _heartbeatRequestInProgress;
|
private bool _heartbeatRequestInProgress;
|
||||||
private int _wechatProtocolAuthFailCount;
|
private int _wechatProtocolAuthFailCount;
|
||||||
private Vmianqian.AlipayMonitor? _alipayMonitor;
|
private Vmianqian.AlipayMonitor? _alipayMonitor;
|
||||||
|
private Vmianqian.AlipayLoginForm? _alipayLoginForm;
|
||||||
|
private bool _alipayAutoRefreshingCookie;
|
||||||
private AntdUI.Label? _lblWechatSidHint;
|
private AntdUI.Label? _lblWechatSidHint;
|
||||||
|
|
||||||
private AntdUI.PageHeader titlebar = null!;
|
private AntdUI.PageHeader titlebar = null!;
|
||||||
@ -103,7 +113,6 @@ namespace Vmianqian
|
|||||||
private AntdUI.Label lblWechatSidTitle = null!;
|
private AntdUI.Label lblWechatSidTitle = null!;
|
||||||
private AntdUI.Label lblWechatFrequencyTitle = null!;
|
private AntdUI.Label lblWechatFrequencyTitle = null!;
|
||||||
private AntdUI.Label lblHeartbeatDesc = null!;
|
private AntdUI.Label lblHeartbeatDesc = null!;
|
||||||
private AntdUI.Label lblMemberPlaceholder = null!;
|
|
||||||
private AntdUI.Switch chkHeartbeatEnabled = null!;
|
private AntdUI.Switch chkHeartbeatEnabled = null!;
|
||||||
private AntdUI.Button btnHeartbeatCheck = null!;
|
private AntdUI.Button btnHeartbeatCheck = null!;
|
||||||
private AntdUI.Button btnClearLog = null!;
|
private AntdUI.Button btnClearLog = null!;
|
||||||
@ -129,9 +138,12 @@ namespace Vmianqian
|
|||||||
|
|
||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
|
_deviceCode = BuildDeviceCode();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
UpdateTitlebarVersion();
|
||||||
InitializeDesignerLayout();
|
InitializeDesignerLayout();
|
||||||
InitializeWechatUi();
|
InitializeWechatUi();
|
||||||
|
UpdateDeviceCodeLabel();
|
||||||
// InitializeAlipayUi();
|
// InitializeAlipayUi();
|
||||||
ReloadMenuItems();
|
ReloadMenuItems();
|
||||||
SelectPage("home");
|
SelectPage("home");
|
||||||
@ -250,6 +262,12 @@ namespace Vmianqian
|
|||||||
{
|
{
|
||||||
summaryCard.SuspendLayout();
|
summaryCard.SuspendLayout();
|
||||||
lblSummaryTitle.Location = new Point(28, 20);
|
lblSummaryTitle.Location = new Point(28, 20);
|
||||||
|
if (btnCheckUpdate != null)
|
||||||
|
{
|
||||||
|
btnCheckUpdate.Size = new Size(92, 36);
|
||||||
|
btnCheckUpdate.Location = new Point(Math.Max(28, summaryCard.ClientSize.Width - btnCheckUpdate.Width - 24), 20);
|
||||||
|
btnCheckUpdate.BringToFront();
|
||||||
|
}
|
||||||
var summaryDescTop = lblSummaryTitle.Bottom + 8;
|
var summaryDescTop = lblSummaryTitle.Bottom + 8;
|
||||||
var summaryDescWidth = Math.Max(220, pageAvailableWidth - 56);
|
var summaryDescWidth = Math.Max(220, pageAvailableWidth - 56);
|
||||||
var summaryDescPreferred = TextRenderer.MeasureText(
|
var summaryDescPreferred = TextRenderer.MeasureText(
|
||||||
@ -278,7 +296,7 @@ namespace Vmianqian
|
|||||||
var leftWidth = rowWidth / 2;
|
var leftWidth = rowWidth / 2;
|
||||||
var rightWidth = rowWidth - leftWidth;
|
var rightWidth = rowWidth - leftWidth;
|
||||||
var configHeight = 390;
|
var configHeight = 390;
|
||||||
var memberHeight = 128;
|
var memberHeight = 170;
|
||||||
|
|
||||||
configCard.Bounds = new Rectangle(margin, top, leftWidth, configHeight);
|
configCard.Bounds = new Rectangle(margin, top, leftWidth, configHeight);
|
||||||
memberCard.Bounds = new Rectangle(margin + leftWidth + gap, top, rightWidth, memberHeight);
|
memberCard.Bounds = new Rectangle(margin + leftWidth + gap, top, rightWidth, memberHeight);
|
||||||
@ -305,7 +323,24 @@ namespace Vmianqian
|
|||||||
const int heartbeatTop = 310;
|
const int heartbeatTop = 310;
|
||||||
chkHeartbeatEnabled.Location = new Point(contentLeft, heartbeatTop - 4);
|
chkHeartbeatEnabled.Location = new Point(contentLeft, heartbeatTop - 4);
|
||||||
lblHeartbeatDesc.Location = new Point(chkHeartbeatEnabled.Right + 4, heartbeatTop + 1);
|
lblHeartbeatDesc.Location = new Point(chkHeartbeatEnabled.Right + 4, heartbeatTop + 1);
|
||||||
lblMemberPlaceholder.Size = new Size(Math.Max(220, memberCard.ClientSize.Width - 48), 52);
|
|
||||||
|
const int memberLeft = 21;
|
||||||
|
const int memberRight = 20;
|
||||||
|
const int memberTop = 20;
|
||||||
|
const int memberButtonGap = 8;
|
||||||
|
var memberContentWidth = Math.Max(220, memberCard.ClientSize.Width - memberLeft - memberRight);
|
||||||
|
var memberActionWidth = 92;
|
||||||
|
var memberInputWidth = Math.Max(140, memberContentWidth - memberActionWidth - memberButtonGap);
|
||||||
|
|
||||||
|
label3.Location = new Point(memberLeft, memberTop);
|
||||||
|
label3.Size = new Size(memberInputWidth, 36);
|
||||||
|
btnCopyDeviceCode.Location = new Point(memberCard.ClientSize.Width - memberRight - memberActionWidth, 18);
|
||||||
|
btnCopyDeviceCode.Size = new Size(memberActionWidth, 36);
|
||||||
|
lblActivationCodeTitle.Location = new Point(memberLeft, 62);
|
||||||
|
txtActivationCode.Location = new Point(memberLeft, 84);
|
||||||
|
txtActivationCode.Size = new Size(memberInputWidth, 40);
|
||||||
|
btnActivate.Location = new Point(memberCard.ClientSize.Width - memberRight - memberActionWidth, 84);
|
||||||
|
btnActivate.Size = new Size(memberActionWidth, 40);
|
||||||
|
|
||||||
var rowBottom = Math.Max(configCard.Bottom, memberCard.Bottom);
|
var rowBottom = Math.Max(configCard.Bottom, memberCard.Bottom);
|
||||||
var logCardTop = rowBottom + margin;
|
var logCardTop = rowBottom + margin;
|
||||||
@ -607,6 +642,7 @@ namespace Vmianqian
|
|||||||
ApplyHeartbeatSetting();
|
ApplyHeartbeatSetting();
|
||||||
|
|
||||||
Log("程序已启动。");
|
Log("程序已启动。");
|
||||||
|
_ = CheckForUpdatesAsync(silentWhenUpToDate: true, showNoUpdateMessage: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Form1_FormClosing(object? sender, FormClosingEventArgs e)
|
private void Form1_FormClosing(object? sender, FormClosingEventArgs e)
|
||||||
@ -618,6 +654,20 @@ namespace Vmianqian
|
|||||||
StopWechatSidCapture();
|
StopWechatSidCapture();
|
||||||
_alipayMonitor?.Stop();
|
_alipayMonitor?.Stop();
|
||||||
_alipayMonitor?.Dispose();
|
_alipayMonitor?.Dispose();
|
||||||
|
if (_alipayLoginForm != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_alipayLoginForm.Close();
|
||||||
|
_alipayLoginForm.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_alipayLoginForm = null;
|
||||||
|
}
|
||||||
|
|
||||||
_runtimeTimer.Stop();
|
_runtimeTimer.Stop();
|
||||||
_runtimeTimer.Dispose();
|
_runtimeTimer.Dispose();
|
||||||
_heartbeatTimer.Dispose();
|
_heartbeatTimer.Dispose();
|
||||||
@ -737,6 +787,8 @@ namespace Vmianqian
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void LoadPendingOrders()
|
private void LoadPendingOrders()
|
||||||
|
{
|
||||||
|
lock (_pendingOrdersFileLock)
|
||||||
{
|
{
|
||||||
if (!File.Exists(_pendingOrdersFilePath))
|
if (!File.Exists(_pendingOrdersFilePath))
|
||||||
{
|
{
|
||||||
@ -746,7 +798,9 @@ namespace Vmianqian
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(_pendingOrdersFilePath, Encoding.UTF8);
|
using var stream = new FileStream(_pendingOrdersFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
|
||||||
|
var json = reader.ReadToEnd();
|
||||||
_pendingOrders = JsonSerializer.Deserialize<List<PendingOrderRecord>>(json, _jsonOptions) ?? new List<PendingOrderRecord>();
|
_pendingOrders = JsonSerializer.Deserialize<List<PendingOrderRecord>>(json, _jsonOptions) ?? new List<PendingOrderRecord>();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -754,11 +808,54 @@ namespace Vmianqian
|
|||||||
_pendingOrders = new List<PendingOrderRecord>();
|
_pendingOrders = new List<PendingOrderRecord>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SavePendingOrders()
|
private void SavePendingOrders()
|
||||||
|
{
|
||||||
|
lock (_pendingOrdersFileLock)
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(_pendingOrders, _jsonOptions);
|
var json = JsonSerializer.Serialize(_pendingOrders, _jsonOptions);
|
||||||
File.WriteAllText(_pendingOrdersFilePath, json, Encoding.UTF8);
|
var tempFilePath = _pendingOrdersFilePath + ".tmp";
|
||||||
|
|
||||||
|
Exception? lastError = null;
|
||||||
|
for (var attempt = 1; attempt <= 5; attempt++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(tempFilePath, json, Encoding.UTF8);
|
||||||
|
|
||||||
|
if (File.Exists(_pendingOrdersFilePath))
|
||||||
|
{
|
||||||
|
File.Copy(tempFilePath, _pendingOrdersFilePath, overwrite: true);
|
||||||
|
File.Delete(tempFilePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
File.Move(tempFilePath, _pendingOrdersFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
lastError = ex;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(tempFilePath))
|
||||||
|
{
|
||||||
|
File.Delete(tempFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(80 * attempt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException($"保存待支付订单文件失败:{lastError?.Message}", lastError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BindConfigToUi()
|
private void BindConfigToUi()
|
||||||
@ -770,6 +867,9 @@ namespace Vmianqian
|
|||||||
txtSmtpPort.Text = _config.SmtpPort.ToString();
|
txtSmtpPort.Text = _config.SmtpPort.ToString();
|
||||||
txtNotifyEmail.Text = _config.NotifyEmail;
|
txtNotifyEmail.Text = _config.NotifyEmail;
|
||||||
txtEmailAuthCode.Text = _config.EmailAuthCode;
|
txtEmailAuthCode.Text = _config.EmailAuthCode;
|
||||||
|
txtAlipayAccount.Text = _config.AlipayAccount;
|
||||||
|
txtAlipayPassword.Text = _config.AlipayPassword;
|
||||||
|
chkAlipayAutoFill.Checked = _config.AlipayEnableAutoFill;
|
||||||
txtWechatPath.Text = _config.WechatPath;
|
txtWechatPath.Text = _config.WechatPath;
|
||||||
txtWechatId.Text = _config.WechatSid;
|
txtWechatId.Text = _config.WechatSid;
|
||||||
var savedAlipayUrl = string.IsNullOrWhiteSpace(_config.AlipayBillApiUrl)
|
var savedAlipayUrl = string.IsNullOrWhiteSpace(_config.AlipayBillApiUrl)
|
||||||
@ -796,6 +896,9 @@ namespace Vmianqian
|
|||||||
_config.SmtpPort = ParseSmtpPort(txtSmtpPort.Text);
|
_config.SmtpPort = ParseSmtpPort(txtSmtpPort.Text);
|
||||||
_config.NotifyEmail = txtNotifyEmail.Text.Trim();
|
_config.NotifyEmail = txtNotifyEmail.Text.Trim();
|
||||||
_config.EmailAuthCode = txtEmailAuthCode.Text.Trim();
|
_config.EmailAuthCode = txtEmailAuthCode.Text.Trim();
|
||||||
|
_config.AlipayAccount = txtAlipayAccount.Text.Trim();
|
||||||
|
_config.AlipayPassword = txtAlipayPassword.Text;
|
||||||
|
_config.AlipayEnableAutoFill = chkAlipayAutoFill.Checked;
|
||||||
_config.WechatPath = txtWechatPath.Text.Trim();
|
_config.WechatPath = txtWechatPath.Text.Trim();
|
||||||
_config.WechatSid = txtWechatId.Text.Trim();
|
_config.WechatSid = txtWechatId.Text.Trim();
|
||||||
_config.AlipayPath = txtAliPath.Text.Trim();
|
_config.AlipayPath = txtAliPath.Text.Trim();
|
||||||
@ -2101,6 +2204,22 @@ namespace Vmianqian
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btnAlipayAccountSave_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SaveUiToConfig();
|
||||||
|
SaveConfig();
|
||||||
|
Log("支付宝账号登录配置已保存到本地缓存文件。");
|
||||||
|
MessageBox.Show("支付宝配置已保存。", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"保存支付宝配置失败:{ex.Message}");
|
||||||
|
MessageBox.Show(ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void btnAlipayLogin_Click(object? sender, EventArgs e)
|
private void btnAlipayLogin_Click(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -2119,43 +2238,7 @@ namespace Vmianqian
|
|||||||
|
|
||||||
SaveUiToConfig();
|
SaveUiToConfig();
|
||||||
SaveConfig();
|
SaveConfig();
|
||||||
|
OpenAlipayLoginWindow(autoRefresh: false);
|
||||||
using var loginForm = new Vmianqian.AlipayLoginForm(BuildAlipayOptions());
|
|
||||||
loginForm.LoginSucceeded += (_, args) =>
|
|
||||||
{
|
|
||||||
EnsureAlipayMonitorCreated();
|
|
||||||
_alipayMonitor!.SetCookies(args.CookieContainer);
|
|
||||||
_alipayMonitor.SetCtoken(args.CToken);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(args.CToken))
|
|
||||||
{
|
|
||||||
var pureApiUrl = txtAliPath.Text.Trim();
|
|
||||||
if (Uri.TryCreate(pureApiUrl, UriKind.Absolute, out var apiUri))
|
|
||||||
{
|
|
||||||
pureApiUrl = $"{apiUri.Scheme}://{apiUri.Host}{apiUri.AbsolutePath}";
|
|
||||||
}
|
|
||||||
|
|
||||||
txtAliPath.Text = pureApiUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log($"支付宝登录成功,Cookie 已提取,ctoken={args.CToken},当前地址:{args.CurrentUrl}");
|
|
||||||
lblAlipayStatusValue.Text = "支付宝: 已登录";
|
|
||||||
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
|
||||||
|
|
||||||
// 登录成功后自动开始监听,不再需要用户手动再点一次。
|
|
||||||
_alipayMonitor.Start();
|
|
||||||
|
|
||||||
btnAlipayLogin.Text = "停止监听";
|
|
||||||
btnAlipayLogin.Type = TTypeMini.Error;
|
|
||||||
|
|
||||||
loginForm.BeginInvoke(() => loginForm.Close());
|
|
||||||
};
|
|
||||||
loginForm.StatusChanged += (_, args) =>
|
|
||||||
{
|
|
||||||
Log($"支付宝登录窗口:{args.Message}");
|
|
||||||
};
|
|
||||||
|
|
||||||
loginForm.ShowDialog(this);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -2215,7 +2298,10 @@ namespace Vmianqian
|
|||||||
JsonpCallback = "callback",
|
JsonpCallback = "callback",
|
||||||
MinPollSeconds = 15,
|
MinPollSeconds = 15,
|
||||||
MaxPollSeconds = Math.Max(15, Math.Min(35, (int)numAlipayInterval.Value)),
|
MaxPollSeconds = Math.Max(15, Math.Min(35, (int)numAlipayInterval.Value)),
|
||||||
PageSize = 10
|
PageSize = 10,
|
||||||
|
Account = txtAlipayAccount.Text.Trim(),
|
||||||
|
Password = txtAlipayPassword.Text,
|
||||||
|
EnableAutoFill = chkAlipayAutoFill.Checked
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2295,21 +2381,154 @@ namespace Vmianqian
|
|||||||
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
||||||
break;
|
break;
|
||||||
case "CookieExpired":
|
case "CookieExpired":
|
||||||
lblAlipayStatusValue.Text = "支付宝: Cookie失效";
|
lblAlipayStatusValue.Text = "支付宝: 自动续登中";
|
||||||
lblAlipayStatusValue.ForeColor = Color.Red;
|
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
||||||
btnAlipayLogin.Text = "扫码登录";
|
btnAlipayLogin.Text = "自动续登中";
|
||||||
btnAlipayLogin.Type = TTypeMini.Primary;
|
btnAlipayLogin.Type = TTypeMini.Warn;
|
||||||
MessageBox.Show("支付宝 Cookie 失效,请重新扫码登录。", "支付宝监听提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
BeginAlipayAutoRefreshCookie();
|
||||||
break;
|
break;
|
||||||
case "Stopped":
|
case "Stopped":
|
||||||
|
if (_alipayAutoRefreshingCookie)
|
||||||
|
{
|
||||||
|
lblAlipayStatusValue.Text = "支付宝: 自动续登中";
|
||||||
|
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
||||||
|
btnAlipayLogin.Text = "自动续登中";
|
||||||
|
btnAlipayLogin.Type = TTypeMini.Warn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lblAlipayStatusValue.Text = "支付宝: 离线";
|
lblAlipayStatusValue.Text = "支付宝: 离线";
|
||||||
lblAlipayStatusValue.ForeColor = Color.Red;
|
lblAlipayStatusValue.ForeColor = Color.Red;
|
||||||
btnAlipayLogin.Text = "扫码登录";
|
btnAlipayLogin.Text = "扫码登录";
|
||||||
btnAlipayLogin.Type = TTypeMini.Primary;
|
btnAlipayLogin.Type = TTypeMini.Primary;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BeginAlipayAutoRefreshCookie()
|
||||||
|
{
|
||||||
|
if (_alipayAutoRefreshingCookie)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_alipayAutoRefreshingCookie = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SaveUiToConfig();
|
||||||
|
SaveConfig();
|
||||||
|
Log("支付宝 Cookie 已失效,正在自动打开登录窗口续 Cookie……");
|
||||||
|
OpenAlipayLoginWindow(autoRefresh: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_alipayAutoRefreshingCookie = false;
|
||||||
|
Log($"支付宝自动续 Cookie 失败:{ex.Message}");
|
||||||
|
lblAlipayStatusValue.Text = "支付宝: 续登失败";
|
||||||
|
lblAlipayStatusValue.ForeColor = Color.Red;
|
||||||
|
btnAlipayLogin.Text = "扫码登录";
|
||||||
|
btnAlipayLogin.Type = TTypeMini.Primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenAlipayLoginWindow(bool autoRefresh)
|
||||||
|
{
|
||||||
|
if (_alipayLoginForm != null && !_alipayLoginForm.IsDisposed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_alipayLoginForm.BringToFront();
|
||||||
|
_alipayLoginForm.Focus();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginForm = new Vmianqian.AlipayLoginForm(BuildAlipayOptions());
|
||||||
|
_alipayLoginForm = loginForm;
|
||||||
|
|
||||||
|
loginForm.FormClosed += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(_alipayLoginForm, loginForm))
|
||||||
|
{
|
||||||
|
_alipayLoginForm = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_alipayAutoRefreshingCookie)
|
||||||
|
{
|
||||||
|
_alipayAutoRefreshingCookie = false;
|
||||||
|
|
||||||
|
if (_alipayMonitor?.IsRunning != true)
|
||||||
|
{
|
||||||
|
lblAlipayStatusValue.Text = "支付宝: 离线";
|
||||||
|
lblAlipayStatusValue.ForeColor = Color.Red;
|
||||||
|
btnAlipayLogin.Text = "扫码登录";
|
||||||
|
btnAlipayLogin.Type = TTypeMini.Primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loginForm.LoginSucceeded += (_, args) =>
|
||||||
|
{
|
||||||
|
EnsureAlipayMonitorCreated();
|
||||||
|
_alipayMonitor!.SetCookies(args.CookieContainer);
|
||||||
|
_alipayMonitor.SetCtoken(args.CToken);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(args.CToken))
|
||||||
|
{
|
||||||
|
var pureApiUrl = txtAliPath.Text.Trim();
|
||||||
|
if (Uri.TryCreate(pureApiUrl, UriKind.Absolute, out var apiUri))
|
||||||
|
{
|
||||||
|
pureApiUrl = $"{apiUri.Scheme}://{apiUri.Host}{apiUri.AbsolutePath}";
|
||||||
|
}
|
||||||
|
|
||||||
|
txtAliPath.Text = pureApiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(autoRefresh
|
||||||
|
? $"支付宝自动续 Cookie 成功,ctoken={args.CToken},当前地址:{args.CurrentUrl}"
|
||||||
|
: $"支付宝登录成功,Cookie 已提取,ctoken={args.CToken},当前地址:{args.CurrentUrl}");
|
||||||
|
|
||||||
|
lblAlipayStatusValue.Text = "支付宝: 已登录";
|
||||||
|
lblAlipayStatusValue.ForeColor = Color.DarkOrange;
|
||||||
|
|
||||||
|
_alipayAutoRefreshingCookie = false;
|
||||||
|
|
||||||
|
// 登录成功后自动开始监听,不再需要用户手动再点一次。
|
||||||
|
_alipayMonitor.Start();
|
||||||
|
|
||||||
|
btnAlipayLogin.Text = "停止监听";
|
||||||
|
btnAlipayLogin.Type = TTypeMini.Error;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
loginForm.BeginInvoke(() => loginForm.Close());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loginForm.StatusChanged += (_, args) =>
|
||||||
|
{
|
||||||
|
Log($"支付宝登录窗口:{args.Message}");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (autoRefresh)
|
||||||
|
{
|
||||||
|
loginForm.Show(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loginForm.ShowDialog(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task StartListenerAsync()
|
private async Task StartListenerAsync()
|
||||||
{
|
{
|
||||||
StopListener();
|
StopListener();
|
||||||
@ -3090,6 +3309,451 @@ namespace Vmianqian
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将设备号显示到首页会员卡区域。
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateDeviceCodeLabel()
|
||||||
|
{
|
||||||
|
if (label3 == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
label3.Text = $"设备号:{_deviceCode}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 复制设备号按钮点击事件。
|
||||||
|
/// 便于用户一键复制后发送给他人。
|
||||||
|
/// </summary>
|
||||||
|
private void btnCopyDeviceCode_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_deviceCode))
|
||||||
|
{
|
||||||
|
MessageBox.Show("当前设备号为空,无法复制。", "复制失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clipboard.SetText(_deviceCode);
|
||||||
|
Log($"设备号已复制:{_deviceCode}");
|
||||||
|
MessageBox.Show("设备号已复制到剪贴板。", "复制成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"复制设备号失败:{ex.Message}");
|
||||||
|
MessageBox.Show($"复制失败:{ex.Message}", "复制失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnActivate_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var activationCode = txtActivationCode?.Text?.Trim() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(activationCode))
|
||||||
|
{
|
||||||
|
MessageBox.Show("请输入激活码。", "激活提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log($"收到激活请求,激活码:{activationCode}");
|
||||||
|
MessageBox.Show("激活按钮已添加,后续可在此接入真实激活接口。", "激活", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"激活失败:{ex.Message}");
|
||||||
|
MessageBox.Show($"激活失败:{ex.Message}", "激活失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void btnCheckUpdate_Click(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
await CheckForUpdatesAsync(silentWhenUpToDate: false, showNoUpdateMessage: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckForUpdatesAsync(bool silentWhenUpToDate, bool showNoUpdateMessage)
|
||||||
|
{
|
||||||
|
if (_isUpdating)
|
||||||
|
{
|
||||||
|
if (!silentWhenUpToDate)
|
||||||
|
{
|
||||||
|
MessageBox.Show("更新任务正在执行中,请稍候。", "检查更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var currentVersion = GetCurrentVersion();
|
||||||
|
var requestUrl = BuildUpgradeCheckUrl(UpgradeProductCode, currentVersion);
|
||||||
|
var response = await _httpClient.GetAsync(requestUrl);
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Log($"检查更新失败:HTTP {(int)response.StatusCode} {response.StatusCode}");
|
||||||
|
if (!silentWhenUpToDate)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"检查更新失败:HTTP {(int)response.StatusCode} {response.StatusCode}", "检查更新", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = JsonSerializer.Deserialize<SoftwareUpgradeCheckResponse>(body, _jsonOptions);
|
||||||
|
if (result?.Data == null)
|
||||||
|
{
|
||||||
|
Log("检查更新失败:服务端响应无法解析。");
|
||||||
|
if (!silentWhenUpToDate)
|
||||||
|
{
|
||||||
|
MessageBox.Show("检查更新失败:服务端响应无法解析。", "检查更新", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteVersion = (result.Data.LatestVersion ?? string.Empty).Trim();
|
||||||
|
var upToDate = result.Data.UpToDate;
|
||||||
|
|
||||||
|
if (!upToDate && IsRemoteVersionNewer(currentVersion, remoteVersion))
|
||||||
|
{
|
||||||
|
Log($"发现新版本:当前={currentVersion},最新={remoteVersion}");
|
||||||
|
var notes = string.IsNullOrWhiteSpace(result.Data.ReleaseNotes) ? "暂无更新说明" : result.Data.ReleaseNotes.Trim();
|
||||||
|
var downloadUrl = (result.Data.DownloadUrl ?? string.Empty).Trim();
|
||||||
|
var forceText = result.Data.ForceUpdate ? "是" : "否";
|
||||||
|
|
||||||
|
var promptMessage =
|
||||||
|
$"发现新版本:{remoteVersion}{Environment.NewLine}" +
|
||||||
|
$"当前版本:{currentVersion}{Environment.NewLine}" +
|
||||||
|
$"是否强更:{forceText}{Environment.NewLine}{Environment.NewLine}" +
|
||||||
|
$"更新说明:{Environment.NewLine}{notes}{Environment.NewLine}{Environment.NewLine}" +
|
||||||
|
(result.Data.ForceUpdate
|
||||||
|
? "当前版本为强制更新,点击“确定”后将立即开始增量更新。"
|
||||||
|
: "点击“是”开始增量更新,程序会在下载完成后自动退出并覆盖更新。");
|
||||||
|
|
||||||
|
var dialogResult = MessageBox.Show(
|
||||||
|
promptMessage,
|
||||||
|
"发现新版本",
|
||||||
|
result.Data.ForceUpdate ? MessageBoxButtons.OK : MessageBoxButtons.YesNo,
|
||||||
|
MessageBoxIcon.Information);
|
||||||
|
|
||||||
|
if ((result.Data.ForceUpdate && dialogResult == DialogResult.OK) ||
|
||||||
|
(!result.Data.ForceUpdate && dialogResult == DialogResult.Yes))
|
||||||
|
{
|
||||||
|
await StartIncrementalUpdateAsync(remoteVersion, downloadUrl, result.Data.ForceUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log($"已是最新版本:{currentVersion}");
|
||||||
|
if (showNoUpdateMessage)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"当前已是最新版本:{currentVersion}", "检查更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"检查更新异常:{ex.Message}");
|
||||||
|
if (!silentWhenUpToDate)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"检查更新失败:{ex.Message}", "检查更新", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartIncrementalUpdateAsync(string remoteVersion, string downloadUrl, bool forceUpdate)
|
||||||
|
{
|
||||||
|
if (_isUpdating)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(downloadUrl))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("服务端未提供更新包下载地址。");
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdating = true;
|
||||||
|
if (btnCheckUpdate != null)
|
||||||
|
{
|
||||||
|
btnCheckUpdate.Loading = true;
|
||||||
|
btnCheckUpdate.Enabled = false;
|
||||||
|
btnCheckUpdate.Text = "更新中";
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tempRoot = Path.Combine(Path.GetTempPath(), "VmianqianUpdate", remoteVersion + "_" + DateTime.Now.ToString("yyyyMMddHHmmss"));
|
||||||
|
var packageFile = Path.Combine(tempRoot, "update-package.zip");
|
||||||
|
var extractRoot = Path.Combine(tempRoot, "package");
|
||||||
|
Directory.CreateDirectory(tempRoot);
|
||||||
|
|
||||||
|
Log($"开始下载增量更新包:{downloadUrl}");
|
||||||
|
await DownloadFileAsync(downloadUrl, packageFile);
|
||||||
|
Log($"增量更新包下载完成:{packageFile}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ZipFile.ExtractToDirectory(packageFile, extractRoot, true);
|
||||||
|
}
|
||||||
|
catch (InvalidDataException)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("更新包不是有效的 zip 压缩包,无法执行增量更新。");
|
||||||
|
}
|
||||||
|
|
||||||
|
var payloadRoot = ResolveUpdatePayloadRoot(extractRoot);
|
||||||
|
Log($"增量更新包已解压:{payloadRoot}");
|
||||||
|
|
||||||
|
var launcherPath = Application.ExecutablePath;
|
||||||
|
var currentProcessId = Environment.ProcessId;
|
||||||
|
var scriptPath = Path.Combine(tempRoot, "apply-update.cmd");
|
||||||
|
var updateLogPath = Path.Combine(tempRoot, "apply-update.log");
|
||||||
|
var appDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
|
var deleteListPath = Path.Combine(payloadRoot, "delete-files.txt");
|
||||||
|
|
||||||
|
var deleteCommands = new StringBuilder();
|
||||||
|
if (File.Exists(deleteListPath))
|
||||||
|
{
|
||||||
|
foreach (var line in File.ReadAllLines(deleteListPath, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
var relativePath = line.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(relativePath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relativePath.StartsWith("#", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedRelative = relativePath.Replace('/', '\\').TrimStart('\\');
|
||||||
|
var fullDeletePath = Path.Combine(appDirectory, normalizedRelative);
|
||||||
|
deleteCommands.AppendLine($"if exist \"{fullDeletePath}\" del /f /q \"{fullDeletePath}\" >> \"%LOG%\" 2>&1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var script = $@"@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
setlocal enableextensions
|
||||||
|
set ""APPDIR={appDirectory}""
|
||||||
|
set ""SRCDIR={payloadRoot}""
|
||||||
|
set ""EXE={launcherPath}""
|
||||||
|
set ""PID={currentProcessId}""
|
||||||
|
set ""LOG={updateLogPath}""
|
||||||
|
|
||||||
|
echo [%%date%% %%time%%] updater started > ""%LOG%""
|
||||||
|
echo APPDIR=%APPDIR% >> ""%LOG%""
|
||||||
|
echo SRCDIR=%SRCDIR% >> ""%LOG%""
|
||||||
|
echo EXE=%EXE% >> ""%LOG%""
|
||||||
|
echo PID=%PID% >> ""%LOG%""
|
||||||
|
|
||||||
|
taskkill /PID %PID% /T /F >> ""%LOG%"" 2>&1
|
||||||
|
|
||||||
|
:waitloop
|
||||||
|
tasklist /FI ""PID eq %PID%"" | find ""%PID%"" >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
timeout /t 1 /nobreak >nul
|
||||||
|
goto waitloop
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [%%date%% %%time%%] process stopped, start copy >> ""%LOG%""
|
||||||
|
robocopy ""%SRCDIR%"" ""%APPDIR%"" /E /R:3 /W:1 /NFL /NDL /NJH /NJS /NP >> ""%LOG%"" 2>&1
|
||||||
|
set ""RC=%ERRORLEVEL%""
|
||||||
|
echo robocopy exit code=%RC% >> ""%LOG%""
|
||||||
|
if %RC% GEQ 8 goto copyfailed
|
||||||
|
|
||||||
|
{deleteCommands}
|
||||||
|
echo [%%date%% %%time%%] copy success, start app >> ""%LOG%""
|
||||||
|
start """" ""%EXE%""
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:copyfailed
|
||||||
|
echo [%%date%% %%time%%] copy failed, robocopy exit code=%RC% >> ""%LOG%""
|
||||||
|
start notepad.exe ""%LOG%""
|
||||||
|
exit /b %RC%
|
||||||
|
";
|
||||||
|
File.WriteAllText(scriptPath, script, new UTF8Encoding(false));
|
||||||
|
|
||||||
|
Log($"增量更新准备完成,目标版本:{remoteVersion}");
|
||||||
|
var confirmText = forceUpdate
|
||||||
|
? "更新包已下载完成,当前版本要求强制更新。点击确定后程序将退出并自动完成覆盖更新。"
|
||||||
|
: "更新包已下载完成。点击确定后程序将退出并自动完成覆盖更新。";
|
||||||
|
MessageBox.Show(confirmText, "开始更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
|
||||||
|
Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = scriptPath,
|
||||||
|
UseShellExecute = true,
|
||||||
|
WorkingDirectory = tempRoot,
|
||||||
|
WindowStyle = ProcessWindowStyle.Normal
|
||||||
|
});
|
||||||
|
|
||||||
|
BeginInvoke(new Action(Close));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_isUpdating = false;
|
||||||
|
if (btnCheckUpdate != null)
|
||||||
|
{
|
||||||
|
btnCheckUpdate.Loading = false;
|
||||||
|
btnCheckUpdate.Enabled = true;
|
||||||
|
btnCheckUpdate.Text = "检查更新";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadFileAsync(string requestUrl, string targetFilePath)
|
||||||
|
{
|
||||||
|
using var response = await _httpClient.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath)!);
|
||||||
|
|
||||||
|
await using var httpStream = await response.Content.ReadAsStreamAsync();
|
||||||
|
await using var fileStream = new FileStream(targetFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
await httpStream.CopyToAsync(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveUpdatePayloadRoot(string extractRoot)
|
||||||
|
{
|
||||||
|
var incrementalRoot = Path.Combine(extractRoot, "incremental");
|
||||||
|
if (Directory.Exists(incrementalRoot))
|
||||||
|
{
|
||||||
|
return incrementalRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appRoot = Path.Combine(extractRoot, "app");
|
||||||
|
if (Directory.Exists(appRoot))
|
||||||
|
{
|
||||||
|
return appRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTitlebarVersion()
|
||||||
|
{
|
||||||
|
if (titlebar == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
titlebar.SubText = $"V{GetCurrentVersion()}";
|
||||||
|
titlebar.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCurrentVersion()
|
||||||
|
{
|
||||||
|
var informationalVersion = Assembly
|
||||||
|
.GetExecutingAssembly()
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
|
?.InformationalVersion;
|
||||||
|
|
||||||
|
var version = string.IsNullOrWhiteSpace(informationalVersion)
|
||||||
|
? Application.ProductVersion?.Trim()
|
||||||
|
: informationalVersion.Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(version))
|
||||||
|
{
|
||||||
|
version = "0.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
var plusIndex = version.IndexOf('+');
|
||||||
|
if (plusIndex >= 0)
|
||||||
|
{
|
||||||
|
version = version[..plusIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return version.Trim().TrimStart('V', 'v');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildUpgradeCheckUrl(string code, string version)
|
||||||
|
{
|
||||||
|
return $"{UpgradeCheckApiBaseUrl}/api/softwareupgrade/check?code={Uri.EscapeDataString(code)}&version={Uri.EscapeDataString(string.IsNullOrWhiteSpace(version) ? "0.0.0" : version)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRemoteVersionNewer(string currentVersion, string remoteVersion)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(remoteVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Version.TryParse(NormalizeVersion(remoteVersion), out var remote) &&
|
||||||
|
Version.TryParse(NormalizeVersion(currentVersion), out var current))
|
||||||
|
{
|
||||||
|
return remote > current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !string.Equals(currentVersion?.Trim(), remoteVersion.Trim(), StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeVersion(string? version)
|
||||||
|
{
|
||||||
|
var value = string.IsNullOrWhiteSpace(version) ? "0.0.0" : version.Trim();
|
||||||
|
var parts = value.Split('.', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
while (parts.Count < 3)
|
||||||
|
{
|
||||||
|
parts.Add("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(".", parts.Take(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成当前设备的唯一标识。
|
||||||
|
/// 优先拼接主板序列号、CPU ProcessorId、磁盘序列号和机器名,再做 SHA256 摘要。
|
||||||
|
/// 若某些硬件信息读取失败,则自动降级为可获取到的信息组合。
|
||||||
|
/// </summary>
|
||||||
|
private static string BuildDeviceCode()
|
||||||
|
{
|
||||||
|
var parts = new List<string>
|
||||||
|
{
|
||||||
|
GetWmiProperty("Win32_BaseBoard", "SerialNumber"),
|
||||||
|
GetWmiProperty("Win32_Processor", "ProcessorId"),
|
||||||
|
GetWmiProperty("Win32_DiskDrive", "SerialNumber"),
|
||||||
|
Environment.MachineName
|
||||||
|
};
|
||||||
|
|
||||||
|
var raw = string.Join("|", parts.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()));
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
{
|
||||||
|
raw = $"{Environment.MachineName}|{Environment.UserName}|{Environment.OSVersion.VersionString}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
|
||||||
|
var hex = Convert.ToHexString(bytes);
|
||||||
|
return hex[..24];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取指定 WMI 类的首个属性值。
|
||||||
|
/// </summary>
|
||||||
|
private static string GetWmiProperty(string className, string propertyName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var searcher = new ManagementObjectSearcher($"SELECT {propertyName} FROM {className}");
|
||||||
|
foreach (var item in searcher.Get())
|
||||||
|
{
|
||||||
|
var value = item[propertyName]?.ToString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return value.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private static (string Sid, string Version, string Source) TryExtractSidFromWechatLocalFiles()
|
private static (string Sid, string Version, string Source) TryExtractSidFromWechatLocalFiles()
|
||||||
{
|
{
|
||||||
foreach (var root in GetWechatDataRoots())
|
foreach (var root in GetWechatDataRoots())
|
||||||
@ -3444,6 +4108,9 @@ namespace Vmianqian
|
|||||||
public int SmtpPort { get; set; } = 465;
|
public int SmtpPort { get; set; } = 465;
|
||||||
public string NotifyEmail { get; set; } = string.Empty;
|
public string NotifyEmail { get; set; } = string.Empty;
|
||||||
public string EmailAuthCode { get; set; } = string.Empty;
|
public string EmailAuthCode { get; set; } = string.Empty;
|
||||||
|
public string AlipayAccount { get; set; } = string.Empty;
|
||||||
|
public string AlipayPassword { get; set; } = string.Empty;
|
||||||
|
public bool AlipayEnableAutoFill { get; set; }
|
||||||
public string WechatPath { get; set; } = string.Empty;
|
public string WechatPath { get; set; } = string.Empty;
|
||||||
public string WechatSid { get; set; } = string.Empty;
|
public string WechatSid { get; set; } = string.Empty;
|
||||||
public string WechatApiVersion { get; set; } = "7.10.1";
|
public string WechatApiVersion { get; set; } = "7.10.1";
|
||||||
@ -3508,6 +4175,31 @@ namespace Vmianqian
|
|||||||
public List<PendingOrderRegistration>? Data { get; set; }
|
public List<PendingOrderRegistration>? Data { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SoftwareUpgradeCheckResponse
|
||||||
|
{
|
||||||
|
public int Code { get; set; }
|
||||||
|
public string Msg { get; set; } = string.Empty;
|
||||||
|
public SoftwareUpgradeCheckData? Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SoftwareUpgradeCheckData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("upToDate")]
|
||||||
|
public bool UpToDate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("latestVersion")]
|
||||||
|
public string LatestVersion { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("downloadUrl")]
|
||||||
|
public string DownloadUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("forceUpdate")]
|
||||||
|
public bool ForceUpdate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("releaseNotes")]
|
||||||
|
public string ReleaseNotes { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class ServerCallbackPayload
|
public sealed class ServerCallbackPayload
|
||||||
{
|
{
|
||||||
public string ApiKey { get; set; } = string.Empty;
|
public string ApiKey { get; set; } = string.Empty;
|
||||||
|
|||||||
11
Properties/PublishProfiles/FolderProfile.pubxml
Normal file
11
Properties/PublishProfiles/FolderProfile.pubxml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Any CPU</Platform>
|
||||||
|
<PublishDir>bin\Release\net10.0-windows\publish\</PublishDir>
|
||||||
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
|
<_TargetId>Folder</_TargetId>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
4
Properties/PublishProfiles/FolderProfile.pubxml.user
Normal file
4
Properties/PublishProfiles/FolderProfile.pubxml.user
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||||
|
<Project>
|
||||||
|
</Project>
|
||||||
73
Properties/Resources.Designer.cs
generated
Normal file
73
Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 此代码由工具生成。
|
||||||
|
// 运行时版本:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||||
|
// 重新生成代码,这些更改将会丢失。
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Vmianqian.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||||
|
/// </summary>
|
||||||
|
// 此类是由 StronglyTypedResourceBuilder
|
||||||
|
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||||
|
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||||
|
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Vmianqian.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||||
|
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap logo {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("logo", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Properties/Resources.resx
Normal file
124
Properties/Resources.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="logo" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\logo.ico;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
5
Readmd.md
Normal file
5
Readmd.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 自动生成升级包代码
|
||||||
|
```
|
||||||
|
powershell -ExecutionPolicy Bypass -File .\package-incremental.ps1
|
||||||
|
|
||||||
|
```
|
||||||
@ -1,6 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<Version>0.0.3</Version>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net10.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@ -8,12 +9,14 @@
|
|||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<GitRepositoryConfigurationScope>local</GitRepositoryConfigurationScope>
|
<GitRepositoryConfigurationScope>local</GitRepositoryConfigurationScope>
|
||||||
|
<ApplicationIcon>logo.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AntdUI" Version="2.3.10" />
|
<PackageReference Include="AntdUI" Version="2.3.10" />
|
||||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||||||
|
<PackageReference Include="System.Management" Version="9.0.0" />
|
||||||
<PackageReference Include="Titanium.Web.Proxy" Version="3.2.0" />
|
<PackageReference Include="Titanium.Web.Proxy" Version="3.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -25,4 +28,23 @@
|
|||||||
<None Remove="antdui-demo\**\*" />
|
<None Remove="antdui-demo\**\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="logo.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Properties\Resources.Designer.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Update="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<_LastSelectedProfileId>E:\Demo\C\Vmianqian\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Form1.cs">
|
<Compile Update="Form1.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
|
|||||||
@ -6,11 +6,12 @@
|
|||||||
"compilationOptions": {},
|
"compilationOptions": {},
|
||||||
"targets": {
|
"targets": {
|
||||||
".NETCoreApp,Version=v10.0": {
|
".NETCoreApp,Version=v10.0": {
|
||||||
"Vmianqian/1.0.0": {
|
"Vmianqian/0.0.2": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AntdUI": "2.3.10",
|
"AntdUI": "2.3.10",
|
||||||
"MailKit": "4.16.0",
|
"MailKit": "4.16.0",
|
||||||
"Microsoft.Web.WebView2": "1.0.2903.40",
|
"Microsoft.Web.WebView2": "1.0.2903.40",
|
||||||
|
"System.Management": "9.0.0",
|
||||||
"Titanium.Web.Proxy": "3.2.0",
|
"Titanium.Web.Proxy": "3.2.0",
|
||||||
"Microsoft.Web.WebView2.Core": "1.0.2903.40",
|
"Microsoft.Web.WebView2.Core": "1.0.2903.40",
|
||||||
"Microsoft.Web.WebView2.WinForms": "1.0.2903.40",
|
"Microsoft.Web.WebView2.WinForms": "1.0.2903.40",
|
||||||
@ -93,6 +94,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Management/9.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net9.0/System.Management.dll": {
|
||||||
|
"assemblyVersion": "9.0.0.0",
|
||||||
|
"fileVersion": "9.0.24.52809"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeTargets": {
|
||||||
|
"runtimes/win/lib/net9.0/System.Management.dll": {
|
||||||
|
"rid": "win",
|
||||||
|
"assetType": "runtime",
|
||||||
|
"assemblyVersion": "9.0.0.0",
|
||||||
|
"fileVersion": "9.0.24.52809"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Titanium.Web.Proxy/3.2.0": {
|
"Titanium.Web.Proxy/3.2.0": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BrotliSharpLib": "0.3.3",
|
"BrotliSharpLib": "0.3.3",
|
||||||
@ -132,7 +149,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"libraries": {
|
"libraries": {
|
||||||
"Vmianqian/1.0.0": {
|
"Vmianqian/0.0.2": {
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"serviceable": false,
|
"serviceable": false,
|
||||||
"sha512": ""
|
"sha512": ""
|
||||||
@ -186,6 +203,13 @@
|
|||||||
"path": "portable.bouncycastle/1.8.8",
|
"path": "portable.bouncycastle/1.8.8",
|
||||||
"hashPath": "portable.bouncycastle.1.8.8.nupkg.sha512"
|
"hashPath": "portable.bouncycastle.1.8.8.nupkg.sha512"
|
||||||
},
|
},
|
||||||
|
"System.Management/9.0.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-bVh4xAMI5grY5GZoklKcMBLirhC8Lqzp63Ft3zXJacwGAlLyFdF4k0qz4pnKIlO6HyL2Z4zqmHm9UkzEo6FFsA==",
|
||||||
|
"path": "system.management/9.0.0",
|
||||||
|
"hashPath": "system.management.9.0.0.nupkg.sha512"
|
||||||
|
},
|
||||||
"Titanium.Web.Proxy/3.2.0": {
|
"Titanium.Web.Proxy/3.2.0": {
|
||||||
"type": "package",
|
"type": "package",
|
||||||
"serviceable": true,
|
"serviceable": true,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -8,5 +8,5 @@
|
|||||||
"top_topics_and_observing_domains": [ ]
|
"top_topics_and_observing_domains": [ ]
|
||||||
} ],
|
} ],
|
||||||
"hex_encoded_hmac_key": "3D20341D26496BECEC5CACADB813B2813BF9F4CB81F7946788CD04BBF5F9787C",
|
"hex_encoded_hmac_key": "3D20341D26496BECEC5CACADB813B2813BF9F4CB81F7946788CD04BBF5F9787C",
|
||||||
"next_scheduled_calculation_time": "13423151610108124"
|
"next_scheduled_calculation_time": "13423151610108165"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.890 a9f0 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/MANIFEST-000001
|
2026/05/07-20:24:27.760 1648 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.890 a9f0 Recovering log #3
|
2026/05/07-20:24:27.761 1648 Recovering log #3
|
||||||
2026/05/07-00:59:30.890 a9f0 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/000003.log
|
2026/05/07-20:24:27.761 1648 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:08.006 5d14 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/MANIFEST-000001
|
2026/05/07-20:03:13.009 ef30 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/MANIFEST-000001
|
||||||
2026/05/07-00:34:08.007 5d14 Recovering log #3
|
2026/05/07-20:03:13.009 ef30 Recovering log #3
|
||||||
2026/05/07-00:34:08.007 5d14 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/000003.log
|
2026/05/07-20:03:13.010 ef30 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Extension State/000003.log
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.851 50dc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
2026/05/07-20:24:27.720 c6fc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.857 50dc Recovering log #8
|
2026/05/07-20:24:27.728 c6fc Recovering log #8
|
||||||
2026/05/07-00:59:30.859 50dc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000008.log
|
2026/05/07-20:24:27.731 c6fc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000008.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:07.975 f674 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
2026/05/07-20:03:12.956 f7ec Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||||
2026/05/07-00:34:07.981 f674 Recovering log #8
|
2026/05/07-20:03:12.964 f7ec Recovering log #8
|
||||||
2026/05/07-00:34:07.990 f674 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000008.log
|
2026/05/07-20:03:12.967 f7ec Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000008.log
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:31.188 50dc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
2026/05/07-20:24:28.037 c6fc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||||
2026/05/07-00:59:31.188 50dc Recovering log #3
|
2026/05/07-20:24:28.038 c6fc Recovering log #3
|
||||||
2026/05/07-00:59:31.190 50dc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
2026/05/07-20:24:28.040 c6fc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:08.312 f674 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
2026/05/07-20:03:13.318 f7ec Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||||
2026/05/07-00:34:08.312 f674 Recovering log #3
|
2026/05/07-20:03:13.319 f7ec Recovering log #3
|
||||||
2026/05/07-00:34:08.315 f674 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
2026/05/07-20:03:13.321 f7ec Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||||
|
|||||||
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.785 ecb0 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
2026/05/07-20:24:27.648 a5cc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.789 ecb0 Recovering log #3
|
2026/05/07-20:24:27.651 a5cc Recovering log #3
|
||||||
2026/05/07-00:59:30.789 ecb0 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log
|
2026/05/07-20:24:27.652 a5cc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:07.905 ece0 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
2026/05/07-20:03:12.876 c9c4 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||||
2026/05/07-00:34:07.909 ece0 Recovering log #3
|
2026/05/07-20:03:12.882 c9c4 Recovering log #3
|
||||||
2026/05/07-00:34:07.909 ece0 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log
|
2026/05/07-20:03:12.882 c9c4 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.784 d424 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/MANIFEST-000001
|
2026/05/07-20:24:27.647 73b4 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.789 d424 Recovering log #3
|
2026/05/07-20:24:27.651 73b4 Recovering log #3
|
||||||
2026/05/07-00:59:30.789 d424 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/000003.log
|
2026/05/07-20:24:27.651 73b4 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:07.904 6f20 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/MANIFEST-000001
|
2026/05/07-20:03:12.876 b7cc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/MANIFEST-000001
|
||||||
2026/05/07-00:34:07.909 6f20 Recovering log #3
|
2026/05/07-20:03:12.882 b7cc Recovering log #3
|
||||||
2026/05/07-00:34:07.909 6f20 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/000003.log
|
2026/05/07-20:03:12.882 b7cc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\Sync Data\LevelDB/000003.log
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -26,3 +26,8 @@
|
|||||||
2026-05-06 16:31:24.926: [INFO] OnDoneLoading sync enabled: 0
|
2026-05-06 16:31:24.926: [INFO] OnDoneLoading sync enabled: 0
|
||||||
2026-05-06 16:34:07.923: [INFO] OnDoneLoading sync enabled: 0
|
2026-05-06 16:34:07.923: [INFO] OnDoneLoading sync enabled: 0
|
||||||
2026-05-06 16:59:30.809: [INFO] OnDoneLoading sync enabled: 0
|
2026-05-06 16:59:30.809: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
2026-05-07 11:33:26.664: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
2026-05-07 11:51:55.297: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
2026-05-07 11:55:20.733: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
2026-05-07 11:55:55.674: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
2026-05-07 11:56:13.319: [INFO] OnDoneLoading sync enabled: 0
|
||||||
|
|||||||
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.817 f6dc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/MANIFEST-000001
|
2026/05/07-20:24:27.673 1648 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.817 f6dc Recovering log #3
|
2026/05/07-20:24:27.673 1648 Recovering log #3
|
||||||
2026/05/07-00:59:30.817 f6dc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/000003.log
|
2026/05/07-20:24:27.674 1648 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:07.931 f2c0 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/MANIFEST-000001
|
2026/05/07-20:03:12.908 b7cc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/MANIFEST-000001
|
||||||
2026/05/07-00:34:07.931 f2c0 Recovering log #3
|
2026/05/07-20:03:12.908 b7cc Recovering log #3
|
||||||
2026/05/07-00:34:07.932 f2c0 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/000003.log
|
2026/05/07-20:03:12.909 b7cc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db/000003.log
|
||||||
|
|||||||
Binary file not shown.
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:59:30.813 f6dc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/MANIFEST-000001
|
2026/05/07-20:24:27.669 1648 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||||
2026/05/07-00:59:30.813 f6dc Recovering log #3
|
2026/05/07-20:24:27.670 1648 Recovering log #3
|
||||||
2026/05/07-00:59:30.813 f6dc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/000003.log
|
2026/05/07-20:24:27.670 1648 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/000003.log
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
2026/05/07-00:34:07.927 f2c0 Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/MANIFEST-000001
|
2026/05/07-20:03:12.903 b7cc Reusing MANIFEST E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/MANIFEST-000001
|
||||||
2026/05/07-00:34:07.927 f2c0 Recovering log #3
|
2026/05/07-20:03:12.904 b7cc Recovering log #3
|
||||||
2026/05/07-00:34:07.927 f2c0 Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/000003.log
|
2026/05/07-20:03:12.904 b7cc Reusing old log E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Vmianqian.exe.WebView2\EBWebView\Default\shared_proto_db\metadata/000003.log
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@ -6,6 +6,9 @@
|
|||||||
"SmtpPort": 465,
|
"SmtpPort": 465,
|
||||||
"NotifyEmail": "1066960883@qq.com",
|
"NotifyEmail": "1066960883@qq.com",
|
||||||
"EmailAuthCode": "TPPMKSMvCadyzu3m",
|
"EmailAuthCode": "TPPMKSMvCadyzu3m",
|
||||||
|
"AlipayAccount": "19895983967",
|
||||||
|
"AlipayPassword": "Lzq920103!",
|
||||||
|
"AlipayEnableAutoFill": true,
|
||||||
"WechatPath": "C:\\Softwares\\Tencent\\Weixin\\Weixin.exe",
|
"WechatPath": "C:\\Softwares\\Tencent\\Weixin\\Weixin.exe",
|
||||||
"WechatSid": "AAHhwahUM3etjBpBQ-qeuaAZ9RF_NLJRg-ljztn_3qLcCQ",
|
"WechatSid": "AAHhwahUM3etjBpBQ-qeuaAZ9RF_NLJRg-ljztn_3qLcCQ",
|
||||||
"WechatApiVersion": "7.10.2",
|
"WechatApiVersion": "7.10.2",
|
||||||
|
|||||||
@ -1,16 +1 @@
|
|||||||
[
|
[]
|
||||||
{
|
|
||||||
"OrderId": "202605070105294312",
|
|
||||||
"PayId": "2026050701002863867",
|
|
||||||
"Param": "MBSI70BBVRCGHU0K",
|
|
||||||
"PayType": 2,
|
|
||||||
"Price": 0.1,
|
|
||||||
"ReallyPrice": 0.1,
|
|
||||||
"TimeOut": 5,
|
|
||||||
"State": 1,
|
|
||||||
"Date": 1778086829,
|
|
||||||
"RegisteredAt": "2026-05-07T01:00:50.9448888+08:00",
|
|
||||||
"CompletedAt": "2026-05-07T01:00:51.3503482+08:00",
|
|
||||||
"TradeNo": "2026050722001409341411855749"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
70
bin/Inno编译文件.iss
Normal file
70
bin/Inno编译文件.iss
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
; --------------------------------------------------------
|
||||||
|
; V免签PC监听程序 - Inno Setup 完整脚本
|
||||||
|
; 编译环境:.NET 10.0 Windows
|
||||||
|
; 打包模式:生成解决方案 (Release)
|
||||||
|
; --------------------------------------------------------
|
||||||
|
|
||||||
|
#define MyAppName "V免签PC监听程序"
|
||||||
|
#define MyAppVersion "0.0.1"
|
||||||
|
#define MyAppPublisher "扫地僧"
|
||||||
|
#define MyAppURL "https://www.yunzer.cn/"
|
||||||
|
#define MyAppExeName "Vmianqian.exe"
|
||||||
|
#define MyAppAssocName MyAppName + "文件"
|
||||||
|
#define MyAppAssocExt ".exe"
|
||||||
|
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; AppId 保持不变,确保版本覆盖正常
|
||||||
|
AppId={{D8540A89-49BC-4843-9CE7-A96496DFB2DB}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
AppPublisherURL={#MyAppURL}
|
||||||
|
AppSupportURL={#MyAppURL}
|
||||||
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
|
||||||
|
; 默认安装路径
|
||||||
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
|
|
||||||
|
; --- 核心设置:监听程序必须以管理员权限运行 ---
|
||||||
|
PrivilegesRequired=admin
|
||||||
|
|
||||||
|
; 架构支持
|
||||||
|
ArchitecturesAllowed=x64compatible
|
||||||
|
ArchitecturesInstallIn64BitMode=x64compatible
|
||||||
|
ChangesAssociations=yes
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
|
||||||
|
; 输出设置
|
||||||
|
OutputBaseFilename=V免签监听程序_安装包
|
||||||
|
; 请确保此 .ico 文件路径正确
|
||||||
|
SetupIconFile=E:\Demo\C\Vmianqian\logo.ico
|
||||||
|
SolidCompression=yes
|
||||||
|
WizardStyle=modern dynamic
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
; --- 核心修正:直接指向 Release 生成目录,并打包目录下所有文件 ---
|
||||||
|
; recursesubdirs 确保打包子目录(如 runtimes 目录,这对 .NET 程序至关重要)
|
||||||
|
Source: "E:\Demo\C\Vmianqian\bin\Release\net10.0-windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
|
|
||||||
|
[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}"; IconFilename: "{app}\{#MyAppExeName}"
|
||||||
|
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; IconFilename: "{app}\{#MyAppExeName}"
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||||
22
build-upgrade-package.bat
Normal file
22
build-upgrade-package.bat
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Build upgrade package
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
powershell -ExecutionPolicy Bypass -File ".\package-incremental.ps1"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Package build failed.
|
||||||
|
) else (
|
||||||
|
echo Package build completed.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
@ -12,11 +12,11 @@ using System.Reflection;
|
|||||||
|
|
||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Vmianqian")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Vmianqian")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("0.0.3.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+6fb40a7689b5db8902ca50394f29517e5bdc4108")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("0.0.3+4a5dd2418b610b8c830d439dca19d71d132d86c7")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Vmianqian")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Vmianqian")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Vmianqian")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Vmianqian")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("0.0.3.0")]
|
||||||
[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
|
[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
|
||||||
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
|
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
5d9c659649f82a6db3033ce25ce5f2cd30abeb96072a886181b61b685af63a8c
|
3d204494fbae9c9a1df67d0bd1dba87fdd9a76e6167861891cb9d29bd6d54027
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
a200812809a6c12e0e5a805bae74d188c7782b566b9e1e93bf9af5062674a65d
|
24dcc0ae9e7bdc30cfe6b63b0849f0d4b1f637666c5ea893cbac6a9564f6e43d
|
||||||
|
|||||||
@ -77,3 +77,6 @@ E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.Wpf.dll
|
|||||||
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.Core.xml
|
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.Core.xml
|
||||||
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.WinForms.xml
|
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.WinForms.xml
|
||||||
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.Wpf.xml
|
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\Microsoft.Web.WebView2.Wpf.xml
|
||||||
|
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\System.Management.dll
|
||||||
|
E:\Demo\C\Vmianqian\bin\Debug\net10.0-windows\runtimes\win\lib\net9.0\System.Management.dll
|
||||||
|
E:\Demo\C\Vmianqian\obj\Debug\net10.0-windows\Vmianqian.Properties.Resources.resources
|
||||||
|
|||||||
Binary file not shown.
@ -79,6 +79,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Management/9.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net9.0/System.Management.dll": {
|
||||||
|
"assemblyVersion": "9.0.0.0",
|
||||||
|
"fileVersion": "9.0.24.52809"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeTargets": {
|
||||||
|
"runtimes/win/lib/net9.0/System.Management.dll": {
|
||||||
|
"rid": "win",
|
||||||
|
"assetType": "runtime",
|
||||||
|
"assemblyVersion": "9.0.0.0",
|
||||||
|
"fileVersion": "9.0.24.52809"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Titanium.Web.Proxy/3.2.0": {
|
"Titanium.Web.Proxy/3.2.0": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"BrotliSharpLib": "0.3.3",
|
"BrotliSharpLib": "0.3.3",
|
||||||
@ -143,6 +159,13 @@
|
|||||||
"path": "portable.bouncycastle/1.8.8",
|
"path": "portable.bouncycastle/1.8.8",
|
||||||
"hashPath": "portable.bouncycastle.1.8.8.nupkg.sha512"
|
"hashPath": "portable.bouncycastle.1.8.8.nupkg.sha512"
|
||||||
},
|
},
|
||||||
|
"System.Management/9.0.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-bVh4xAMI5grY5GZoklKcMBLirhC8Lqzp63Ft3zXJacwGAlLyFdF4k0qz4pnKIlO6HyL2Z4zqmHm9UkzEo6FFsA==",
|
||||||
|
"path": "system.management/9.0.0",
|
||||||
|
"hashPath": "system.management.9.0.0.nupkg.sha512"
|
||||||
|
},
|
||||||
"Titanium.Web.Proxy/3.2.0": {
|
"Titanium.Web.Proxy/3.2.0": {
|
||||||
"type": "package",
|
"type": "package",
|
||||||
"serviceable": true,
|
"serviceable": true,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user