Fix: Copy text between master and client need a delay

This commit is contained in:
shaun
2025-12-12 14:36:32 +01:00
committed by yuanyuanxiang
parent 2160922ba0
commit 93d6e730b8
8 changed files with 129 additions and 83 deletions

View File

@@ -457,7 +457,7 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
SendData(szBuffer, sizeof(szBuffer)); SendData(szBuffer, sizeof(szBuffer));
break; break;
} }
SendClientClipboard(); SendClientClipboard(ulLength > 1);
break; break;
} }
case COMMAND_SCREEN_SET_CLIPBOARD: { case COMMAND_SCREEN_SET_CLIPBOARD: {
@@ -531,26 +531,48 @@ VOID CScreenManager::UpdateClientClipboard(char *szBuffer, ULONG ulLength)
CloseClipboard(); CloseClipboard();
} }
VOID CScreenManager::SendClientClipboard() VOID CScreenManager::SendClientClipboard(BOOL fast)
{ {
if (!::OpenClipboard(NULL)) //打开剪切板设备 if (!::OpenClipboard(NULL))
return; return;
HGLOBAL hGlobal = GetClipboardData(CF_TEXT); //代表着一个内存
if (hGlobal == NULL) {
::CloseClipboard();
return;
}
size_t iPacketLength = GlobalSize(hGlobal) + 1;
char* szClipboardVirtualAddress = (LPSTR) GlobalLock(hGlobal); //锁定
LPBYTE szBuffer = new BYTE[iPacketLength];
// 改为获取 Unicode 格式
HGLOBAL hGlobal = GetClipboardData(CF_UNICODETEXT);
if (hGlobal == NULL) {
::CloseClipboard();
return;
}
szBuffer[0] = TOKEN_CLIPBOARD_TEXT; wchar_t* pWideStr = (wchar_t*)GlobalLock(hGlobal);
memcpy(szBuffer + 1, szClipboardVirtualAddress, iPacketLength - 1); if (pWideStr == NULL) {
::GlobalUnlock(hGlobal); ::CloseClipboard();
::CloseClipboard(); return;
m_ClientObject->Send2Server((char*)szBuffer, iPacketLength); }
delete[] szBuffer;
// Unicode 转 UTF-8
int utf8Len = WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, NULL, 0, NULL, NULL);
if (utf8Len <= 0) {
GlobalUnlock(hGlobal);
::CloseClipboard();
return;
}
if (fast && utf8Len > 200 * 1024) {
Mprintf("剪切板文本太长, 无法快速拷贝: %d\n", utf8Len);
GlobalUnlock(hGlobal);
::CloseClipboard();
return;
}
LPBYTE szBuffer = new BYTE[utf8Len + 1];
szBuffer[0] = TOKEN_CLIPBOARD_TEXT;
WideCharToMultiByte(CP_UTF8, 0, pWideStr, -1, (char*)(szBuffer + 1), utf8Len, NULL, NULL);
GlobalUnlock(hGlobal);
::CloseClipboard();
m_ClientObject->Send2Server((char*)szBuffer, utf8Len + 1);
delete[] szBuffer;
} }

View File

@@ -46,7 +46,7 @@ public:
std::string m_DesktopID; std::string m_DesktopID;
BOOL m_bIsWorking; BOOL m_bIsWorking;
BOOL m_bIsBlockInput; BOOL m_bIsBlockInput;
VOID SendClientClipboard(); VOID SendClientClipboard(BOOL fast);
VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength); VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength);
std::string m_hash; std::string m_hash;

Binary file not shown.

Binary file not shown.

View File

