Feature: Support copy text from remote with Ctrl+V

This commit is contained in:
yuanyuanxiang
2025-10-20 03:56:54 +08:00
parent 9c8f6fa3aa
commit 11a77fda06
5 changed files with 175 additions and 0 deletions

View File

@@ -494,6 +494,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_EXECUTE_DOWNLOAD, &CMy2015RemoteDlg::OnExecuteDownload)
ON_COMMAND(ID_EXECUTE_UPLOAD, &CMy2015RemoteDlg::OnExecuteUpload)
ON_COMMAND(ID_MACHINE_LOGOUT, &CMy2015RemoteDlg::OnMachineLogout)
ON_WM_DESTROY()
ON_MESSAGE(WM_SESSION_ACTIVATED, &CMy2015RemoteDlg::OnSessionActivatedMsg)
END_MESSAGE_MAP()
@@ -962,6 +964,9 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
{
AUTO_TICK(500);
CDialogEx::OnInitDialog();
g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, AfxGetInstanceHandle(), 0);
m_GroupList = {"default"};
// Grid 容器
int size = THIS_CFG.GetInt("settings", "VideoWallSize");
@@ -3428,3 +3433,142 @@ void CMy2015RemoteDlg::OnExecuteUpload()
{
TODO_NOTICE;
}
void CMy2015RemoteDlg::OnDestroy()
{
if (g_hKeyboardHook)
{
UnhookWindowsHookEx(g_hKeyboardHook);
g_hKeyboardHook = NULL;
}
CDialogEx::OnDestroy();
}
CString GetClipboardText()
{
if (!OpenClipboard(nullptr)) return _T("");
#ifdef UNICODE
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
#else
HANDLE hData = GetClipboardData(CF_TEXT);
#endif
if (!hData) { CloseClipboard(); 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)
{
if (!OpenClipboard(nullptr)) return;
EmptyClipboard();
#ifdef UNICODE
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, (text.GetLength() + 1) * sizeof(wchar_t));
wchar_t* p = static_cast<wchar_t*>(GlobalLock(hGlob));
if (p) wcscpy_s(p, text.GetLength() + 1, text);
#else
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, (text.GetLength() + 1) * sizeof(char));
char* p = static_cast<char*>(GlobalLock(hGlob));
if (p) strcpy_s(p, text.GetLength() + 1, CT2A(text)); // CT2A 宏把 CString 转成 char*
#endif
GlobalUnlock(hGlob);
#ifdef UNICODE
SetClipboardData(CF_UNICODETEXT, hGlob);
#else
SetClipboardData(CF_TEXT, hGlob);
#endif
CloseClipboard();
}
CDialogBase* CMy2015RemoteDlg::GetRemoteWindow(HWND hWnd)
{
if (!::IsWindow(hWnd)) return FALSE;
EnterCriticalSection(&m_cs);
auto find = m_RemoteWnds.find(hWnd);
auto ret = find == m_RemoteWnds.end() ? NULL : find->second;
LeaveCriticalSection(&m_cs);
return ret;
}
void CMy2015RemoteDlg::RemoveRemoteWindow(HWND wnd) {
EnterCriticalSection(&m_cs);
m_RemoteWnds.erase(wnd);
LeaveCriticalSection(&m_cs);
}
LRESULT CALLBACK CMy2015RemoteDlg::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
do {
static CDialogBase* operateWnd = nullptr;
KBDLLHOOKSTRUCT* pKey = (KBDLLHOOKSTRUCT*)lParam;
// 只在按下时处理
if (wParam == WM_KEYDOWN)
{
// 检测 Ctrl+C / Ctrl+X
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (pKey->vkCode == 'C' || pKey->vkCode == 'X')) {
HWND hFore = ::GetForegroundWindow();
operateWnd = g_2015RemoteDlg->GetRemoteWindow(hFore);
if (!operateWnd)
g_2015RemoteDlg->m_pActiveSession = nullptr;
}
// 检测 Ctrl+V
else if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && pKey->vkCode == 'V')
{
HWND hFore = ::GetForegroundWindow();
CDialogBase* dlg = g_2015RemoteDlg->GetRemoteWindow(hFore);
if (dlg)
{
if (dlg == operateWnd)break;
// [1] 本地 -> 远程
CString strText = GetClipboardText();
if (!strText.IsEmpty()) {
BYTE* szBuffer = new BYTE[strText.GetLength() + 1];
szBuffer[0] = COMMAND_SCREEN_SET_CLIPBOARD;
memcpy(szBuffer + 1, strText.GetString(), strText.GetLength());
dlg->m_ContextObject->Send2Client(szBuffer, strText.GetLength() + 1);
Mprintf("【Ctrl+V】 从本地拷贝到远程 \n");
SAFE_DELETE_ARRAY(szBuffer);
}
}
else if (g_2015RemoteDlg->m_pActiveSession)
{
// [2] 远程 -> 本地
BYTE bToken = COMMAND_SCREEN_GET_CLIPBOARD;
g_2015RemoteDlg->m_pActiveSession->m_ContextObject->Send2Client(&bToken, sizeof(bToken));
Mprintf("【Ctrl+V】 从远程拷贝到本地 \n");
}
else
{
Mprintf("[Ctrl+V] 没有活动的远程桌面会话 \n");
}
}
}
} while (0);
}
// 允许消息继续传递
return CallNextHookEx(g_2015RemoteDlg->g_hKeyboardHook, nCode, wParam, lParam);
}
LRESULT CMy2015RemoteDlg::OnSessionActivatedMsg(WPARAM wParam, LPARAM lParam)
{
CDialogBase* pSession = reinterpret_cast<CDialogBase*>(wParam);
m_pActiveSession = pSession;
return 0;
}

