Compare commits
3 Commits
9fe8ab746a
...
v1.3.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da024fb3fb | ||
|
|
a5a04aaab7 | ||
|
|
c846d11efa |
@@ -11,6 +11,8 @@
|
|||||||
- [jpeg v3.1.1](https://github.com/libjpeg-turbo/libjpeg-turbo)
|
- [jpeg v3.1.1](https://github.com/libjpeg-turbo/libjpeg-turbo)
|
||||||
- [opus-1.6.1](https://opus-codec.org/release/stable/2026/01/14/libopus-1_6_1.html)
|
- [opus-1.6.1](https://opus-codec.org/release/stable/2026/01/14/libopus-1_6_1.html)
|
||||||
- [libpeconv c7d1e48](https://github.com/hasherezade/libpeconv)
|
- [libpeconv c7d1e48](https://github.com/hasherezade/libpeconv)
|
||||||
|
- [libvpl v2.16.0](https://github.com/intel/libvpl)
|
||||||
|
- [dav1d 62501cc](https://github.com/videolan/dav1d)
|
||||||
|
|
||||||
## execution
|
## execution
|
||||||
|
|
||||||
|
|||||||
29
ReadMe.md
29
ReadMe.md
@@ -357,6 +357,35 @@ nohup ./server_linux_amd64 --port 6543 --http-port 9001 > yama.log 2>&1 &
|
|||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
|
### v1.3.5 (2026.5.31)
|
||||||
|
|
||||||
|
**硬件编码扩展(H.264 / AV1)& 多客户许可证生产化 & FRP 子级自动化**
|
||||||
|
|
||||||
|
**新功能:**
|
||||||
|
- **客户端硬件编码**:新增 FFmpeg 路径的 `CFFmpegH264Encoder` / `CFFmpegAV1Encoder`,可调用 NVENC / Quick Sync / AMF 等 GPU 编码器;`EncoderFactory` 运行时自动优选
|
||||||
|
- **静屏跳编码**:捕获层比对前后帧,完全相同时跳过编码与传输——硬件编码器在静屏不再被强行喂入相同帧
|
||||||
|
- **菜单驱动的压缩 / 解压**:自定义文件 + 文件夹选择器(`ZstaPickerDlg`),可从远程主机直接选混合目录树打包或解压到目标路径
|
||||||
|
- **下级主控自动起 frp client**:上级签发 V2 授权时一并下发 frp 配置,子级主控启动即接通中继链路,无需人工配 `frpc.toml`
|
||||||
|
- **合规可裁剪构建**:`DISABLE_X264` / `DISABLE_FFMPEG` 编译开关,可在不动源码的前提下产出完全不带 x264 / FFmpeg 的二进制,配套 `LICENSE-THIRD-PARTY.txt`
|
||||||
|
|
||||||
|
**改进:**
|
||||||
|
- **多客户许可证服务端硬化**:`licenses.ini` hot-path 互斥锁 + 30s 节流,写频从 0.6 → 0.07 次/秒(外推 100 在线:~160 → ~3.3 次/秒);闭环了"预设续期配额消失"的 read-modify-write 竞态
|
||||||
|
- **`licenses.ini` IP 列表 4KB 截断修复**:分段写入避免溢出尾部被永久丢弃
|
||||||
|
- **导入 SN 按 `BindType` 严格校验**:避免离线版 / 在线版 / 试用版 SN 串库
|
||||||
|
- **客户端 SCLoader 大瘦身**:移除一万行硬编码 stub(`SCLoader.cpp`),改用主控运行时下发 DLL 注入
|
||||||
|
- **客户端 logger 优雅退出**:进程退出刷出队列里的日志并记录退出信号
|
||||||
|
- **IOCPClient 早期数据包防护**:`setManagerCallBack` 之前抵达的包不再触发空回调崩溃
|
||||||
|
- **多显示器光标位置修正 & MJPEG 录制翻转修复**:trace cursor 跨屏坐标系修正;MJPEG 上下颠倒回放修正 + 编码失败 0 字节 AVI 残留清理
|
||||||
|
- **FRP `privilegeKey` 改用 UTC 时间戳**:跨时区主控 / 中继 / 客户端不再因本地时区让 frp auth 失效
|
||||||
|
- **Linux 客户端 `install.sh` / `uninstall.sh`**:补齐一键部署 / 卸载脚本
|
||||||
|
- **Go 服务端构建管线**:`build.ps1` / `build.cmd` 把 Go 主控纳入主构建
|
||||||
|
- **Release / Download 链接全量迁移到 Gitea**:v1.3.4+ 不再发到 GitHub
|
||||||
|
|
||||||
|
**Bug 修复:**
|
||||||
|
- Web 文件管理触屏双击不稳:触摸阈值放宽防误判拖拽 + 两次 `click` 模拟物理双击;修复跨平台文件夹重命名 / 点击无响应
|
||||||
|
- 向 sub-master 发送 AUTH 时密码生成路径错误,下级始终认证失败
|
||||||
|
- 试用 SN 误进入 V2 / V1 授权下发分支
|
||||||
|
|
||||||
### v1.3.4 (2026.5.20)
|
### v1.3.4 (2026.5.20)
|
||||||
|
|
||||||
**Go 主控 & 全平台主控闭环 & Linux/macOS 客户端剪贴板**
|
**Go 主控 & 全平台主控闭环 & Linux/macOS 客户端剪贴板**
|
||||||
|
|||||||
29
ReadMe_EN.md
29
ReadMe_EN.md
@@ -357,6 +357,35 @@ Valid : 2026-02-01 to 2028-02-01
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v1.3.5 (2026.5.31)
|
||||||
|
|
||||||
|
**Hardware encoding expansion (H.264 / AV1) & multi-tenant license hardening & FRP sub-master automation**
|
||||||
|
|
||||||
|
**New features:**
|
||||||
|
- **Client hardware encoding**: new `CFFmpegH264Encoder` / `CFFmpegAV1Encoder` on the FFmpeg path, driving NVENC / Quick Sync / AMF GPU encoders; `EncoderFactory` picks the best available encoder at runtime
|
||||||
|
- **Skip-encode on identical frames**: capture layer compares consecutive frames and skips both encode and transmit when the picture is static — hardware encoders no longer get fed duplicate frames during idle desktops
|
||||||
|
- **Menu-driven compress / extract**: custom file + folder picker (`ZstaPickerDlg`) lets you select a mixed directory tree on the remote host to zip up, or extract an archive to a target path
|
||||||
|
- **Auto-launch frp client for sub-masters**: when upstream issues a V2 license, frp config is shipped alongside it; the sub-master connects to the relay automatically with no manual `frpc.toml`
|
||||||
|
- **Compliance-tailorable build**: `DISABLE_X264` / `DISABLE_FFMPEG` build flags produce binaries with zero x264 / FFmpeg dependency without touching source; paired with `LICENSE-THIRD-PARTY.txt`
|
||||||
|
|
||||||
|
**Improvements:**
|
||||||
|
- **Multi-tenant license server hardening**: `licenses.ini` hot path now has a recursive mutex + 30s throttle; write rate dropped from 0.6 → 0.07/sec (extrapolated to 100 online targets: ~160 → ~3.3 writes/sec). Closes the read-modify-write race that caused "preset renewal quota silently disappears"
|
||||||
|
- **`licenses.ini` IP list 4KB truncation fix**: segmented writes prevent the tail of large IP histories from being silently dropped by `WritePrivateProfileString`'s 4KB single-value cap
|
||||||
|
- **`BindType` enforced on SN import**: offline / online / trial SNs can no longer be cross-imported into the wrong bucket
|
||||||
|
- **Client SCLoader slim-down**: removed `SCLoader.cpp` (10K lines of hard-coded stub); the client now uses the DLL delivered by the master at runtime
|
||||||
|
- **Client logger graceful shutdown**: drains queued log lines on exit and records the exit signal — after a restart you still have the last 1-2 seconds of context
|
||||||
|
- **IOCPClient early-packet guard**: packets that arrive before `setManagerCallBack` no longer trigger a null-callback crash (startup race)
|
||||||
|
- **Multi-monitor trace-cursor position fix & MJPEG playback flip fix**: trace cursor coordinates corrected for cross-monitor capture; MJPEG upside-down playback fixed and 0-byte AVI residue removed on encoder-open failure
|
||||||
|
- **FRP `privilegeKey` switched to UTC**: master / relay / client across different time zones no longer reject each other's frp auth because of local-time skew
|
||||||
|
- **Linux client `install.sh` / `uninstall.sh`**: one-shot install / uninstall scripts, on par with macOS
|
||||||
|
- **Go server build pipeline**: `build.ps1` / `build.cmd` now build the Go master as part of the main build
|
||||||
|
- **Release / Download links migrated to Gitea**: v1.3.4+ is no longer published to GitHub
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- Web file manager touch double-click unreliability: move threshold widened to avoid spurious drag detection, plus two sequential `click` events (20ms apart) instead of the non-standard `dblclick` — fixes folder rename / unresponsive clicks on Windows, Linux, and macOS
|
||||||
|
- AUTH packet to sub-master used the wrong password generation path, causing sub-masters to fail authentication every time
|
||||||
|
- Trial SN was being routed through the V2 / V1 license-issue branch
|
||||||
|
|
||||||
### v1.3.4 (2026.5.20)
|
### v1.3.4 (2026.5.20)
|
||||||
|
|
||||||
**Go master & full-platform master loop & Linux/macOS clipboard**
|
**Go master & full-platform master loop & Linux/macOS clipboard**
|
||||||
|
|||||||
29
ReadMe_TW.md
29
ReadMe_TW.md
@@ -357,6 +357,35 @@ nohup ./server_linux_amd64 --port 6543 --http-port 9001 > yama.log 2>&1 &
|
|||||||
|
|
||||||
## 更新日誌
|
## 更新日誌
|
||||||
|
|
||||||
|
### v1.3.5 (2026.5.31)
|
||||||
|
|
||||||
|
**硬體編碼擴充(H.264 / AV1)& 多客戶授權生產化 & FRP 子級自動化**
|
||||||
|
|
||||||
|
**新功能:**
|
||||||
|
- **用戶端硬體編碼**:新增 FFmpeg 路徑的 `CFFmpegH264Encoder` / `CFFmpegAV1Encoder`,可呼叫 NVENC / Quick Sync / AMF 等 GPU 編碼器;`EncoderFactory` 執行時自動優選
|
||||||
|
- **靜畫跳編碼**:擷取層比對前後影格,完全相同時跳過編碼與傳輸——硬體編碼器在靜畫時不再被強行餵入相同影格
|
||||||
|
- **選單驅動的壓縮 / 解壓**:自訂檔案 + 資料夾選擇器(`ZstaPickerDlg`),可從遠端主機直接選混合目錄樹打包或解壓到目標路徑
|
||||||
|
- **下級主控自動啟動 frp client**:上級簽發 V2 授權時一併下發 frp 設定,子級主控啟動即接通中繼鏈路,無需人工設定 `frpc.toml`
|
||||||
|
- **合規可裁剪建置**:`DISABLE_X264` / `DISABLE_FFMPEG` 編譯開關,可在不動原始碼的前提下產出完全不含 x264 / FFmpeg 的二進位,搭配 `LICENSE-THIRD-PARTY.txt`
|
||||||
|
|
||||||
|
**改進:**
|
||||||
|
- **多客戶授權伺服端硬化**:`licenses.ini` hot-path 互斥鎖 + 30s 節流,寫入頻率從 0.6 → 0.07 次/秒(外推 100 在線:~160 → ~3.3 次/秒);閉環「預設續期配額消失」的 read-modify-write 競態
|
||||||
|
- **`licenses.ini` IP 清單 4KB 截斷修復**:分段寫入避免溢出尾部被永久丟棄
|
||||||
|
- **匯入 SN 按 `BindType` 嚴格校驗**:避免離線版 / 連線版 / 試用版 SN 串庫
|
||||||
|
- **用戶端 SCLoader 大瘦身**:移除一萬行硬編碼 stub(`SCLoader.cpp`),改用主控執行時下發 DLL 注入
|
||||||
|
- **用戶端 logger 優雅退出**:程序結束時刷出佇列裡的日誌並記錄退出訊號
|
||||||
|
- **IOCPClient 早期封包防護**:`setManagerCallBack` 之前抵達的封包不再觸發空回呼崩潰
|
||||||
|
- **多顯示器游標位置修正 & MJPEG 錄製翻轉修復**:trace cursor 跨螢幕座標系修正;MJPEG 上下顛倒回放修正 + 編碼失敗 0 位元組 AVI 殘留清理
|
||||||
|
- **FRP `privilegeKey` 改用 UTC 時間戳**:跨時區主控 / 中繼 / 用戶端不再因本地時區讓 frp auth 失效
|
||||||
|
- **Linux 用戶端 `install.sh` / `uninstall.sh`**:補齊一鍵部署 / 解除安裝指令稿
|
||||||
|
- **Go 伺服端建置管線**:`build.ps1` / `build.cmd` 把 Go 主控納入主建置流程
|
||||||
|
- **Release / Download 連結全面遷移到 Gitea**:v1.3.4+ 不再發行到 GitHub
|
||||||
|
|
||||||
|
**Bug 修復:**
|
||||||
|
- Web 檔案管理觸控雙擊不穩:觸控閾值放寬避免誤判拖曳 + 兩次 `click` 模擬實體雙擊;修復跨平台資料夾重新命名 / 點擊無回應
|
||||||
|
- 向 sub-master 發送 AUTH 時密碼產生路徑錯誤,下級始終認證失敗
|
||||||
|
- 試用 SN 誤進入 V2 / V1 授權下發分支
|
||||||
|
|
||||||
### v1.3.4 (2026.5.20)
|
### v1.3.4 (2026.5.20)
|
||||||
|
|
||||||
**Go 主控 & 全平台主控閉環 & Linux/macOS 用戶端剪貼簿**
|
**Go 主控 & 全平台主控閉環 & Linux/macOS 用戶端剪貼簿**
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ IDR_WAVE WAVE "Res\\msg.wav"
|
|||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,0,3,4
|
FILEVERSION 1,0,3,5
|
||||||
PRODUCTVERSION 1,0,0,1
|
PRODUCTVERSION 1,0,0,1
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@@ -106,7 +106,7 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "FUCK THE UNIVERSE"
|
VALUE "CompanyName", "FUCK THE UNIVERSE"
|
||||||
VALUE "FileDescription", "A GHOST"
|
VALUE "FileDescription", "A GHOST"
|
||||||
VALUE "FileVersion", "1.0.3.4"
|
VALUE "FileVersion", "1.0.3.5"
|
||||||
VALUE "InternalName", "ServerDll.dll"
|
VALUE "InternalName", "ServerDll.dll"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2019-2026"
|
VALUE "LegalCopyright", "Copyright (C) 2019-2026"
|
||||||
VALUE "OriginalFilename", "ServerDll.dll"
|
VALUE "OriginalFilename", "ServerDll.dll"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -637,7 +637,8 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
|
|||||||
m_bmOnline[52].LoadBitmap(IDB_BITMAP_WEBDESKTOP);
|
m_bmOnline[52].LoadBitmap(IDB_BITMAP_WEBDESKTOP);
|
||||||
m_bmOnline[53].LoadBitmap(IDB_BITMAP_PLUGINCONFIG);
|
m_bmOnline[53].LoadBitmap(IDB_BITMAP_PLUGINCONFIG);
|
||||||
m_bmOnline[54].LoadBitmap(IDB_BITMAP_SNAPSHOT); // "播放快照" 菜单的眼睛图标
|
m_bmOnline[54].LoadBitmap(IDB_BITMAP_SNAPSHOT); // "播放快照" 菜单的眼睛图标
|
||||||
|
m_bmOnline[55].LoadBitmap(IDB_BITMAP_COMPRESS);
|
||||||
|
m_bmOnline[56].LoadBitmap(IDB_BITMAP_UNCOMPRESS);
|
||||||
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
|
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
|
||||||
m_ServerDLL[i] = nullptr;
|
m_ServerDLL[i] = nullptr;
|
||||||
m_ServerBin[i] = nullptr;
|
m_ServerBin[i] = nullptr;
|
||||||
@@ -919,6 +920,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
|
|||||||
ON_COMMAND(ID_WEB_REMOTE_CONTROL, &CMy2015RemoteDlg::OnWebRemoteControl)
|
ON_COMMAND(ID_WEB_REMOTE_CONTROL, &CMy2015RemoteDlg::OnWebRemoteControl)
|
||||||
ON_COMMAND(ID_PROXY_PORT_AUTORUN, &CMy2015RemoteDlg::OnProxyPortAutorun)
|
ON_COMMAND(ID_PROXY_PORT_AUTORUN, &CMy2015RemoteDlg::OnProxyPortAutorun)
|
||||||
ON_COMMAND(ID_SCREENPREVIEW_LOOP, &CMy2015RemoteDlg::OnScreenpreviewLoop)
|
ON_COMMAND(ID_SCREENPREVIEW_LOOP, &CMy2015RemoteDlg::OnScreenpreviewLoop)
|
||||||
|
ON_COMMAND(ID_MENU_COMPRESS, &CMy2015RemoteDlg::OnMenuCompress)
|
||||||
|
ON_COMMAND(ID_MENU_UNCOMPRESS, &CMy2015RemoteDlg::OnMenuUncompress)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
|
||||||
@@ -989,6 +992,8 @@ VOID CMy2015RemoteDlg::CreateSolidMenu()
|
|||||||
m_MainMenu.SetMenuItemBitmaps(ID_MAIN_NETWORK, MF_BYCOMMAND, &m_bmOnline[29], &m_bmOnline[29]);
|
m_MainMenu.SetMenuItemBitmaps(ID_MAIN_NETWORK, MF_BYCOMMAND, &m_bmOnline[29], &m_bmOnline[29]);
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_TRIGGER_SETTINGS, MF_BYCOMMAND, &m_bmOnline[51], &m_bmOnline[51]);
|
m_MainMenu.SetMenuItemBitmaps(ID_TRIGGER_SETTINGS, MF_BYCOMMAND, &m_bmOnline[51], &m_bmOnline[51]);
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_MAIN_EXIT, MF_BYCOMMAND, &m_bmOnline[26], &m_bmOnline[26]);
|
m_MainMenu.SetMenuItemBitmaps(ID_MAIN_EXIT, MF_BYCOMMAND, &m_bmOnline[26], &m_bmOnline[26]);
|
||||||
|
m_MainMenu.SetMenuItemBitmaps(ID_MENU_COMPRESS, MF_BYCOMMAND, &m_bmOnline[55], &m_bmOnline[55]);
|
||||||
|
m_MainMenu.SetMenuItemBitmaps(ID_MENU_UNCOMPRESS, MF_BYCOMMAND, &m_bmOnline[56], &m_bmOnline[56]);
|
||||||
// Tools menu
|
// Tools menu
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_INPUT_PASSWORD, MF_BYCOMMAND, &m_bmOnline[30], &m_bmOnline[30]);
|
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_INPUT_PASSWORD, MF_BYCOMMAND, &m_bmOnline[30], &m_bmOnline[30]);
|
||||||
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_IMPORT_LICENSE, MF_BYCOMMAND, &m_bmOnline[31], &m_bmOnline[31]);
|
m_MainMenu.SetMenuItemBitmaps(ID_TOOL_IMPORT_LICENSE, MF_BYCOMMAND, &m_bmOnline[31], &m_bmOnline[31]);
|
||||||
@@ -1891,7 +1896,8 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
|
|||||||
Mprintf("[WebService] Admin password configured from %s\n",
|
Mprintf("[WebService] Admin password configured from %s\n",
|
||||||
(webPassEnv && *webPassEnv) ? BRAND_WEB_ENV_VAR : BRAND_ENV_VAR);
|
(webPassEnv && *webPassEnv) ? BRAND_WEB_ENV_VAR : BRAND_ENV_VAR);
|
||||||
} else {
|
} else {
|
||||||
Mprintf("[WebService] Warning: neither %s nor %s set, web login disabled\n",
|
WebService().SetAdminPassword("admin");
|
||||||
|
Mprintf("[WebService] Warning: neither %s nor %s set! Use 'admin' as password\n",
|
||||||
BRAND_WEB_ENV_VAR, BRAND_ENV_VAR);
|
BRAND_WEB_ENV_VAR, BRAND_ENV_VAR);
|
||||||
}
|
}
|
||||||
// HideWebSessions: 1=hide (default), 0=show (for debugging)
|
// HideWebSessions: 1=hide (default), 0=show (for debugging)
|
||||||
@@ -10837,7 +10843,8 @@ void CMy2015RemoteDlg::OnWebRemoteControl()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (m_superPass.empty()) {
|
else if (m_superPass.empty()) {
|
||||||
MessageBoxL("请设置环境变量 " BRAND_ENV_VAR " 来使用Web远程桌面!", "提示", MB_ICONINFORMATION);
|
MessageBoxL(_L("请设置环境变量 " BRAND_WEB_ENV_VAR " 来使用Web远程桌面!") + _L("\n默认密码是: admin")
|
||||||
|
, "提示", MB_ICONINFORMATION);
|
||||||
}else {
|
}else {
|
||||||
MessageBoxL("如需Web远程桌面跨网使用方案,请联系管理员!", "提示", MB_ICONINFORMATION);
|
MessageBoxL("如需Web远程桌面跨网使用方案,请联系管理员!", "提示", MB_ICONINFORMATION);
|
||||||
}
|
}
|
||||||
@@ -10915,3 +10922,112 @@ void CMy2015RemoteDlg::OnScreenpreviewLoop()
|
|||||||
ShowMessage(_TR("提示"), msg);
|
ShowMessage(_TR("提示"), msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== ZSTA 压缩 / 解压 =====
|
||||||
|
#include "ZstdArchive.h"
|
||||||
|
#include "ZstaPickerDlg.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 把一组源路径压缩为单个 .zsta 文件(弹出保存对话框选择输出)
|
||||||
|
// 调用者只负责传路径,输出路径与压缩过程由本函数处理
|
||||||
|
void CompressPathsToZsta(CWnd* parent, const std::vector<std::string>& srcPaths)
|
||||||
|
{
|
||||||
|
if (srcPaths.empty()) return;
|
||||||
|
|
||||||
|
// 默认输出文件名:第一个源的 basename 或 archive
|
||||||
|
std::string defaultName;
|
||||||
|
{
|
||||||
|
std::string first = srcPaths[0];
|
||||||
|
while (!first.empty() && (first.back() == '/' || first.back() == '\\'))
|
||||||
|
first.pop_back();
|
||||||
|
size_t pos = first.find_last_of("/\\");
|
||||||
|
defaultName = (pos == std::string::npos) ? first : first.substr(pos + 1);
|
||||||
|
if (srcPaths.size() > 1) defaultName = "archive";
|
||||||
|
defaultName += ".zsta";
|
||||||
|
}
|
||||||
|
|
||||||
|
CFileDialog saveDlg(FALSE, _T("zsta"), CString(defaultName.c_str()),
|
||||||
|
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
|
||||||
|
_T("ZSTA Files (*.zsta)|*.zsta|All Files (*.*)|*.*||"),
|
||||||
|
parent);
|
||||||
|
CString saveTitle = _TR("选择压缩输出文件");
|
||||||
|
saveDlg.m_ofn.lpstrTitle = saveTitle;
|
||||||
|
if (saveDlg.DoModal() != IDOK) return;
|
||||||
|
|
||||||
|
std::string outPath = std::string(CT2A(saveDlg.GetPathName().GetString()));
|
||||||
|
auto err = zsta::CZstdArchive::Compress(srcPaths, outPath, 3);
|
||||||
|
if (err == zsta::Error::Success) {
|
||||||
|
Mprintf("ZSTA 压缩成功: %u 项 -> %s\n",
|
||||||
|
(unsigned)srcPaths.size(), outPath.c_str());
|
||||||
|
CString msg;
|
||||||
|
msg.FormatL("压缩成功 (%u 项):\n%s",
|
||||||
|
(unsigned)srcPaths.size(), outPath.c_str());
|
||||||
|
parent->MessageBox(msg, _TR("提示"), MB_OK | MB_ICONINFORMATION);
|
||||||
|
} else {
|
||||||
|
Mprintf("ZSTA 压缩失败 [%s]: -> %s\n",
|
||||||
|
zsta::CZstdArchive::GetErrorString(err), outPath.c_str());
|
||||||
|
CString msg;
|
||||||
|
msg.FormatL("压缩失败: %s", zsta::CZstdArchive::GetErrorString(err));
|
||||||
|
parent->MessageBox(msg, _TR("错误"), MB_OK | MB_ICONERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnMenuCompress()
|
||||||
|
{
|
||||||
|
CZstaPickerDlg picker(this);
|
||||||
|
if (picker.DoModal() != IDOK) return;
|
||||||
|
if (picker.m_Paths.empty()) {
|
||||||
|
MessageBoxL("未选择任何文件或文件夹", "提示", MB_OK | MB_ICONINFORMATION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompressPathsToZsta(this, picker.m_Paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMy2015RemoteDlg::OnMenuUncompress()
|
||||||
|
{
|
||||||
|
const DWORD MAX_BUF = 64 * 1024;
|
||||||
|
std::vector<TCHAR> buf(MAX_BUF, 0);
|
||||||
|
CFileDialog dlg(TRUE, _T("zsta"), NULL,
|
||||||
|
OFN_ALLOWMULTISELECT | OFN_EXPLORER |
|
||||||
|
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST,
|
||||||
|
_T("ZSTA Files (*.zsta)|*.zsta|All Files (*.*)|*.*||"), this);
|
||||||
|
dlg.m_ofn.lpstrFile = buf.data();
|
||||||
|
dlg.m_ofn.nMaxFile = MAX_BUF;
|
||||||
|
CString title = _TR("请选择要解压的 ZSTA 文件 (可多选)");
|
||||||
|
dlg.m_ofn.lpstrTitle = title;
|
||||||
|
if (dlg.DoModal() != IDOK) return;
|
||||||
|
|
||||||
|
int ok = 0, fail = 0;
|
||||||
|
POSITION pos = dlg.GetStartPosition();
|
||||||
|
while (pos) {
|
||||||
|
CString cpath = dlg.GetNextPathName(pos);
|
||||||
|
std::string src(CT2A(cpath.GetString()));
|
||||||
|
|
||||||
|
// 目标目录:去掉 .zsta 后缀;若无该后缀则追加 _extract
|
||||||
|
std::string dst;
|
||||||
|
if (src.size() >= 5) {
|
||||||
|
std::string ext = src.substr(src.size() - 5);
|
||||||
|
for (char& c : ext) c = (char)tolower((unsigned char)c);
|
||||||
|
if (ext == ".zsta") dst = src.substr(0, src.size() - 5);
|
||||||
|
}
|
||||||
|
if (dst.empty()) dst = src + "_extract";
|
||||||
|
|
||||||
|
auto err = zsta::CZstdArchive::Extract(src, dst);
|
||||||
|
if (err == zsta::Error::Success) {
|
||||||
|
++ok;
|
||||||
|
Mprintf("ZSTA 解压成功: %s -> %s\n", src.c_str(), dst.c_str());
|
||||||
|
} else {
|
||||||
|
++fail;
|
||||||
|
Mprintf("ZSTA 解压失败 [%s]: %s\n",
|
||||||
|
zsta::CZstdArchive::GetErrorString(err), src.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CString msg;
|
||||||
|
msg.FormatL("解压完成: 成功 %d, 失败 %d", ok, fail);
|
||||||
|
MessageBox(msg, _TR("提示"),
|
||||||
|
MB_OK | (fail > 0 ? MB_ICONWARNING : MB_ICONINFORMATION));
|
||||||
|
}
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ public:
|
|||||||
bool IsDllRequestLimited(const std::string& ip);
|
bool IsDllRequestLimited(const std::string& ip);
|
||||||
void RecordDllRequest(const std::string& ip);
|
void RecordDllRequest(const std::string& ip);
|
||||||
CMenu m_MainMenu;
|
CMenu m_MainMenu;
|
||||||
CBitmap m_bmOnline[55]; // 21 original + 4 context menu + 2 tray menu + 23 main menu + 3 new menu icons + 1 snapshot
|
CBitmap m_bmOnline[57]; // 21 original + 4 context menu + 2 tray menu + 25 main menu + 3 new menu icons + 1 snapshot
|
||||||
uint64_t m_superID;
|
uint64_t m_superID;
|
||||||
std::map<HWND, CDialogBase *> m_RemoteWnds;
|
std::map<HWND, CDialogBase *> m_RemoteWnds;
|
||||||
FileTransformCmd m_CmdList;
|
FileTransformCmd m_CmdList;
|
||||||
@@ -604,4 +604,6 @@ public:
|
|||||||
afx_msg void OnWebRemoteControl();
|
afx_msg void OnWebRemoteControl();
|
||||||
afx_msg void OnProxyPortAutorun();
|
afx_msg void OnProxyPortAutorun();
|
||||||
afx_msg void OnScreenpreviewLoop();
|
afx_msg void OnScreenpreviewLoop();
|
||||||
|
afx_msg void OnMenuCompress();
|
||||||
|
afx_msg void OnMenuUncompress();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -334,6 +334,7 @@
|
|||||||
<ClInclude Include="HideScreenSpyDlg.h" />
|
<ClInclude Include="HideScreenSpyDlg.h" />
|
||||||
<ClInclude Include="HostInfo.h" />
|
<ClInclude Include="HostInfo.h" />
|
||||||
<ClInclude Include="InputDlg.h" />
|
<ClInclude Include="InputDlg.h" />
|
||||||
|
<ClInclude Include="ZstaPickerDlg.h" />
|
||||||
<ClInclude Include="IOCPKCPServer.h" />
|
<ClInclude Include="IOCPKCPServer.h" />
|
||||||
<ClInclude Include="IOCPServer.h" />
|
<ClInclude Include="IOCPServer.h" />
|
||||||
<ClInclude Include="IOCPUDPServer.h" />
|
<ClInclude Include="IOCPUDPServer.h" />
|
||||||
@@ -459,6 +460,7 @@
|
|||||||
<ClCompile Include="file\CFileTransferModeDlg.cpp" />
|
<ClCompile Include="file\CFileTransferModeDlg.cpp" />
|
||||||
<ClCompile Include="HideScreenSpyDlg.cpp" />
|
<ClCompile Include="HideScreenSpyDlg.cpp" />
|
||||||
<ClCompile Include="InputDlg.cpp" />
|
<ClCompile Include="InputDlg.cpp" />
|
||||||
|
<ClCompile Include="ZstaPickerDlg.cpp" />
|
||||||
<ClCompile Include="IOCPKCPServer.cpp" />
|
<ClCompile Include="IOCPKCPServer.cpp" />
|
||||||
<ClCompile Include="IOCPServer.cpp" />
|
<ClCompile Include="IOCPServer.cpp" />
|
||||||
<ClCompile Include="IOCPUDPServer.cpp" />
|
<ClCompile Include="IOCPUDPServer.cpp" />
|
||||||
@@ -525,7 +527,9 @@
|
|||||||
<Image Include="res\Bitmap\AuthGen.bmp" />
|
<Image Include="res\Bitmap\AuthGen.bmp" />
|
||||||
<Image Include="res\Bitmap\authorize.bmp" />
|
<Image Include="res\Bitmap\authorize.bmp" />
|
||||||
<Image Include="res\Bitmap\Backup.bmp" />
|
<Image Include="res\Bitmap\Backup.bmp" />
|
||||||
|
<Image Include="res\bitmap\bitmap9.bmp" />
|
||||||
<Image Include="res\Bitmap\CancelShare.bmp" />
|
<Image Include="res\Bitmap\CancelShare.bmp" />
|
||||||
|
<Image Include="res\bitmap\compress.bmp" />
|
||||||
<Image Include="res\Bitmap\delete.bmp" />
|
<Image Include="res\Bitmap\delete.bmp" />
|
||||||
<Image Include="res\Bitmap\DxgiDesktop.bmp" />
|
<Image Include="res\Bitmap\DxgiDesktop.bmp" />
|
||||||
<Image Include="res\Bitmap\EditGroup.bmp" />
|
<Image Include="res\Bitmap\EditGroup.bmp" />
|
||||||
@@ -569,6 +573,7 @@
|
|||||||
<Image Include="res\Bitmap\Trial.bmp" />
|
<Image Include="res\Bitmap\Trial.bmp" />
|
||||||
<Image Include="res\Bitmap\Trigger.bmp" />
|
<Image Include="res\Bitmap\Trigger.bmp" />
|
||||||
<Image Include="res\Bitmap\unauthorize.bmp" />
|
<Image Include="res\Bitmap\unauthorize.bmp" />
|
||||||
|
<Image Include="res\bitmap\uncompress.bmp" />
|
||||||
<Image Include="res\Bitmap\update.bmp" />
|
<Image Include="res\Bitmap\update.bmp" />
|
||||||
<Image Include="res\Bitmap\VirtualDesktop.bmp" />
|
<Image Include="res\Bitmap\VirtualDesktop.bmp" />
|
||||||
<Image Include="res\Bitmap\Wallet.bmp" />
|
<Image Include="res\Bitmap\Wallet.bmp" />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<ClCompile Include="FileTransferModeDlg.cpp" />
|
<ClCompile Include="FileTransferModeDlg.cpp" />
|
||||||
<ClCompile Include="HideScreenSpyDlg.cpp" />
|
<ClCompile Include="HideScreenSpyDlg.cpp" />
|
||||||
<ClCompile Include="InputDlg.cpp" />
|
<ClCompile Include="InputDlg.cpp" />
|
||||||
|
<ClCompile Include="ZstaPickerDlg.cpp" />
|
||||||
<ClCompile Include="IOCPServer.cpp" />
|
<ClCompile Include="IOCPServer.cpp" />
|
||||||
<ClCompile Include="KeyBoardDlg.cpp" />
|
<ClCompile Include="KeyBoardDlg.cpp" />
|
||||||
<ClCompile Include="Loader.c" />
|
<ClCompile Include="Loader.c" />
|
||||||
@@ -107,6 +108,7 @@
|
|||||||
<ClInclude Include="FileTransferModeDlg.h" />
|
<ClInclude Include="FileTransferModeDlg.h" />
|
||||||
<ClInclude Include="HideScreenSpyDlg.h" />
|
<ClInclude Include="HideScreenSpyDlg.h" />
|
||||||
<ClInclude Include="InputDlg.h" />
|
<ClInclude Include="InputDlg.h" />
|
||||||
|
<ClInclude Include="ZstaPickerDlg.h" />
|
||||||
<ClInclude Include="IOCPServer.h" />
|
<ClInclude Include="IOCPServer.h" />
|
||||||
<ClInclude Include="KeyBoardDlg.h" />
|
<ClInclude Include="KeyBoardDlg.h" />
|
||||||
<ClInclude Include="proxy\HPSocket.h" />
|
<ClInclude Include="proxy\HPSocket.h" />
|
||||||
@@ -275,6 +277,9 @@
|
|||||||
<Image Include="res\Bitmap\Trigger.bmp" />
|
<Image Include="res\Bitmap\Trigger.bmp" />
|
||||||
<Image Include="res\Bitmap\WebDesktop.bmp" />
|
<Image Include="res\Bitmap\WebDesktop.bmp" />
|
||||||
<Image Include="res\Bitmap\PluginConfig.bmp" />
|
<Image Include="res\Bitmap\PluginConfig.bmp" />
|
||||||
|
<Image Include="res\bitmap\bitmap9.bmp" />
|
||||||
|
<Image Include="res\bitmap\compress.bmp" />
|
||||||
|
<Image Include="res\bitmap\uncompress.bmp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\Release\ghost.exe" />
|
<None Include="..\..\Release\ghost.exe" />
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
// 程序版本号 [建议格式: X.Y.Z]
|
// 程序版本号 [建议格式: X.Y.Z]
|
||||||
// 影响:关于对话框、标题栏
|
// 影响:关于对话框、标题栏
|
||||||
#define BRAND_VERSION "1.3.4"
|
#define BRAND_VERSION "1.3.5"
|
||||||
|
|
||||||
// 启动画面名称 [建议大写,更有 Logo 感]
|
// 启动画面名称 [建议大写,更有 Logo 感]
|
||||||
// 影响:启动画面 Logo 文字(大号艺术字体渲染)
|
// 影响:启动画面 Logo 文字(大号艺术字体渲染)
|
||||||
|
|||||||
260
server/2015Remote/ZstaPickerDlg.cpp
Normal file
260
server/2015Remote/ZstaPickerDlg.cpp
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "ZstaPickerDlg.h"
|
||||||
|
#include "LangManager.h"
|
||||||
|
#include <shobjidl.h>
|
||||||
|
#include <atlconv.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#define new DEBUG_NEW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool IsDirectoryPath(const std::string& p)
|
||||||
|
{
|
||||||
|
DWORD attr = GetFileAttributesA(p.c_str());
|
||||||
|
return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造一个无控件的 DLGTEMPLATE:cdit=0,控件由 OnInitDialog 动态创建。
|
||||||
|
void BuildDialogTemplate(std::vector<BYTE>& out, LPCWSTR caption,
|
||||||
|
short cx, short cy)
|
||||||
|
{
|
||||||
|
out.clear();
|
||||||
|
auto append = [&](const void* p, size_t n) {
|
||||||
|
const BYTE* b = (const BYTE*)p;
|
||||||
|
out.insert(out.end(), b, b + n);
|
||||||
|
};
|
||||||
|
auto appendW = [&](WORD v) {
|
||||||
|
out.push_back((BYTE)(v & 0xFF));
|
||||||
|
out.push_back((BYTE)((v >> 8) & 0xFF));
|
||||||
|
};
|
||||||
|
auto appendWStr = [&](LPCWSTR s) {
|
||||||
|
size_t n = wcslen(s);
|
||||||
|
append(s, (n + 1) * sizeof(WCHAR));
|
||||||
|
};
|
||||||
|
|
||||||
|
DLGTEMPLATE dt = { 0 };
|
||||||
|
dt.style = DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER |
|
||||||
|
WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME;
|
||||||
|
dt.dwExtendedStyle = 0;
|
||||||
|
dt.cdit = 0;
|
||||||
|
dt.x = 0; dt.y = 0;
|
||||||
|
dt.cx = cx; dt.cy = cy;
|
||||||
|
append(&dt, sizeof(dt));
|
||||||
|
appendW(0); // no menu
|
||||||
|
appendW(0); // default dialog class
|
||||||
|
appendWStr(caption); // caption
|
||||||
|
appendW(8); // font point size
|
||||||
|
appendWStr(L"MS Shell Dlg"); // typeface
|
||||||
|
|
||||||
|
while (out.size() % 4) out.push_back(0); // DWORD align
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
CZstaPickerDlg::CZstaPickerDlg(CWnd* parent)
|
||||||
|
: CDialog((LPCTSTR)NULL, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CZstaPickerDlg, CDialog)
|
||||||
|
ON_BN_CLICKED(IDC_ZSTA_ADDFILES, &CZstaPickerDlg::OnAddFiles)
|
||||||
|
ON_BN_CLICKED(IDC_ZSTA_ADDFOLDERS, &CZstaPickerDlg::OnAddFolders)
|
||||||
|
ON_BN_CLICKED(IDC_ZSTA_REMOVE, &CZstaPickerDlg::OnRemove)
|
||||||
|
ON_WM_SIZE()
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
INT_PTR CZstaPickerDlg::DoModal()
|
||||||
|
{
|
||||||
|
CString title = _TR("选择要压缩的文件 / 文件夹");
|
||||||
|
USES_CONVERSION;
|
||||||
|
BuildDialogTemplate(m_Template, T2CW(title), 360, 220);
|
||||||
|
InitModalIndirect((LPCDLGTEMPLATE)m_Template.data());
|
||||||
|
return CDialog::DoModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CZstaPickerDlg::OnInitDialog()
|
||||||
|
{
|
||||||
|
CDialog::OnInitDialog();
|
||||||
|
|
||||||
|
CRect cli;
|
||||||
|
GetClientRect(&cli);
|
||||||
|
|
||||||
|
// 占位 rect,真正布局在 LayoutControls 里
|
||||||
|
CRect r0(0, 0, 10, 10);
|
||||||
|
|
||||||
|
m_List.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP |
|
||||||
|
LVS_REPORT | LVS_SHOWSELALWAYS,
|
||||||
|
r0, this, IDC_ZSTA_LIST);
|
||||||
|
m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
|
||||||
|
m_List.InsertColumn(0, _TR("类型"), LVCFMT_LEFT, 60);
|
||||||
|
m_List.InsertColumn(1, _TR("路径"), LVCFMT_LEFT, 400);
|
||||||
|
|
||||||
|
auto mkBtn = [&](CButton& b, LPCTSTR text, int id, DWORD extra = 0) {
|
||||||
|
b.Create(text,
|
||||||
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | extra,
|
||||||
|
r0, this, id);
|
||||||
|
};
|
||||||
|
mkBtn(m_BtnAddFiles, _TR("添加文件..."), IDC_ZSTA_ADDFILES);
|
||||||
|
mkBtn(m_BtnAddFolders, _TR("添加文件夹..."), IDC_ZSTA_ADDFOLDERS);
|
||||||
|
mkBtn(m_BtnRemove, _TR("移除选中"), IDC_ZSTA_REMOVE);
|
||||||
|
mkBtn(m_BtnOK, _TR("确定"), IDOK, BS_DEFPUSHBUTTON);
|
||||||
|
mkBtn(m_BtnCancel, _TR("取消"), IDCANCEL);
|
||||||
|
|
||||||
|
// 子控件继承对话框的字体 (DS_SETFONT)
|
||||||
|
HFONT hFont = (HFONT)::SendMessage(GetSafeHwnd(), WM_GETFONT, 0, 0);
|
||||||
|
if (hFont) {
|
||||||
|
m_List.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
m_BtnAddFiles.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
m_BtnAddFolders.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
m_BtnRemove.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
m_BtnOK.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
m_BtnCancel.SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutControls(cli.Width(), cli.Height());
|
||||||
|
RefreshList();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::OnSize(UINT nType, int cx, int cy)
|
||||||
|
{
|
||||||
|
CDialog::OnSize(nType, cx, cy);
|
||||||
|
if (m_List.GetSafeHwnd()) LayoutControls(cx, cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::LayoutControls(int cx, int cy)
|
||||||
|
{
|
||||||
|
const int margin = 10;
|
||||||
|
const int btnW = 120;
|
||||||
|
const int btnH = 26;
|
||||||
|
const int gap = 6;
|
||||||
|
|
||||||
|
int listRight = cx - margin - btnW - margin;
|
||||||
|
if (listRight < margin + 100) listRight = margin + 100;
|
||||||
|
|
||||||
|
m_List.MoveWindow(margin, margin, listRight - margin, cy - margin * 2);
|
||||||
|
|
||||||
|
int x = listRight + margin;
|
||||||
|
int y = margin;
|
||||||
|
m_BtnAddFiles.MoveWindow(x, y, btnW, btnH); y += btnH + gap;
|
||||||
|
m_BtnAddFolders.MoveWindow(x, y, btnW, btnH); y += btnH + gap;
|
||||||
|
m_BtnRemove.MoveWindow(x, y, btnW, btnH);
|
||||||
|
|
||||||
|
int bottomY = cy - margin - btnH;
|
||||||
|
m_BtnCancel.MoveWindow(x, bottomY, btnW, btnH);
|
||||||
|
m_BtnOK.MoveWindow(x, bottomY - btnH - gap, btnW, btnH);
|
||||||
|
|
||||||
|
// 让"路径"列填满剩余宽度
|
||||||
|
if (m_List.GetSafeHwnd()) {
|
||||||
|
CRect lr;
|
||||||
|
m_List.GetClientRect(&lr);
|
||||||
|
int w0 = m_List.GetColumnWidth(0);
|
||||||
|
int w1 = lr.Width() - w0 - GetSystemMetrics(SM_CXVSCROLL) - 4;
|
||||||
|
if (w1 > 100) m_List.SetColumnWidth(1, w1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::AddPath(const std::string& path)
|
||||||
|
{
|
||||||
|
if (path.empty()) return;
|
||||||
|
if (std::find(m_Paths.begin(), m_Paths.end(), path) == m_Paths.end()) {
|
||||||
|
m_Paths.push_back(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::RefreshList()
|
||||||
|
{
|
||||||
|
m_List.DeleteAllItems();
|
||||||
|
for (size_t i = 0; i < m_Paths.size(); ++i) {
|
||||||
|
bool isDir = IsDirectoryPath(m_Paths[i]);
|
||||||
|
m_List.InsertItem((int)i, isDir ? _TR("文件夹") : _TR("文件"));
|
||||||
|
m_List.SetItemText((int)i, 1, CString(m_Paths[i].c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::OnAddFiles()
|
||||||
|
{
|
||||||
|
const DWORD MAX_BUF = 64 * 1024;
|
||||||
|
std::vector<TCHAR> buf(MAX_BUF, 0);
|
||||||
|
CFileDialog dlg(TRUE, NULL, NULL,
|
||||||
|
OFN_ALLOWMULTISELECT | OFN_EXPLORER |
|
||||||
|
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST,
|
||||||
|
_T("All Files (*.*)|*.*||"), this);
|
||||||
|
dlg.m_ofn.lpstrFile = buf.data();
|
||||||
|
dlg.m_ofn.nMaxFile = MAX_BUF;
|
||||||
|
CString title = _TR("选择文件 (可多选)");
|
||||||
|
dlg.m_ofn.lpstrTitle = title;
|
||||||
|
if (dlg.DoModal() != IDOK) return;
|
||||||
|
|
||||||
|
POSITION pos = dlg.GetStartPosition();
|
||||||
|
while (pos) {
|
||||||
|
CString p = dlg.GetNextPathName(pos);
|
||||||
|
AddPath(std::string(CT2A(p.GetString())));
|
||||||
|
}
|
||||||
|
RefreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::OnAddFolders()
|
||||||
|
{
|
||||||
|
IFileOpenDialog* pfd = nullptr;
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL,
|
||||||
|
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
||||||
|
if (FAILED(hr) || !pfd) return;
|
||||||
|
|
||||||
|
DWORD flags = 0;
|
||||||
|
pfd->GetOptions(&flags);
|
||||||
|
pfd->SetOptions(flags | FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT |
|
||||||
|
FOS_PATHMUSTEXIST | FOS_FORCEFILESYSTEM);
|
||||||
|
|
||||||
|
USES_CONVERSION;
|
||||||
|
CString title = _TR("选择文件夹 (可多选)");
|
||||||
|
pfd->SetTitle(T2CW(title));
|
||||||
|
|
||||||
|
if (SUCCEEDED(pfd->Show(GetSafeHwnd()))) {
|
||||||
|
IShellItemArray* psia = nullptr;
|
||||||
|
if (SUCCEEDED(pfd->GetResults(&psia)) && psia) {
|
||||||
|
DWORD count = 0;
|
||||||
|
psia->GetCount(&count);
|
||||||
|
for (DWORD i = 0; i < count; ++i) {
|
||||||
|
IShellItem* psi = nullptr;
|
||||||
|
if (SUCCEEDED(psia->GetItemAt(i, &psi)) && psi) {
|
||||||
|
PWSTR wpath = nullptr;
|
||||||
|
if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &wpath)) && wpath) {
|
||||||
|
int n = WideCharToMultiByte(CP_ACP, 0, wpath, -1,
|
||||||
|
NULL, 0, NULL, NULL);
|
||||||
|
if (n > 1) {
|
||||||
|
std::string s(n - 1, '\0');
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wpath, -1,
|
||||||
|
&s[0], n, NULL, NULL);
|
||||||
|
AddPath(s);
|
||||||
|
}
|
||||||
|
CoTaskMemFree(wpath);
|
||||||
|
}
|
||||||
|
psi->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
psia->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pfd->Release();
|
||||||
|
RefreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CZstaPickerDlg::OnRemove()
|
||||||
|
{
|
||||||
|
std::vector<int> indices;
|
||||||
|
POSITION pos = m_List.GetFirstSelectedItemPosition();
|
||||||
|
while (pos) indices.push_back(m_List.GetNextSelectedItem(pos));
|
||||||
|
|
||||||
|
std::sort(indices.begin(), indices.end(), std::greater<int>());
|
||||||
|
for (int idx : indices) {
|
||||||
|
if (idx >= 0 && idx < (int)m_Paths.size()) {
|
||||||
|
m_Paths.erase(m_Paths.begin() + idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RefreshList();
|
||||||
|
}
|
||||||
52
server/2015Remote/ZstaPickerDlg.h
Normal file
52
server/2015Remote/ZstaPickerDlg.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <afxwin.h>
|
||||||
|
#include <afxcmn.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// 让用户在同一个对话框里累加要压缩的"文件 + 文件夹"组合:
|
||||||
|
// [添加文件...] 调出多选文件对话框
|
||||||
|
// [添加文件夹...] 调出多选文件夹对话框 (IFileOpenDialog + FOS_PICKFOLDERS)
|
||||||
|
// [移除选中] 从列表里删除
|
||||||
|
// DoModal() 返回 IDOK 时,m_Paths 即为结果 (ANSI/MBCS 路径,与 ZSTA 管道一致)。
|
||||||
|
class CZstaPickerDlg : public CDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CZstaPickerDlg(CWnd* parent = nullptr);
|
||||||
|
|
||||||
|
virtual INT_PTR DoModal();
|
||||||
|
|
||||||
|
std::vector<std::string> m_Paths;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual BOOL OnInitDialog();
|
||||||
|
|
||||||
|
afx_msg void OnAddFiles();
|
||||||
|
afx_msg void OnAddFolders();
|
||||||
|
afx_msg void OnRemove();
|
||||||
|
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum CtrlId {
|
||||||
|
IDC_ZSTA_LIST = 1001,
|
||||||
|
IDC_ZSTA_ADDFILES = 1002,
|
||||||
|
IDC_ZSTA_ADDFOLDERS = 1003,
|
||||||
|
IDC_ZSTA_REMOVE = 1004,
|
||||||
|
};
|
||||||
|
|
||||||
|
CListCtrl m_List;
|
||||||
|
CButton m_BtnAddFiles;
|
||||||
|
CButton m_BtnAddFolders;
|
||||||
|
CButton m_BtnRemove;
|
||||||
|
CButton m_BtnOK;
|
||||||
|
CButton m_BtnCancel;
|
||||||
|
|
||||||
|
std::vector<BYTE> m_Template; // in-memory DLGTEMPLATE bytes
|
||||||
|
|
||||||
|
void AddPath(const std::string& path);
|
||||||
|
void RefreshList();
|
||||||
|
void LayoutControls(int cx, int cy);
|
||||||
|
};
|
||||||
@@ -1772,7 +1772,7 @@ FRPS
|
|||||||
Web端口设置无效!\n必须具有有效的授权才能使用Web远程监控!=Web port set failed!\nA valid authorization is required!
|
Web端口设置无效!\n必须具有有效的授权才能使用Web远程监控!=Web port set failed!\nA valid authorization is required!
|
||||||
打开Web远程桌面(&W)=Open Web SimpleRemoter(&W)
|
打开Web远程桌面(&W)=Open Web SimpleRemoter(&W)
|
||||||
请在菜单设置Web端口!=Please set Web liscening port!
|
请在菜单设置Web端口!=Please set Web liscening port!
|
||||||
请设置环境变量 YAMA_PWD 来使用Web远程桌面!=Please set YAMA_PWD to use Web SimpleRemoter!
|
请设置环境变量 YAMA_WEB_ADMIN_PASS 来使用Web远程桌面!=Please set YAMA_WEB_ADMIN_PASS to use Web SimpleRemoter!
|
||||||
如需Web远程桌面跨网使用方案,请联系管理员!=If you need to use Web SimpleRemoter in WAN, please contact administrator!
|
如需Web远程桌面跨网使用方案,请联系管理员!=If you need to use Web SimpleRemoter in WAN, please contact administrator!
|
||||||
; Plugin Settings Dialog - English Translation
|
; Plugin Settings Dialog - English Translation
|
||||||
; Format: Simplified Chinese=English
|
; Format: Simplified Chinese=English
|
||||||
@@ -1894,3 +1894,34 @@ FRPC Զ
|
|||||||
创建AVI文件失败=Create AVI file failed
|
创建AVI文件失败=Create AVI file failed
|
||||||
启用 H264 硬编码=Enable HW H264 Encoding
|
启用 H264 硬编码=Enable HW H264 Encoding
|
||||||
启用 AV1 硬编码=Enable HW AV1 Encoding
|
启用 AV1 硬编码=Enable HW AV1 Encoding
|
||||||
|
; ZSTA Compress / Picker Dialog - English Translation
|
||||||
|
; Format: Simplified Chinese=English
|
||||||
|
|
||||||
|
; --- Picker dialog (CZstaPickerDlg) ---
|
||||||
|
选择要压缩的文件 / 文件夹=Select Files / Folders to Compress
|
||||||
|
添加文件...=Add Files...
|
||||||
|
添加文件夹...=Add Folders...
|
||||||
|
移除选中=Remove Selected
|
||||||
|
类型=Type
|
||||||
|
路径=Path
|
||||||
|
文件=File
|
||||||
|
文件夹=Folder
|
||||||
|
选择文件 (可多选)=Select Files (multi-select)
|
||||||
|
选择文件夹 (可多选)=Select Folders (multi-select)
|
||||||
|
|
||||||
|
; --- Compress / Extract handlers (CMy2015RemoteDlg) ---
|
||||||
|
未选择任何文件或文件夹=No file or folder selected
|
||||||
|
选择压缩输出文件=Choose Output Archive
|
||||||
|
请选择要解压的 ZSTA 文件 (可多选)=Choose ZSTA File(s) to Extract (multi-select)
|
||||||
|
压缩成功 (%u 项):\n%s=Compression succeeded (%u item(s)):\n%s
|
||||||
|
压缩失败: %s=Compression failed: %s
|
||||||
|
解压完成: 成功 %d, 失败 %d=Extraction complete: %d succeeded, %d failed
|
||||||
|
|
||||||
|
; --- Common (likely already present in en_US.ini; included for completeness) ---
|
||||||
|
确定=OK
|
||||||
|
取消=Cancel
|
||||||
|
提示=Notice
|
||||||
|
错误=Error
|
||||||
|
压缩(&C)=&Compress
|
||||||
|
解压缩(&U)=&Uncompress
|
||||||
|
\n默认密码是: admin=\nDefault password is: admin
|
||||||
@@ -1764,7 +1764,7 @@ FRPS
|
|||||||
监听端口和Web服务端口冲突!=监听端口和Web服务端口冲突!
|
监听端口和Web服务端口冲突!=监听端口和Web服务端口冲突!
|
||||||
打开Web远程桌面(&W)=打开Web远程桌面(&W)
|
打开Web远程桌面(&W)=打开Web远程桌面(&W)
|
||||||
请在菜单设置Web端口!=请在菜单设置Web端口!
|
请在菜单设置Web端口!=请在菜单设置Web端口!
|
||||||
请设置环境变量 YAMA_PWD 来使用Web远程桌面!=请设置环境变量 YAMA_PWD 来使用Web远程桌面!
|
请设置环境变量 YAMA_WEB_ADMIN_PASS 来使用Web远程桌面!=请设置环境变量 YAMA_WEB_ADMIN_PASS 来使用Web远程桌面!
|
||||||
如需Web远程桌面跨网使用方案,请联系管理员!=如需Web远程桌面跨网使用方案,请联系管理员!
|
如需Web远程桌面跨网使用方案,请联系管理员!=如需Web远程桌面跨网使用方案,请联系管理员!
|
||||||
; Plugin Settings Dialog - Traditional Chinese Translation
|
; Plugin Settings Dialog - Traditional Chinese Translation
|
||||||
; Format: Simplified Chinese=Traditional Chinese
|
; Format: Simplified Chinese=Traditional Chinese
|
||||||
@@ -1885,3 +1885,34 @@ FRPC Զ
|
|||||||
创建AVI文件失败=创建AVI文件失败
|
创建AVI文件失败=创建AVI文件失败
|
||||||
启用 H264 硬编码=启用 H264 硬编码
|
启用 H264 硬编码=启用 H264 硬编码
|
||||||
启用 AV1 硬编码=启用 AV1 硬编码
|
启用 AV1 硬编码=启用 AV1 硬编码
|
||||||
|
; ZSTA Compress / Picker Dialog - Traditional Chinese Translation
|
||||||
|
; Format: Simplified Chinese=Traditional Chinese
|
||||||
|
|
||||||
|
; --- Picker dialog (CZstaPickerDlg) ---
|
||||||
|
选择要压缩的文件 / 文件夹=選擇要壓縮的檔案 / 資料夾
|
||||||
|
添加文件...=新增檔案...
|
||||||
|
添加文件夹...=新增資料夾...
|
||||||
|
移除选中=移除選取項
|
||||||
|
类型=類型
|
||||||
|
路径=路徑
|
||||||
|
文件=檔案
|
||||||
|
文件夹=資料夾
|
||||||
|
选择文件 (可多选)=選擇檔案 (可多選)
|
||||||
|
选择文件夹 (可多选)=選擇資料夾 (可多選)
|
||||||
|
|
||||||
|
; --- Compress / Extract handlers (CMy2015RemoteDlg) ---
|
||||||
|
未选择任何文件或文件夹=未選擇任何檔案或資料夾
|
||||||
|
选择压缩输出文件=選擇壓縮輸出檔案
|
||||||
|
请选择要解压的 ZSTA 文件 (可多选)=請選擇要解壓縮的 ZSTA 檔案 (可多選)
|
||||||
|
压缩成功 (%u 项):\n%s=壓縮成功 (%u 項):\n%s
|
||||||
|
压缩失败: %s=壓縮失敗: %s
|
||||||
|
解压完成: 成功 %d, 失败 %d=解壓縮完成: 成功 %d, 失敗 %d
|
||||||
|
|
||||||
|
; --- Common (likely already present in zh_TW.ini; included for completeness) ---
|
||||||
|
确定=確定
|
||||||
|
取消=取消
|
||||||
|
提示=提示
|
||||||
|
错误=錯誤
|
||||||
|
压缩(&C)=壓縮(&C)
|
||||||
|
解压缩(&U)=解壓縮(&U)
|
||||||
|
\n默认密码是: admin=\n默认密码是: admin
|
||||||
|
|||||||
BIN
server/2015Remote/res/Bitmap/compress.bmp
Normal file
BIN
server/2015Remote/res/Bitmap/compress.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 822 B |
BIN
server/2015Remote/res/Bitmap/uncompress.bmp
Normal file
BIN
server/2015Remote/res/Bitmap/uncompress.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 822 B |
@@ -263,6 +263,9 @@
|
|||||||
#define IDR_WEB_XTERM_CSS 383
|
#define IDR_WEB_XTERM_CSS 383
|
||||||
#define IDR_WEB_XTERM_FIT_JS 384
|
#define IDR_WEB_XTERM_FIT_JS 384
|
||||||
#define IDR_WEB_INDEX_HTML 385
|
#define IDR_WEB_INDEX_HTML 385
|
||||||
|
#define IDB_BITMAP_COMPRESS 386
|
||||||
|
#define IDB_BITMAP9 387
|
||||||
|
#define IDB_BITMAP_UNCOMPRESS 387
|
||||||
#define IDC_MESSAGE 1000
|
#define IDC_MESSAGE 1000
|
||||||
#define IDC_ONLINE 1001
|
#define IDC_ONLINE 1001
|
||||||
#define IDC_STATIC_TIPS 1002
|
#define IDC_STATIC_TIPS 1002
|
||||||
@@ -985,14 +988,19 @@
|
|||||||
#define ID_PARAM_THUMBNAIL_PREVIEW 33050
|
#define ID_PARAM_THUMBNAIL_PREVIEW 33050
|
||||||
#define ID_LICENSE_AUTO_FRP 33051
|
#define ID_LICENSE_AUTO_FRP 33051
|
||||||
#define ID_LICENSE_REVOKE_FRP 33052
|
#define ID_LICENSE_REVOKE_FRP 33052
|
||||||
|
#define ID_Menu 33053
|
||||||
|
#define ID_33054 33054
|
||||||
|
#define ID_MENU_COMPRESS 33055
|
||||||
|
#define ID_33056 33056
|
||||||
|
#define ID_MENU_UNCOMPRESS 33057
|
||||||
#define ID_EXIT_FULLSCREEN 40001
|
#define ID_EXIT_FULLSCREEN 40001
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 386
|
#define _APS_NEXT_RESOURCE_VALUE 388
|
||||||
#define _APS_NEXT_COMMAND_VALUE 33053
|
#define _APS_NEXT_COMMAND_VALUE 33058
|
||||||
#define _APS_NEXT_CONTROL_VALUE 2542
|
#define _APS_NEXT_CONTROL_VALUE 2542
|
||||||
#define _APS_NEXT_SYMED_VALUE 105
|
#define _APS_NEXT_SYMED_VALUE 105
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
BIN
server/go/cmd/winres/icon.png
Normal file
BIN
server/go/cmd/winres/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
60
server/go/cmd/winres/winres.json
Normal file
60
server/go/cmd/winres/winres.json
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"RT_GROUP_ICON": {
|
||||||
|
"APP": {
|
||||||
|
"0000": [
|
||||||
|
"icon.png"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RT_MANIFEST": {
|
||||||
|
"#1": {
|
||||||
|
"0409": {
|
||||||
|
"identity": {
|
||||||
|
"name": "YAMA Go Server",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"description": "YAMA Go Server",
|
||||||
|
"minimum-os": "win7",
|
||||||
|
"execution-level": "as invoker",
|
||||||
|
"ui-access": false,
|
||||||
|
"auto-elevate": false,
|
||||||
|
"dpi-awareness": "system",
|
||||||
|
"disable-theming": false,
|
||||||
|
"disable-window-filtering": false,
|
||||||
|
"high-resolution-scrolling-aware": false,
|
||||||
|
"ultra-high-resolution-scrolling-aware": false,
|
||||||
|
"long-path-aware": false,
|
||||||
|
"printer-driver-isolation": false,
|
||||||
|
"gdi-scaling": false,
|
||||||
|
"segment-heap": false,
|
||||||
|
"use-common-controls-v6": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RT_VERSION": {
|
||||||
|
"#1": {
|
||||||
|
"0000": {
|
||||||
|
"fixed": {
|
||||||
|
"file_version": "1.0.0.0",
|
||||||
|
"product_version": "1.0.0.0"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"0409": {
|
||||||
|
"Comments": "YAMA Go Remote Desktop Server",
|
||||||
|
"CompanyName": "SimpleRemoter",
|
||||||
|
"FileDescription": "YAMA Go Remote Desktop Server",
|
||||||
|
"FileVersion": "1.0.0",
|
||||||
|
"InternalName": "YamaGo.exe",
|
||||||
|
"LegalCopyright": "Copyright © 2026 YAMA",
|
||||||
|
"LegalTrademarks": "",
|
||||||
|
"OriginalFilename": "YamaGo.exe",
|
||||||
|
"PrivateBuild": "",
|
||||||
|
"ProductName": "YAMA Go Server",
|
||||||
|
"ProductVersion": "1.0.0",
|
||||||
|
"SpecialBuild": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3129,7 +3129,7 @@
|
|||||||
const totalDist = Math.sqrt(totalDx * totalDx + totalDy * totalDy);
|
const totalDist = Math.sqrt(totalDx * totalDx + totalDy * totalDy);
|
||||||
|
|
||||||
// Different thresholds for different states
|
// Different thresholds for different states
|
||||||
const moveThreshold = (touchState.state === T_SECOND_DOWN) ? 10 : 20;
|
const moveThreshold = (touchState.state === T_SECOND_DOWN) ? 22 : 20;
|
||||||
|
|
||||||
if (totalDist > moveThreshold && !touchState.moved) {
|
if (totalDist > moveThreshold && !touchState.moved) {
|
||||||
touchState.moved = true;
|
touchState.moved = true;
|
||||||
@@ -3226,7 +3226,13 @@
|
|||||||
// Must send first click before dblclick for Windows to recognize
|
// Must send first click before dblclick for Windows to recognize
|
||||||
console.log('[Touch] Double click');
|
console.log('[Touch] Double click');
|
||||||
clickAtCursor(0); // First click
|
clickAtCursor(0); // First click
|
||||||
dblClickAtCursor(); // Then double click
|
// dblClickAtCursor(); // Then double click
|
||||||
|
// 强制人工延迟 20 毫秒发送第二次标准单击
|
||||||
|
// 这 20ms 的延迟在操作上完全感觉不到,但对远程桌面的网络和操作系统驱动至关重要!
|
||||||
|
// 它能完美地把两次点击在时间线上拉开,让任何操作系统都 100% 判定这是标准的“物理鼠标双击”。
|
||||||
|
setTimeout(() => {
|
||||||
|
clickAtCursor(0);
|
||||||
|
}, 20);
|
||||||
touchState.state = T_IDLE;
|
touchState.state = T_IDLE;
|
||||||
} else if (touchState.state === T_FIRST_DOWN && !touchState.moved) {
|
} else if (touchState.state === T_FIRST_DOWN && !touchState.moved) {
|
||||||
// First tap released without moving = single click
|
// First tap released without moving = single click
|
||||||
|
|||||||
Reference in New Issue
Block a user