@@ -134,21 +134,21 @@ static BOOL HandleServiceCommandLine()
// -service: 作为服务运行 // -service: 作为服务运行
if (cmdLine.Find(_T("-service")) != -1) { if (cmdLine.Find(_T("-service")) != -1) {
int r = ServerService_Run(); int r = ServerService_Run();
Mprintf("[HandleServiceCommandLine] ServerService_Run %s", r ? "failed" : "succeed"); Mprintf("[HandleServiceCommandLine] ServerService_Run %s\n", r ? "failed" : "succeed");
return TRUE; return TRUE;
} }
// -install: 安装服务 // -install: 安装服务
if (cmdLine.Find(_T("-install")) != -1) { if (cmdLine.Find(_T("-install")) != -1) {
BOOL r = ServerService_Install(); BOOL r = ServerService_Install();
Mprintf("[HandleServiceCommandLine] ServerService_Install %s", !r ? "failed" : "succeed"); Mprintf("[HandleServiceCommandLine] ServerService_Install %s\n", !r ? "failed" : "succeed");
return TRUE; return TRUE;
} }
// -uninstall: 卸载服务 // -uninstall: 卸载服务
if (cmdLine.Find(_T("-uninstall")) != -1) { if (cmdLine.Find(_T("-uninstall")) != -1) {
BOOL r = ServerService_Uninstall(); BOOL r = ServerService_Uninstall();
Mprintf("[HandleServiceCommandLine] ServerService_Uninstall %s", !r ? "failed" : "succeed"); Mprintf("[HandleServiceCommandLine] ServerService_Uninstall %s\n", !r ? "failed" : "succeed");
return TRUE; return TRUE;
} }
@@ -156,7 +156,7 @@ static BOOL HandleServiceCommandLine()
// 此模式下正常运行GUI但使用不同的互斥量名称避免冲突 // 此模式下正常运行GUI但使用不同的互斥量名称避免冲突
if (cmdLine.Find(_T("-agent")) != -1) { if (cmdLine.Find(_T("-agent")) != -1) {
// 继续正常启动GUI但标记为代理模式 // 继续正常启动GUI但标记为代理模式
Mprintf("[HandleServiceCommandLine] Run service agent: '%s'", cmdLine.GetString()); Mprintf("[HandleServiceCommandLine] Run service agent: '%s'\n", cmdLine.GetString());
return FALSE; return FALSE;
} }
@@ -165,7 +165,7 @@ static BOOL HandleServiceCommandLine()
BOOL running = FALSE; BOOL running = FALSE;
char servicePath[MAX_PATH] = { 0 }; char servicePath[MAX_PATH] = { 0 };
BOOL r = ServerService_CheckStatus(&registered, &running, servicePath, MAX_PATH); BOOL r = ServerService_CheckStatus(&registered, &running, servicePath, MAX_PATH);
Mprintf("[HandleServiceCommandLine] ServerService_CheckStatus %s", !r ? "failed" : "succeed"); Mprintf("[HandleServiceCommandLine] ServerService_CheckStatus %s\n", !r ? "failed" : "succeed");
char curPath[MAX_PATH]; char curPath[MAX_PATH];
GetModuleFileNameA(NULL, curPath, MAX_PATH); GetModuleFileNameA(NULL, curPath, MAX_PATH);
@@ -242,13 +242,13 @@ BOOL CMy2015RemoteApp::InitInstance()
char curFile[MAX_PATH] = { 0 }; char curFile[MAX_PATH] = { 0 };
GetModuleFileNameA(NULL, curFile, MAX_PATH); GetModuleFileNameA(NULL, curFile, MAX_PATH);
if (!IsRunningAsAdmin() && LaunchAsAdmin(curFile, "runas")) { if (!IsRunningAsAdmin() && LaunchAsAdmin(curFile, "runas")) {
Mprintf("[InitInstance] 程序没有管理员权限,用户选择以管理员身份重新运行。"); Mprintf("[InitInstance] 程序没有管理员权限,用户选择以管理员身份重新运行。\n");
return FALSE; return FALSE;
} }
// 首先处理服务命令行参数 // 首先处理服务命令行参数
if (HandleServiceCommandLine()) { if (HandleServiceCommandLine()) {
Mprintf("[InitInstance] 服务命令已处理,退出。"); Mprintf("[InitInstance] 服务命令已处理,退出。\n");
return FALSE; // 服务命令已处理,退出 return FALSE; // 服务命令已处理,退出
} }
@@ -268,7 +268,7 @@ BOOL CMy2015RemoteApp::InitInstance()
} }
#endif #endif
Mprintf("[InitInstance] 主控程序启动运行。"); Mprintf("[InitInstance] 主控程序启动运行。\n");
SetUnhandledExceptionFilter(&whenbuged); SetUnhandledExceptionFilter(&whenbuged);
// 创建并显示启动画面 // 创建并显示启动画面
@@ -349,10 +349,11 @@ int CMy2015RemoteApp::ExitInstance()
// 只有在代理模式退出时才停止服务 // 只有在代理模式退出时才停止服务
if (IsAgentMode()) { if (IsAgentMode()) {
ServerService_Stop(); ServerService_Stop();
Mprintf("[InitInstance] 主控程序为代理模式,停止服务。"); Mprintf("[InitInstance] 主控程序为代理模式,停止服务。\n");
} }
Mprintf("[InitInstance] 主控程序退出运行。"); Mprintf("[InitInstance] 主控程序退出运行。\n");
Sleep(1000);
return CWinApp::ExitInstance(); return CWinApp::ExitInstance();
} }

