Feature: Support compress files in file management dialog

This commit is contained in:
yuanyuanxiang
2026-01-04 12:05:28 +01:00
parent 5e031523af
commit 49373a1137
9 changed files with 1147 additions and 32 deletions

View File

@@ -101,6 +101,92 @@ std::string GetPwdHash();
// CMy2015RemoteApp 构造
// 自定义压缩文件
#include <windows.h>
#include <shlobj.h>
#include "ZstdArchive.h"
bool RegisterZstaMenu(const std::string& exePath) {
HKEY hKey;
const char* compressText = "压缩为 ZSTA 文件";
const char* extractText = "解压 ZSTA 文件";
const char* zstaDesc = "ZSTA 压缩文件";
const char* zstaExt = "ZstaArchive";
// 文件右键
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "*\\shell\\CompressToZsta", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)compressText, strlen(compressText) + 1);
RegCloseKey(hKey);
}
std::string compressCmd = "\"" + exePath + "\" -c \"%1\"";
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "*\\shell\\CompressToZsta\\command", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)compressCmd.c_str(), compressCmd.size() + 1);
RegCloseKey(hKey);
}
// 文件夹右键
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "Directory\\shell\\CompressToZsta", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)compressText, strlen(compressText) + 1);
RegCloseKey(hKey);
}
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "Directory\\shell\\CompressToZsta\\command", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)compressCmd.c_str(), compressCmd.size() + 1);
RegCloseKey(hKey);
}
// .zsta 文件关联
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, ".zsta", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)zstaExt, strlen(zstaExt) + 1);
RegCloseKey(hKey);
}
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "ZstaArchive", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)zstaDesc, strlen(zstaDesc) + 1);
RegCloseKey(hKey);
}
// .zsta 右键菜单
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "ZstaArchive\\shell\\extract", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)extractText, strlen(extractText) + 1);
RegCloseKey(hKey);
}
std::string extractCmd = "\"" + exePath + "\" -x \"%1\"";
if (RegCreateKeyExA(HKEY_CLASSES_ROOT, "ZstaArchive\\shell\\extract\\command", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
RegSetValueExA(hKey, NULL, 0, REG_SZ, (BYTE*)extractCmd.c_str(), extractCmd.size() + 1);
RegCloseKey(hKey);
}
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
}
bool UnregisterZstaMenu() {
RegDeleteTreeA(HKEY_CLASSES_ROOT, "*\\shell\\CompressToZsta");
RegDeleteTreeA(HKEY_CLASSES_ROOT, "Directory\\shell\\CompressToZsta");
RegDeleteTreeA(HKEY_CLASSES_ROOT, ".zsta");
RegDeleteTreeA(HKEY_CLASSES_ROOT, "ZstaArchive");
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
}
std::string RemoveTrailingSlash(const std::string& path) {
std::string p = path;
while (!p.empty() && (p.back() == '/' || p.back() == '\\')) {
p.pop_back();
}
return p;
}
std::string RemoveZstaExtension(const std::string& path) {
if (path.size() >= 5) {
std::string ext = path.substr(path.size() - 5);
for (char& c : ext) c = tolower(c);
if (ext == ".zsta") {
return path.substr(0, path.size() - 5);
}
}
return path + "_extract";
}
CMy2015RemoteApp::CMy2015RemoteApp()
{
// 支持重新启动管理器
@@ -237,8 +323,65 @@ BOOL LaunchAsAdmin(const char* szFilePath, const char* verb)
return ShellExecuteExA(&shExecInfo);
}
BOOL CMy2015RemoteApp::ProcessZstaCmd() {
// 检查是否已注册右键菜单
char exePath[MAX_PATH];
GetModuleFileNameA(NULL, exePath, MAX_PATH);
// 检查当前注册的路径是否是自己
HKEY hKey;
bool needRegister = false;
if (RegOpenKeyExA(HKEY_CLASSES_ROOT, "ZstaArchive\\shell\\extract\\command",
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
char regPath[MAX_PATH * 2] = { 0 };
DWORD size = sizeof(regPath);
RegQueryValueExA(hKey, NULL, NULL, NULL, (BYTE*)regPath, &size);
RegCloseKey(hKey);
// 检查注册的路径是否包含当前程序路径
if (strstr(regPath, exePath) == NULL) {
needRegister = true; // 路径不同,需要重新注册
}
}
else {
needRegister = true; // 未注册
}
if (needRegister) {
RegisterZstaMenu(exePath);
}
// 处理自定义压缩和解压命令
if (__argc >= 3) {
std::string cmd = __argv[1];
std::string path = __argv[2];
// 压缩
if (cmd == "-c") {
std::string src = RemoveTrailingSlash(path);
std::string dst = src + ".zsta";
auto b = (zsta::CZstdArchive::Compress(src, dst) == zsta::Error::Success);
Mprintf("压缩%s: %s -> %s\n", b ? "成功" : "失败", src.c_str(), dst.c_str());
return FALSE;
}
// 解压
if (cmd == "-x") {
std::string dst = RemoveZstaExtension(path);
auto b = (zsta::CZstdArchive::Extract(path, dst) == zsta::Error::Success);
Mprintf("解压%s: %s -> %s\n", b ? "成功" : "失败", path.c_str(), dst.c_str());
return FALSE;
}
}
return TRUE;
}
BOOL CMy2015RemoteApp::InitInstance()
{
if (!ProcessZstaCmd()) {
Mprintf("[InitInstance] 处理自定义压缩/解压命令后退出。\n");
return FALSE;
}
#if _DEBUG
BOOL runNormal = TRUE;
#else

View File

@@ -85,6 +85,7 @@ public:
{
m_pSplash = pSplash;
}
BOOL ProcessZstaCmd();
virtual BOOL InitInstance();

Binary file not shown.

View File

@@ -6,6 +6,8 @@
#include "FileManagerDlg.h"
#include "FileTransferModeDlg.h"
#include "InputDlg.h"
#include "ZstdArchive.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
@@ -130,6 +132,8 @@ BEGIN_MESSAGE_MAP(CFileManagerDlg, CDialog)
ON_NOTIFY(NM_RCLICK, IDC_LIST_REMOTE, OnRclickListRemote)
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILEMANGER_COMPRESS, &CFileManagerDlg::OnFilemangerCompress)
ON_COMMAND(ID_FILEMANGER_UNCOMPRESS, &CFileManagerDlg::OnFilemangerUncompress)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
@@ -1330,6 +1334,78 @@ void CFileManagerDlg::OnLocalCopy()
SendUploadJob();
}
void CFileManagerDlg::OnLocalCompress()
{
std::vector<std::string> paths;
POSITION pos = m_list_local.GetFirstSelectedItemPosition();
while (pos) {
int nItem = m_list_local.GetNextSelectedItem(pos);
CString file = m_Local_Path + m_list_local.GetItemText(nItem, 0);
// 如果是目录
if (m_list_local.GetItemData(nItem)) {
file += '\\';
}
paths.push_back(file.GetBuffer(0));
}
std::string target = m_Local_Path.GetString() + ToPekingDateTime(0) + ".zsta";
zsta::Error err = zsta::CZstdArchive::Compress(paths, target);
if (err != zsta::Error::Success) {
::MessageBoxA(m_hWnd, "压缩失败: " + CString(zsta::CZstdArchive::GetErrorString(err)),
"错误", MB_OK | MB_ICONERROR);
}
else {
FixedLocalFileList(".");
}
}
bool HasZstaExtension(const std::string& path) {
if (path.size() < 5) return false;
std::string ext = path.substr(path.size() - 5);
// 转小写比较
for (char& c : ext) c = tolower(c);
return ext == ".zsta";
}
std::string GetExtractDir(const std::string& archivePath) {
if (archivePath.size() >= 5) {
std::string ext = archivePath.substr(archivePath.size() - 5);
for (char& c : ext) c = tolower(c);
if (ext == ".zsta") {
return archivePath.substr(0, archivePath.size() - 5);
}
}
return archivePath + "_extract";
}
void CFileManagerDlg::OnLocalUnCompress()
{
BOOL needRefresh = FALSE;
POSITION pos = m_list_local.GetFirstSelectedItemPosition();
while (pos) {
int nItem = m_list_local.GetNextSelectedItem(pos);
CString file = m_Local_Path + m_list_local.GetItemText(nItem, 0);
// 如果是目录
if (m_list_local.GetItemData(nItem)) {
continue;
}
else if (HasZstaExtension(file.GetString())){
std::string path(file.GetBuffer(0));
std::string destDir = GetExtractDir(path);
zsta::Error err = zsta::CZstdArchive::Extract(path, destDir);
if (err != zsta::Error::Success) {
::MessageBoxA(m_hWnd, "解压失败: " + CString(zsta::CZstdArchive::GetErrorString(err)),
"错误", MB_OK | MB_ICONERROR);
}
else {
needRefresh = TRUE;
}
}
}
if (needRefresh) {
FixedLocalFileList(".");
}
}
//////////////// 文件传输操作 ////////////////
// 只管发出了下载的文件
// 一个一个发,接收到下载完成时,下载第二个文件 ...
@@ -1360,6 +1436,66 @@ void CFileManagerDlg::OnRemoteCopy()
SendDownloadJob();
}
std::vector<char> BuildMultiStringPath(const std::vector<std::string>& paths);
void CFileManagerDlg::OnRemoteCompress()
{
std::vector<std::string> paths;
std::string target = m_Remote_Path.GetString() + ToPekingDateTime(0) + ".zsta";
paths.push_back(target);
POSITION pos = m_list_remote.GetFirstSelectedItemPosition();
while (pos) {
int nItem = m_list_remote.GetNextSelectedItem(pos);
CString file = m_Remote_Path + m_list_remote.GetItemText(nItem, 0);
// 如果是目录
if (m_list_remote.GetItemData(nItem)) {
file += '\\';
}
paths.push_back(file.GetBuffer(0));
}
if (paths.size() <= 1) {
::MessageBoxA(m_hWnd, "请先选择要压缩的文件或文件夹!", "提示", MB_OK | MB_ICONWARNING);
return;
}
auto pathsMultiString = BuildMultiStringPath(paths);
BYTE* bPacket = (BYTE*)LocalAlloc(LPTR, pathsMultiString.size() + 1);
if (bPacket) {
memcpy(bPacket + 1, pathsMultiString.data(), pathsMultiString.size());
bPacket[0] = CMD_COMPRESS_FILES;
m_ContextObject->Send2Client(bPacket, (DWORD)(pathsMultiString.size() + 1));
LocalFree(bPacket);
}
}
void CFileManagerDlg::OnRemoteUnCompress()
{
std::vector<std::string> paths;
POSITION pos = m_list_remote.GetFirstSelectedItemPosition();
while (pos) {
int nItem = m_list_remote.GetNextSelectedItem(pos);
CString file = m_Remote_Path + m_list_remote.GetItemText(nItem, 0);
// 如果是目录
if (m_list_remote.GetItemData(nItem)) {
continue;
}
else if (HasZstaExtension(file.GetString())) {
paths.push_back(file.GetBuffer(0));
}
}
if (paths.empty()) {
::MessageBoxA(m_hWnd, "请先选择要解压的.zsta文件!", "提示", MB_OK | MB_ICONWARNING);
return;
}
auto pathsMultiString = BuildMultiStringPath(paths);
BYTE* bPacket = (BYTE*)LocalAlloc(LPTR, pathsMultiString.size() + 1);
if (bPacket) {
memcpy(bPacket + 1, pathsMultiString.data(), pathsMultiString.size());
bPacket[0] = CMD_UNCOMPRESS_FILES;
m_ContextObject->Send2Client(bPacket, (DWORD)(pathsMultiString.size() + 1));
LocalFree(bPacket);
}
}
// 发出一个下载任务
BOOL CFileManagerDlg::SendDownloadJob()
{
@@ -2048,6 +2184,31 @@ void CFileManagerDlg::OnTransfer()
}
}
void CFileManagerDlg::OnFilemangerCompress()
{
POINT pt;
GetCursorPos(&pt);
if (GetFocus()->m_hWnd == m_list_local.m_hWnd) {
OnLocalCompress();
}
else {
OnRemoteCompress();
}
}
void CFileManagerDlg::OnFilemangerUncompress()
{
POINT pt;
GetCursorPos(&pt);
if (GetFocus()->m_hWnd == m_list_local.m_hWnd) {
OnLocalUnCompress();
}
else {
OnRemoteUnCompress();
}
}
void CFileManagerDlg::OnRename()
{
// TODO: Add your command handler code here

View File

@@ -164,6 +164,10 @@ protected:
afx_msg void OnUpdateLocalNewfolder(CCmdUI* pCmdUI);
afx_msg void OnRemoteCopy();
afx_msg void OnLocalCopy();
afx_msg void OnRemoteCompress();
afx_msg void OnLocalCompress();
afx_msg void OnRemoteUnCompress();
afx_msg void OnLocalUnCompress();
afx_msg void OnLocalDelete();
afx_msg void OnRemoteDelete();
afx_msg void OnRemoteStop();
@@ -203,6 +207,9 @@ private:
bool DeleteDirectory(LPCTSTR lpszDirectory);
void EnableControl(BOOL bEnable = TRUE);
float m_fScalingFactor;
public:
afx_msg void OnFilemangerCompress();
afx_msg void OnFilemangerUncompress();
};
//{{AFX_INSERT_LOCATION}}

