Fix: V2 file transfer broken via FileManager dialog (both directions)
This commit is contained in:
@@ -8,8 +8,13 @@
|
||||
#include "InputDlg.h"
|
||||
#include "ZstdArchive.h"
|
||||
#include "2015RemoteDlg.h"
|
||||
#include "CDlgFileSend.h"
|
||||
#include <Shlobj.h>
|
||||
|
||||
// V2 接收用:定义在 CPasswordDlg.cpp,按本仓约定就地前置声明
|
||||
std::string GetPwdHash();
|
||||
std::string GetHMAC(int offset);
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
@@ -176,6 +181,8 @@ BEGIN_MESSAGE_MAP(CFileManagerDlg, CDialog)
|
||||
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
|
||||
ON_MESSAGE(WM_LOCAL_SEARCH_DONE, OnLocalSearchDone)
|
||||
ON_MESSAGE(WM_LOCAL_SEARCH_PROGRESS, OnLocalSearchProgress)
|
||||
ON_MESSAGE(WM_RECVFILEV2_CHUNK, &CFileManagerDlg::OnRecvFileV2Chunk)
|
||||
ON_MESSAGE(WM_RECVFILEV2_COMPLETE, &CFileManagerDlg::OnRecvFileV2Complete)
|
||||
//}}AFX_MSG_MAP
|
||||
ON_COMMAND(ID_FILEMANGER_COMPRESS, &CFileManagerDlg::OnFilemangerCompress)
|
||||
ON_COMMAND(ID_FILEMANGER_UNCOMPRESS, &CFileManagerDlg::OnFilemangerUncompress)
|
||||
@@ -994,6 +1001,28 @@ void CFileManagerDlg::OnReceiveComplete()
|
||||
break;
|
||||
case TOKEN_CLIENTID:
|
||||
break;
|
||||
case COMMAND_SEND_FILE_V2:
|
||||
case COMMAND_FILE_COMPLETE_V2: {
|
||||
// V2 下载(远程→本地):客户端把 chunk 通过 FileManager 子连接推回服务端。
|
||||
// 此函数在 NotifyProc -> worker 线程上调用。窗口创建/UI 操作必须回 UI 线程,
|
||||
// 否则消息泵不通,进度框不会显示(落盘可在任意线程,影响仅限 UI)。
|
||||
// 拷贝数据后 PostMessage 回自己;UI 线程的 OnRecvFileV2Chunk/Complete 处理。
|
||||
LPBYTE buf = m_ContextObject->m_DeCompressionBuffer.GetBuffer(0);
|
||||
unsigned len = m_ContextObject->m_DeCompressionBuffer.GetBufferLen();
|
||||
size_t minSize = (buf[0] == COMMAND_FILE_COMPLETE_V2)
|
||||
? sizeof(FileCompletePacketV2) : sizeof(FileChunkPacketV2);
|
||||
if (len >= minSize) {
|
||||
// 两种结构 cmd/transferID 偏移一致,可共用 FileChunkPacketV2 取 transferID
|
||||
uint64_t transferID = ((FileChunkPacketV2*)buf)->transferID;
|
||||
UINT msg = (buf[0] == COMMAND_FILE_COMPLETE_V2)
|
||||
? WM_RECVFILEV2_COMPLETE : WM_RECVFILEV2_CHUNK;
|
||||
// 用 std::pair<vector,uint64> 当 wParam 载体,UI 端 delete
|
||||
auto* payload = new std::pair<std::vector<BYTE>, uint64_t>(
|
||||
std::vector<BYTE>(buf, buf + len), transferID);
|
||||
PostMessage(msg, (WPARAM)payload, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SendException();
|
||||
break;
|
||||
@@ -2682,10 +2711,87 @@ void CFileManagerDlg::OnLocalStop()
|
||||
|
||||
void CFileManagerDlg::PostNcDestroy()
|
||||
{
|
||||
// TODO: Add your specialized code here and/or call the base class
|
||||
// 清理 V2 接收进度框:注意 CDialogBase::PostNcDestroy 写死 `delete this`,
|
||||
// 对话框关窗时会自删——这里**不能** delete,否则双重释放。
|
||||
// 用 HWND 而非 ptr 判活,避免野指针;SendMessage(WM_CLOSE) 让它走自己关闭路径。
|
||||
for (auto& entry : m_FileRecvDlgs) {
|
||||
HWND hWnd = entry.second.first;
|
||||
if (hWnd && ::IsWindow(hWnd)) {
|
||||
::SendMessage(hWnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
m_FileRecvDlgs.clear();
|
||||
__super::PostNcDestroy();
|
||||
}
|
||||
|
||||
// V2 下载(远程→本地)chunk 处理:UI 线程上执行,安全创建/操作进度框
|
||||
LRESULT CFileManagerDlg::OnRecvFileV2Chunk(WPARAM wParam, LPARAM /*lParam*/)
|
||||
{
|
||||
auto* payload = (std::pair<std::vector<BYTE>, uint64_t>*)wParam;
|
||||
if (!payload) return 0;
|
||||
|
||||
BYTE* szBuffer = payload->first.data();
|
||||
size_t len = payload->first.size();
|
||||
uint64_t transferID = payload->second;
|
||||
FileChunkPacketV2* pkt = (FileChunkPacketV2*)szBuffer;
|
||||
|
||||
// 按 transferID 懒加载/复用进度框
|
||||
// 注意:CDlgFileSend 的 PostNcDestroy 自删(CDialogBase 默认行为),
|
||||
// 窗口被自动关闭后 dlg 是野指针,HWND 失效是唯一可信号——重建即可,
|
||||
// 旧 ptr 不再访问、不能 delete。
|
||||
auto& entry = m_FileRecvDlgs[transferID];
|
||||
CDlgFileSend* dlg = entry.second;
|
||||
if (dlg == nullptr || !::IsWindow(entry.first)) {
|
||||
dlg = new CDlgFileSend(g_2015RemoteDlg, m_ContextObject->GetServer(),
|
||||
m_ContextObject, FALSE);
|
||||
dlg->Create(IDD_DIALOG_FILESEND, GetDesktopWindow());
|
||||
dlg->SetWindowTextA(_TR("接收文件 (V2)"));
|
||||
dlg->ShowWindow(SW_SHOW);
|
||||
dlg->m_bKeepConnection = TRUE; // FileManager 子连接复用,对话框关闭时不断开
|
||||
entry = { dlg->GetSafeHwnd(), dlg };
|
||||
}
|
||||
|
||||
// 落盘
|
||||
std::string hash = GetPwdHash(), hmac = GetHMAC(100);
|
||||
int n = RecvFileChunkV2((char*)szBuffer, len, nullptr, nullptr, hash, hmac, 0);
|
||||
if (n) {
|
||||
Mprintf("[FileManager] RecvFileChunkV2 failed: %d\n", n);
|
||||
}
|
||||
|
||||
// 进度
|
||||
BYTE* name = szBuffer + sizeof(FileChunkPacketV2);
|
||||
dlg->UpdateProgress(CString((char*)name, (int)pkt->nameLength), FileProgressInfo(pkt));
|
||||
|
||||
delete payload;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// V2 文件完成校验:UI 线程
|
||||
LRESULT CFileManagerDlg::OnRecvFileV2Complete(WPARAM wParam, LPARAM /*lParam*/)
|
||||
{
|
||||
auto* payload = (std::pair<std::vector<BYTE>, uint64_t>*)wParam;
|
||||
if (!payload) return 0;
|
||||
|
||||
BYTE* szBuffer = payload->first.data();
|
||||
size_t len = payload->first.size();
|
||||
uint64_t transferID = payload->second;
|
||||
|
||||
bool verifyOk = HandleFileCompleteV2((const char*)szBuffer, len, 0);
|
||||
Mprintf("[FileManager] V2 文件校验%s\n", verifyOk ? "通过" : "失败");
|
||||
|
||||
// 关闭对应进度框
|
||||
auto it = m_FileRecvDlgs.find(transferID);
|
||||
if (it != m_FileRecvDlgs.end()) {
|
||||
if (::IsWindow(it->second.first)) {
|
||||
it->second.second->FinishFileSend(verifyOk);
|
||||
}
|
||||
m_FileRecvDlgs.erase(it);
|
||||
}
|
||||
|
||||
delete payload;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFileManagerDlg::SendTransferMode()
|
||||
{
|
||||
CFileTransferModeDlg dlg(this);
|
||||
@@ -3211,18 +3317,19 @@ void CFileManagerDlg::OnTransferV2ToRemote()
|
||||
// 通知客户端目标目录(使用远程当前目录)
|
||||
// 由 SendFilesToClientV2 内部的 COMMAND_C2C_PREPARE 处理
|
||||
|
||||
// 调用V2传输 - 需要通过IP找到主连接(m_ContextObject是子连接)
|
||||
if (g_2015RemoteDlg && m_ContextObject) {
|
||||
// 通过子连接的IP地址找到主连接
|
||||
std::string peerIP = m_ContextObject->GetPeerName();
|
||||
context* mainCtx = g_2015RemoteDlg->FindHostByIP(peerIP);
|
||||
// 调用V2传输 - 通过 clientID 找主连接(m_ContextObject 是子连接)。
|
||||
// 不能用 GetPeerName() + FindHostByIP:NAT/frpc/反代场景下子连接的 socket peer
|
||||
// 常是 127.0.0.1 或内网地址,跟主连接登录时存的 RES_CLIENT_PUBIP 对不上,
|
||||
// 会找到错误的 ctx 或返回 NULL(剪贴板 V2 走 FindHost(clientID) 没此问题)。
|
||||
if (g_2015RemoteDlg) {
|
||||
uint64_t clientID = GetClientID();
|
||||
context* mainCtx = clientID ? g_2015RemoteDlg->FindHost(clientID) : nullptr;
|
||||
if (mainCtx) {
|
||||
// 使用远程当前目录作为目标目录
|
||||
std::string remoteDir = m_Remote_Path.GetString();
|
||||
g_2015RemoteDlg->SendFilesToClientV2(mainCtx, files, remoteDir);
|
||||
ShowMessage(_TRF("V2传输已启动,共 %d 个文件 -> %s"), (int)files.size(), remoteDir.c_str());
|
||||
} else {
|
||||
ShowMessage(_TRF("找不到主连接: %s"), peerIP.c_str());
|
||||
ShowMessage(_TRF("找不到主连接: clientID=%llu"), clientID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#define WM_MY_MESSAGE (WM_USER+300)
|
||||
#define WM_LOCAL_SEARCH_DONE (WM_USER+302)
|
||||
#define WM_LOCAL_SEARCH_PROGRESS (WM_USER+303)
|
||||
#define WM_RECVFILEV2_CHUNK (WM_USER+304)
|
||||
#define WM_RECVFILEV2_COMPLETE (WM_USER+305)
|
||||
|
||||
// FileManagerDlg.h : header file
|
||||
//
|
||||
@@ -269,6 +271,15 @@ protected:
|
||||
void DropItemOnList(CListCtrl* pDragList, CListCtrl* pDropList);
|
||||
private:
|
||||
bool m_bIsUpload; // 是否是把本地主机传到远程上,标志方向位
|
||||
|
||||
// V2 下载(远程→本地):FileManager 子连接的 NotifyProc 在 worker 线程上
|
||||
// 直接调 OnReceiveComplete,不能在那里 new 进度框(窗口创建在 worker 线程
|
||||
// 没有消息泵,PostMessage 投不出去)。把 chunk 数据拷贝出来 PostMessage 回
|
||||
// 自己(UI 线程)处理,参考 ScreenSpyDlg 同样的模式。按 transferID 维护进度框。
|
||||
std::map<uint64_t, std::pair<HWND, class CDlgFileSend*>> m_FileRecvDlgs;
|
||||
afx_msg LRESULT OnRecvFileV2Chunk(WPARAM wParam, LPARAM lParam);
|
||||
afx_msg LRESULT OnRecvFileV2Complete(WPARAM wParam, LPARAM lParam);
|
||||
|
||||
bool MakeSureDirectoryPathExists(LPCTSTR pszDirPath);
|
||||
void SendTransferMode();
|
||||
void SendFileData();
|
||||
|
||||
Reference in New Issue
Block a user