View File

@@ -3809,53 +3809,56 @@ void CMy2015RemoteDlg::OnDestroy()
CString GetClipboardText() CString GetClipboardText()
{ {
if (!OpenClipboard(nullptr)) return _T(""); if (!OpenClipboard(nullptr)) return _T("");
CString strText;
// 优先获取 Unicode 格式
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
if (hData) {
wchar_t* pszText = static_cast<wchar_t*>(GlobalLock(hData));
if (pszText) {
#ifdef UNICODE #ifdef UNICODE
HANDLE hData = GetClipboardData(CF_UNICODETEXT); strText = pszText;
#else #else
HANDLE hData = GetClipboardData(CF_TEXT); // 将 Unicode 转换为多字节(使用系统默认代码页)
int len = WideCharToMultiByte(CP_ACP, 0, pszText, -1, nullptr, 0, nullptr, nullptr);
if (len > 0) {
char* pBuf = strText.GetBuffer(len);
WideCharToMultiByte(CP_ACP, 0, pszText, -1, pBuf, len, nullptr, nullptr);
strText.ReleaseBuffer();
}
#endif #endif
}
GlobalUnlock(hData);
}
if (!hData) { CloseClipboard();
CloseClipboard(); return strText;
return _T("");
}
#ifdef UNICODE
wchar_t* pszText = static_cast<wchar_t*>(GlobalLock(hData));
#else
char* pszText = static_cast<char*>(GlobalLock(hData));
#endif
CString strText = pszText ? pszText : _T("");
GlobalUnlock(hData);
CloseClipboard();
return strText;
} }
void SetClipboardText(const CString& text) void SetClipboardText(const char* utf8Text)
{ {
if (!OpenClipboard(nullptr)) return; if (!OpenClipboard(nullptr)) return;
EmptyClipboard(); EmptyClipboard();
#ifdef UNICODE // UTF-8 转 Unicode
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, (text.GetLength() + 1) * sizeof(wchar_t)); int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8Text, -1, nullptr, 0);
wchar_t* p = static_cast<wchar_t*>(GlobalLock(hGlob)); if (wlen > 0) {
if (p) wcscpy_s(p, text.GetLength() + 1, text); HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, wlen * sizeof(wchar_t));
#else if (hGlob) {
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, (text.GetLength() + 1) * sizeof(char)); wchar_t* p = static_cast<wchar_t*>(GlobalLock(hGlob));
char* p = static_cast<char*>(GlobalLock(hGlob)); if (p) {
if (p) strcpy_s(p, text.GetLength() + 1, CT2A(text)); // CT2A 宏把 CString 转成 char* MultiByteToWideChar(CP_UTF8, 0, utf8Text, -1, p, wlen);
#endif GlobalUnlock(hGlob);
SetClipboardData(CF_UNICODETEXT, hGlob);
GlobalUnlock(hGlob); }
#ifdef UNICODE else {
SetClipboardData(CF_UNICODETEXT, hGlob); GlobalFree(hGlob);
#else }
SetClipboardData(CF_TEXT, hGlob); }
#endif }
CloseClipboard(); CloseClipboard();
} }
CDialogBase* CMy2015RemoteDlg::GetRemoteWindow(HWND hWnd) CDialogBase* CMy2015RemoteDlg::GetRemoteWindow(HWND hWnd)
@@ -3919,10 +3922,15 @@ LRESULT CALLBACK CMy2015RemoteDlg::LowLevelKeyboardProc(int nCode, WPARAM wParam
} else { } else {
CString strText = GetClipboardText(); CString strText = GetClipboardText();
if (!strText.IsEmpty()) { if (!strText.IsEmpty()) {
if (strText.GetLength() > 200*1024) {
Mprintf("【Ctrl+V】 从本地拷贝文本到远程失败, 文本太长: %d \n", strText.GetLength());
break;
}
BYTE* szBuffer = new BYTE[strText.GetLength() + 1]; BYTE* szBuffer = new BYTE[strText.GetLength() + 1];
szBuffer[0] = COMMAND_SCREEN_SET_CLIPBOARD; szBuffer[0] = COMMAND_SCREEN_SET_CLIPBOARD;
memcpy(szBuffer + 1, strText.GetString(), strText.GetLength()); memcpy(szBuffer + 1, strText.GetString(), strText.GetLength());
dlg->m_ContextObject->Send2Client(szBuffer, strText.GetLength() + 1); if (dlg->m_ContextObject->Send2Client(szBuffer, strText.GetLength() + 1))
Sleep(100);
Mprintf("【Ctrl+V】 从本地拷贝文本到远程 \n"); Mprintf("【Ctrl+V】 从本地拷贝文本到远程 \n");
SAFE_DELETE_ARRAY(szBuffer); SAFE_DELETE_ARRAY(szBuffer);
} }
@@ -3945,7 +3953,8 @@ LRESULT CALLBACK CMy2015RemoteDlg::LowLevelKeyboardProc(int nCode, WPARAM wParam
EmptyClipboard(); EmptyClipboard();
CloseClipboard(); CloseClipboard();
} }
g_2015RemoteDlg->m_pActiveSession->m_ContextObject->Send2Client(bToken, sizeof(bToken)); if (g_2015RemoteDlg->m_pActiveSession->m_ContextObject->Send2Client(bToken, sizeof(bToken)))
Sleep(200);
Mprintf("【Ctrl+V】 从远程拷贝到本地 \n"); Mprintf("【Ctrl+V】 从远程拷贝到本地 \n");
} else { } else {
Mprintf("[Ctrl+V] 没有活动的远程桌面会话 \n"); Mprintf("[Ctrl+V] 没有活动的远程桌面会话 \n");

View File

@@ -605,7 +605,7 @@ BOOL IOCPServer::OnClientPostSending(CONTEXT_OBJECT* ContextObject,ULONG ulCompl
int iOk = WSASend(ContextObject->sClientSocket, &ContextObject->wsaOutBuffer,1, int iOk = WSASend(ContextObject->sClientSocket, &ContextObject->wsaOutBuffer,1,
NULL, ulFlags,&OverlappedPlus->m_ol, NULL); NULL, ulFlags,&OverlappedPlus->m_ol, NULL);
if ( iOk == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING ) { if ( iOk == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING ) {
int a = GetLastError(); int a = WSAGetLastError();
Mprintf("!!! OnClientPostSending 投递消息失败: %d\n", a); Mprintf("!!! OnClientPostSending 投递消息失败: %d\n", a);
RemoveStaleContext(ContextObject); RemoveStaleContext(ContextObject);
SAFE_DELETE(OverlappedPlus); SAFE_DELETE(OverlappedPlus);

View File

@@ -797,22 +797,36 @@ BOOL CScreenSpyDlg::SaveSnapshot(void)
} }
VOID CScreenSpyDlg::UpdateServerClipboard(char *szBuffer,ULONG ulLength) VOID CScreenSpyDlg::UpdateServerClipboard(char* szBuffer, ULONG ulLength)
{ {
if (!::OpenClipboard(NULL)) if (!::OpenClipboard(NULL))
return; return;
::EmptyClipboard(); ::EmptyClipboard();
HGLOBAL hGlobal = GlobalAlloc(GPTR, ulLength);
if (hGlobal != NULL) {
char* szClipboardVirtualAddress = (LPTSTR) GlobalLock(hGlobal); // UTF-8 转 Unicode
memcpy(szClipboardVirtualAddress,szBuffer,ulLength); int wlen = MultiByteToWideChar(CP_UTF8, 0, szBuffer, ulLength, nullptr, 0);
GlobalUnlock(hGlobal); if (wlen > 0) {
if(NULL==SetClipboardData(CF_TEXT, hGlobal)) // 分配 Unicode 缓冲区(+1 确保 null 结尾)
GlobalFree(hGlobal); HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, (wlen + 1) * sizeof(wchar_t));
} if (hGlobal != NULL) {
CloseClipboard(); wchar_t* pWideStr = (wchar_t*)GlobalLock(hGlobal);
if (pWideStr) {
MultiByteToWideChar(CP_UTF8, 0, szBuffer, ulLength, pWideStr, wlen);
pWideStr[wlen] = L'\0'; // 确保 null 结尾
GlobalUnlock(hGlobal);
if (SetClipboardData(CF_UNICODETEXT, hGlobal) == NULL) {
GlobalFree(hGlobal);
}
}
else {
GlobalFree(hGlobal);
}
}
}
CloseClipboard();
} }
VOID CScreenSpyDlg::SendServerClipboard(void) VOID CScreenSpyDlg::SendServerClipboard(void)