View File

@@ -28,7 +28,6 @@
#define IDC_CURSOR_DRAG 144
#define IDC_CURSOR_MDRAG 145
#define IDD_DIALOG_TRANSMODE 146
#define IDR_MENU_FILE_OPERATION 147
#define IDD_DIALOG_FILE_COMPRESS 148
#define IDD_DIALOG_TALK 149
#define IDD_DIALOG_SHELL 150
@@ -237,7 +236,6 @@
#define IDR_GH0STTYPE 2003
#define IDD_BUILD 2004
#define IDD_FILE 2005
#define IDR_LIST 2006
#define IDC_DRAG 2007
#define IDC_MUTI_DRAG 2008
#define IDC_CURSOR3 2009
@@ -268,7 +266,6 @@
#define IDI_KEYBOARD 2034
#define IDI_SYSTEM 2035
#define IDC_DOT 2036
#define IDR_MINIMIZE 2037
#define IDD_AUDIO 2038
#define IDI_AUDIO 2039
#define IDC_LIST_REMOTE 2040
@@ -316,9 +313,6 @@
#define ID_LOCAL_TOOLBAR 2082
#define ID_REMOTE_TOOLBAR 2083
#define ID_APP_PWD 2084
#define IDM_FILEMANAGER 2085
#define IDM_SHUTDOWN 2086
#define IDM_REBOOT 2087
#define IDM_TRANSFER 2088
#define IDM_RENAME 2089
#define IDM_DELETE 2090
@@ -344,32 +338,13 @@
#define IDM_REMOTE_SMALLICON 2110
#define IDM_REMOTE_LIST 2111
#define IDM_REMOTE_REPORT 2112
#define IDM_SCREENSPY 2113
#define IDM_DOWNEXEC 2114
#define IDM_WEBCAM 2115
#define IDM_REMOVE 2116
#define IDM_KEYBOARD 2117
#define IDM_REFRESH 2118
#define IDM_SYSTEM 2119
#define IDM_KILLPROCESS 2120
#define IDM_REFRESHPSLIST 2121
#define IDM_REMOTESHELL 2122
#define IDM_LOGOFF 2123
#define IDM_SELECT_ALL 2124
#define IDM_UNSELECT_ALL 2125
#define IDM_OPEN_URL_SHOW 2126
#define IDM_OPEN_URL_HIDE 2127
#define IDM_CLEANEVENT 2128
#define IDM_HIDE 2129
#define IDM_SHOW 2130
#define IDM_EXIT 2131
#define IDM_RENAME_REMARK 2132
#define IDM_UPDATE_SERVER 2133
#define IDM_LOCAL_OPEN 2134
#define IDM_REMOTE_OPEN_SHOW 2135
#define IDM_REMOTE_OPEN_HIDE 2136
#define IDM_AUDIO_LISTEN 2137
#define IDM_DISCONNECT 2138
#define ID_STAUTSTIP 2139
#define ID_STAUTSSPEED 2140
#define ID_STAUTSPORT 2141
@@ -477,12 +452,6 @@
#define ID_VIEW_BIG_ICON 32806
#define ID_VIEW_SMALL_ICON 32807
#define ID_VIEW_DETAIL 32808
#define ID_OPERATION_RENAME 32813
#define ID_OPERATION_CLIENT_SHOW_RUN 32819
#define ID_OPERATION_CLIENT_HIDE_RUN 32820
#define ID_OPERATION_SERVER_RUN 32821
#define ID_OPERATION_COMPRESS 32823
#define ID_OPERATION_DECOMPRESS 32825
#define ID_PLIST_KILL 32828
#define ID_PLIST_REFRESH 32829
#define ID_WLIST_REFRESH 32836
@@ -628,6 +597,10 @@
#define ID_HOOK_WIN 32986
#define ID_32987 32987
#define ID_RUNAS_SERVICE 32988
#define ID_FILEMANGER_32989 32989
#define ID_FILEMANGER_COMPRESS 32990
#define ID_FILEMANGER_32991 32991
#define ID_FILEMANGER_UNCOMPRESS 32992
#define ID_EXIT_FULLSCREEN 40001
// Next default values for new objects
@@ -635,7 +608,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 320
#define _APS_NEXT_COMMAND_VALUE 32989
#define _APS_NEXT_COMMAND_VALUE 32993
#define _APS_NEXT_CONTROL_VALUE 2213
#define _APS_NEXT_SYMED_VALUE 101
#endif