View File

@@ -185,6 +185,8 @@ public:
ContextObject->hWnd = Dlg->GetSafeHwnd();
ContextObject->hDlg = Dlg;
if(id == IDD_DIALOG_SCREEN_SPY)
m_RemoteWnds[Dlg->GetSafeHwnd()]=(CDialogBase*)Dlg;
return 0;
}
@@ -230,6 +232,13 @@ public:
CMenu m_MainMenu;
CBitmap m_bmOnline[18];
uint64_t m_superID;
std::map<HWND, CDialogBase *> m_RemoteWnds;
CDialogBase* GetRemoteWindow(HWND hWnd);
void RemoveRemoteWindow(HWND wnd);
CDialogBase* m_pActiveSession = nullptr; // <20><>ǰ<EFBFBD><EFBFBD><EFBFBD><E1BBB0><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8> / NULL <20><>ʾ<EFBFBD><CABE>
afx_msg LRESULT OnSessionActivatedMsg(WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
HHOOK g_hKeyboardHook = NULL;
enum {
STATUS_UNKNOWN = -1,
STATUS_RUN = 0,
@@ -330,4 +339,5 @@ public:
afx_msg void OnExecuteUpload();
afx_msg void OnMachineLogout();
void MachineManage(MachineCommand type);
afx_msg void OnDestroy();
};

View File

@@ -8,6 +8,7 @@
#include <imm.h>
#include <WinUser.h>
#include "CGridDialog.h"
#include "2015RemoteDlg.h"
// CScreenSpyDlg 对话框
@@ -130,6 +131,7 @@ BEGIN_MESSAGE_MAP(CScreenSpyDlg, CDialog)
ON_WM_KILLFOCUS()
ON_WM_SIZE()
ON_WM_LBUTTONDBLCLK()
ON_WM_ACTIVATE()
END_MESSAGE_MAP()
@@ -218,6 +220,9 @@ VOID CScreenSpyDlg::OnClose()
CWnd* parent = GetParent();
if (parent)
parent->SendMessage(WM_CHILD_CLOSED, (WPARAM)this, 0);
extern CMy2015RemoteDlg *g_2015RemoteDlg;
if(g_2015RemoteDlg)
g_2015RemoteDlg->RemoveRemoteWindow(GetSafeHwnd());
// 等待数据处理完毕
if (IsProcessing()) {
@@ -861,3 +866,17 @@ void CScreenSpyDlg::OnSize(UINT nType, int cx, int cy)
m_wZoom = ((double)m_BitmapInfor_Full->bmiHeader.biWidth) / ((double)(m_CRect.Width()));
m_hZoom = ((double)m_BitmapInfor_Full->bmiHeader.biHeight) / ((double)(m_CRect.Height()));
}
void CScreenSpyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CDialogBase::OnActivate(nState, pWndOther, bMinimized);
CWnd* pMain = AfxGetMainWnd();
if (!pMain)
return;
if (nState != WA_INACTIVE){
// 通知主窗口:远程窗口获得焦点
::PostMessage(pMain->GetSafeHwnd(), WM_SESSION_ACTIVATED, (WPARAM)this, 0);
}
}

View File

@@ -109,6 +109,7 @@ public:
afx_msg void OnMouseLeave();
afx_msg void OnKillFocus(CWnd* pNewWnd);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV ֧<><D6A7>

View File

@@ -84,6 +84,7 @@
#define WM_PASSWORDCHECK WM_USER+3021
#define WM_SHOWMESSAGE WM_USER+3022
#define WM_SHOWERRORMSG WM_USER+3023
#define WM_SESSION_ACTIVATED WM_USER+3024
#ifdef _UNICODE
#if defined _M_IX86