From 3c44d422e80cc8079c137f0b5e5387362248521e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Sep 2023 16:56:28 -0400 Subject: [PATCH] add RDP configurable mouse jiggler feature --- dll/ApiHooks.cpp | 146 +++++++++++++++++- dll/MsRdpClient.cpp | 1 + dll/RdpFile.c | 26 +++- dll/RdpInstance.cpp | 128 ++++++++++++++- dll/RdpSettings.cpp | 117 ++++++++++++-- dotnet/Devolutions.MsRdpEx/Bindings.cs | 12 ++ .../Devolutions.MsRdpEx.csproj | 2 +- dotnet/MsRdpEx_App/MainDlg.cs | 26 +++- include/MsRdpEx/RdpFile.h | 3 +- include/MsRdpEx/RdpInstance.h | 8 + include/MsRdpEx/RdpSettings.h | 10 ++ 11 files changed, 453 insertions(+), 26 deletions(-) diff --git a/dll/ApiHooks.cpp b/dll/ApiHooks.cpp index 8e33607..e5352c6 100644 --- a/dll/ApiHooks.cpp +++ b/dll/ApiHooks.cpp @@ -12,6 +12,7 @@ #include +#include #include #include @@ -351,8 +352,6 @@ LRESULT CALLBACK Hook_OPWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (uMsg == WM_NCCREATE) { CREATESTRUCTW* createStruct = (CREATESTRUCTW*) lParam; - void* lpCreateParams = createStruct->lpCreateParams; - MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, createStruct->lpszName, -1, &lpWindowNameA, 0, NULL, NULL); MsRdpEx_LogPrint(DEBUG, "Window Create: %s", lpWindowNameA); } @@ -414,6 +413,147 @@ ATOM Hook_RegisterClassExW(WNDCLASSEXW* wndClassEx) return wndClassAtom; } +static WNDPROC Real_IHWndProc = NULL; + +#define MOUSE_JIGGLER_MOVE_MOUSE_TIMER_ID 4301 +#define MOUSE_JIGGLER_SPECIAL_KEY_TIMER_ID 4302 + +LRESULT CALLBACK Hook_IHWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + char* lpWindowNameA = NULL; + IMsRdpExInstance* instance = NULL; + CMsRdpExtendedSettings* pExtendedSettings = NULL; + + //MsRdpEx_LogPrint(DEBUG, "IHWndProc: %s (%d)", MsRdpEx_GetWindowMessageName(uMsg), uMsg); + + if (uMsg == WM_NCCREATE) + { + CREATESTRUCTW* createStruct = (CREATESTRUCTW*)lParam; + MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, createStruct->lpszName, -1, &lpWindowNameA, 0, NULL, NULL); + MsRdpEx_LogPrint(DEBUG, "Window Create: %s", lpWindowNameA); + } + else + { + instance = (IMsRdpExInstance*)MsRdpEx_InstanceManager_FindByInputCaptureHwnd(hWnd); + + if (instance) + instance->GetExtendedSettings(&pExtendedSettings); + } + + result = Real_IHWndProc(hWnd, uMsg, wParam, lParam); + + if (uMsg == WM_NCCREATE) + { + CREATESTRUCTW* createStruct = (CREATESTRUCTW*)lParam; + void* pUserData = createStruct->lpCreateParams; + + instance = (IMsRdpExInstance*)MsRdpEx_InstanceManager_AttachInputWindow(hWnd, pUserData); + + if (instance) + { + instance->GetExtendedSettings(&pExtendedSettings); + + if (pExtendedSettings) + { + bool mouseJigglerEnabled = pExtendedSettings->GetMouseJigglerEnabled(); + uint32_t mouseJigglerInterval = pExtendedSettings->GetMouseJigglerInterval(); + uint32_t mouseJigglerMethod = pExtendedSettings->GetMouseJigglerMethod(); + + MsRdpEx_LogPrint(DEBUG, "Mouse Jiggler: Enabled=%d, Interval=%d, Method=%d", + mouseJigglerEnabled ? 1 : 0, mouseJigglerInterval, mouseJigglerMethod); + + UINT_PTR timerEventId = MOUSE_JIGGLER_MOVE_MOUSE_TIMER_ID; + + switch (mouseJigglerMethod) + { + case 0: + timerEventId = MOUSE_JIGGLER_MOVE_MOUSE_TIMER_ID; + break; + + case 1: + timerEventId = MOUSE_JIGGLER_SPECIAL_KEY_TIMER_ID; + break; + } + + SetTimer(hWnd, timerEventId, mouseJigglerInterval * 1000, NULL); + } + } + else + { + MsRdpEx_LogPrint(DEBUG, "Failed to attach input window! hWnd: %p pUserData: %p", hWnd, pUserData); + } + } + else if (uMsg == WM_TIMER) + { + if (wParam == MOUSE_JIGGLER_MOVE_MOUSE_TIMER_ID) + { + if (instance) + { + int32_t oldPosX = 0; + int32_t oldPosY = 0; + instance->GetLastMousePosition(&oldPosX, &oldPosY); + + int32_t newPosX = oldPosX + 1; + int32_t newPosY = oldPosY + 1; + SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(newPosX, newPosY)); + SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(oldPosX, oldPosY)); + } + } + else if (wParam == MOUSE_JIGGLER_SPECIAL_KEY_TIMER_ID) + { + if (instance) + { + uint32_t specialKey = VK_F15; // 'F15' is ignored by most applications + SendMessage(hWnd, WM_KEYDOWN, (WPARAM)specialKey, (LPARAM)0x0); + SendMessage(hWnd, WM_KEYUP, (WPARAM)specialKey, (LPARAM)0x0); + } + } + } + else if (uMsg == WM_MOUSEMOVE) + { + int32_t mousePosX = GET_X_LPARAM(lParam); + int32_t mousePosY = GET_Y_LPARAM(lParam); + + if (instance) + { + instance->SetLastMousePosition(mousePosX, mousePosY); + } + } + else if (uMsg == WM_NCDESTROY) + { + KillTimer(hWnd, MOUSE_JIGGLER_MOVE_MOUSE_TIMER_ID); + KillTimer(hWnd, MOUSE_JIGGLER_SPECIAL_KEY_TIMER_ID); + } + + free(lpWindowNameA); + + return result; +} + +ATOM(WINAPI* Real_RegisterClassW)(const WNDCLASSW* wndClass) = RegisterClassW; + +ATOM Hook_RegisterClassW(WNDCLASSW* wndClass) +{ + ATOM wndClassAtom; + char* lpClassNameA = NULL; + + MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, wndClass->lpszClassName, -1, &lpClassNameA, 0, NULL, NULL); + + MsRdpEx_LogPrint(DEBUG, "RegisterClassW: %s", lpClassNameA); + + if (MsRdpEx_StringEquals(lpClassNameA, "IHWindowClass")) { + Real_IHWndProc = wndClass->lpfnWndProc; + wndClass->lpfnWndProc = Hook_IHWndProc; + } + + wndClassAtom = Real_RegisterClassW(wndClass); + + free(lpClassNameA); + + return wndClassAtom; +} + BOOL (WINAPI * Real_CredReadW)(LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALW* Credential) = CredReadW; BOOL Hook_CredReadW(LPCWSTR TargetName, DWORD Type, DWORD Flags, PCREDENTIALW* Credential) @@ -720,6 +860,7 @@ LONG MsRdpEx_AttachHooks() MSRDPEX_DETOUR_ATTACH(Real_BitBlt, Hook_BitBlt); MSRDPEX_DETOUR_ATTACH(Real_StretchBlt, Hook_StretchBlt); MSRDPEX_DETOUR_ATTACH(Real_RegisterClassExW, Hook_RegisterClassExW); + MSRDPEX_DETOUR_ATTACH(Real_RegisterClassW, Hook_RegisterClassW); MSRDPEX_DETOUR_ATTACH(Real_CredReadW, Hook_CredReadW); //MSRDPEX_DETOUR_ATTACH(Real_CryptProtectMemory, Hook_CryptProtectMemory); @@ -768,6 +909,7 @@ LONG MsRdpEx_DetachHooks() MSRDPEX_DETOUR_DETACH(Real_BitBlt, Hook_BitBlt); MSRDPEX_DETOUR_DETACH(Real_StretchBlt, Hook_StretchBlt); MSRDPEX_DETOUR_DETACH(Real_RegisterClassExW, Hook_RegisterClassExW); + MSRDPEX_DETOUR_DETACH(Real_RegisterClassW, Hook_RegisterClassW); MSRDPEX_DETOUR_DETACH(Real_CredReadW, Hook_CredReadW); //MSRDPEX_DETOUR_DETACH(Real_CryptProtectMemory, Hook_CryptProtectMemory); diff --git a/dll/MsRdpClient.cpp b/dll/MsRdpClient.cpp index 7d41eb5..4ab5922 100644 --- a/dll/MsRdpClient.cpp +++ b/dll/MsRdpClient.cpp @@ -497,6 +497,7 @@ class CMsRdpClient : public IMsRdpClient10 CMsRdpExtendedSettings* pMsRdpExtendedSettings = m_pMsRdpExtendedSettings; m_pMsRdpExtendedSettings->LoadRdpFile(NULL); m_pMsRdpExtendedSettings->PrepareSspiSessionIdHack(); + m_pMsRdpExtendedSettings->PrepareMouseJiggler(); hr = m_pMsTscAx->raw_Connect(); diff --git a/dll/RdpFile.c b/dll/RdpFile.c index c4f9eba..2e5505f 100644 --- a/dll/RdpFile.c +++ b/dll/RdpFile.c @@ -53,7 +53,7 @@ bool MsRdpEx_RdpFileEntry_GetBoolValue(MsRdpEx_RdpFileEntry* entry, bool* pValue return true; } -bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, VARIANT* pVariant) +bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant) { bool bVal = false; @@ -67,6 +67,20 @@ bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, VARIANT* pV return true; } +bool MsRdpEx_RdpFileEntry_GetIntValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant) +{ + if (entry->type != 'i') + return false; + + int iValue = atoi(entry->value); + + VariantInit(pVariant); + pVariant->vt = VT_I4; + pVariant->intVal = iValue; + + return true; +} + void MsRdpEx_RdpFileEntry_Free(MsRdpEx_RdpFileEntry* entry) { if (!entry) @@ -108,11 +122,19 @@ char* MsRdpEx_GetRdpFilenameFromCommandLine() goto exit; } - MsRdpEx_LogPrint(DEBUG, "cmdline(argc=%d): %s", argc, cmdlineA); + MsRdpEx_LogPrint(DEBUG, "cmdline(argc=%d): '%s'", argc, cmdlineA); if (argc < 2) goto exit; + for (index = 0; index < argc; index++) + { + char* argA = NULL; + MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, argsW[index], -1, &argA, 0, NULL, NULL); + MsRdpEx_LogPrint(DEBUG, "arg[%d]: '%s'", index, argA); + free(argA); + } + if (MsRdpEx_ConvertFromUnicode(CP_UTF8, 0, argsW[1], -1, &filename, 0, NULL, NULL) < 0) { goto exit; } diff --git a/dll/RdpInstance.cpp b/dll/RdpInstance.cpp index fccd079..1dcdc1b 100644 --- a/dll/RdpInstance.cpp +++ b/dll/RdpInstance.cpp @@ -174,6 +174,12 @@ class CMsRdpExInstance : public IMsRdpExInstance return S_OK; } + HRESULT STDMETHODCALLTYPE AttachInputWindow(HWND hInputWnd, void* pUserData) + { + m_hInputCaptureWnd = hInputWnd; + return S_OK; + } + HRESULT STDMETHODCALLTYPE AttachOutputWindow(HWND hOutputWnd, void* pUserData) { m_hOutputPresenterWnd = hOutputWnd; @@ -187,6 +193,12 @@ class CMsRdpExInstance : public IMsRdpExInstance return S_OK; } + bool STDMETHODCALLTYPE GetExtendedSettings(CMsRdpExtendedSettings** ppExtendedSettings) + { + *ppExtendedSettings = m_pMsRdpExtendedSettings; + return m_pMsRdpExtendedSettings ? true : false; + } + bool STDMETHODCALLTYPE GetShadowBitmap(HDC* phDC, HBITMAP* phBitmap, uint8_t** pBitmapData, uint32_t* pBitmapWidth, uint32_t* pBitmapHeight, uint32_t* pBitmapStep) { @@ -219,6 +231,18 @@ class CMsRdpExInstance : public IMsRdpExInstance MsRdpEx_OutputMirror_Unlock(outputMirror); } + void STDMETHODCALLTYPE GetLastMousePosition(int32_t* posX, int32_t* posY) + { + *posX = m_LastMousePosX; + *posY = m_LastMousePosY; + } + + void STDMETHODCALLTYPE SetLastMousePosition(int32_t posX, int32_t posY) + { + m_LastMousePosX = posX; + m_LastMousePosY = posY; + } + public: GUID m_sessionId; ULONG m_refCount = NULL; @@ -226,10 +250,13 @@ class CMsRdpExInstance : public IMsRdpExInstance bool m_videoRecordingEnabled = false; bool m_dumpBitmapUpdates = false; CMsRdpClient* m_pMsRdpClient = NULL; + HWND m_hInputCaptureWnd = NULL; HWND m_hOutputPresenterWnd = NULL; MsRdpEx_OutputMirror* m_OutputMirror = NULL; ITSPropertySet* m_pCorePropsRaw = NULL; CMsRdpExtendedSettings* m_pMsRdpExtendedSettings = NULL; + int32_t m_LastMousePosX = 0; + int32_t m_LastMousePosY = 0; }; CMsRdpExInstance* CMsRdpExInstance_New(CMsRdpClient* pMsRdpClient) @@ -318,7 +345,6 @@ CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, vo size_t maxPtrCount = 200; ITSPropertySet* pTSCoreProps = NULL; - ITSPropertySet* pTSBaseProps = NULL; for (int i = 0; i < maxPtrCount; i++) { ITSObjectBase** ppTSObject = (ITSObjectBase**)&((size_t*)pUserData)[i]; @@ -334,9 +360,7 @@ CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, vo if (!pTSCoreProps && TsPropertyMap_IsCoreProps(pTSProps)) { pTSCoreProps = pTSProps; - } - else if (!pTSBaseProps && TsPropertyMap_IsBaseProps(pTSProps)) { - pTSBaseProps = pTSProps; + break; } } } @@ -362,8 +386,6 @@ CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, vo obj->GetCorePropsRawPtr(&pCorePropsRaw2); - MsRdpEx_LogPrint(DEBUG, "pCorePropsRaw: %p == %p", pCorePropsRaw1, pCorePropsRaw2); - if (pCorePropsRaw1 == pCorePropsRaw2) { found = true; @@ -380,6 +402,100 @@ CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, vo return found ? obj : NULL; } +CMsRdpExInstance* MsRdpEx_InstanceManager_FindByInputCaptureHwnd(HWND hWnd) +{ + MsRdpEx_InstanceManager* ctx = g_InstanceManager; + + if (!ctx) + return NULL; + + bool found = false; + CMsRdpExInstance* obj = NULL; + MsRdpEx_ArrayListIt* it = NULL; + + it = MsRdpEx_ArrayList_It(ctx->instances, MSRDPEX_ITERATOR_FLAG_EXCLUSIVE); + + while (!MsRdpEx_ArrayListIt_Done(it)) + { + obj = (CMsRdpExInstance*)MsRdpEx_ArrayListIt_Next(it); + + found = (obj->m_hInputCaptureWnd == hWnd) ? true : false; + + if (found) + break; + } + + MsRdpEx_ArrayListIt_Finish(it); + + return found ? obj : NULL; +} + +CMsRdpExInstance* MsRdpEx_InstanceManager_AttachInputWindow(HWND hInputWnd, void* pUserData) +{ + MsRdpEx_InstanceManager* ctx = g_InstanceManager; + + if (!ctx) + return NULL; + + size_t maxPtrCount = 200; + ITSPropertySet* pTSCoreProps = NULL; + + for (int i = 0; i < maxPtrCount; i++) { + ITSObjectBase** ppTSObject = (ITSObjectBase**)&((size_t*)pUserData)[i]; + if (MsRdpEx_CanReadUnsafePtr(ppTSObject, 8)) { + ITSObjectBase* pTSObject = *ppTSObject; + if (MsRdpEx_CanReadUnsafePtr(pTSObject, sizeof(ITSObjectBase))) { + if (pTSObject->marker == TSOBJECT_MARKER) { + MsRdpEx_LogPrint(DEBUG, "CIHWnd(%d): 0x%08X name: %s refCount: %d", + i, (size_t)pTSObject, pTSObject->name, pTSObject->refCount); + + if (MsRdpEx_StringEqualsUnsafePtr(pTSObject->name, "CTSPropertySet")) { + ITSPropertySet* pTSProps = (ITSPropertySet*)pTSObject; + + if (!pTSCoreProps && TsPropertyMap_IsCoreProps(pTSProps)) { + pTSCoreProps = pTSProps; + break; + } + } + } + } + } + } + + void* pCorePropsRaw1 = (void*)pTSCoreProps; + void* pCorePropsRaw2 = NULL; + + if (!pCorePropsRaw1) + return NULL; + + bool found = false; + CMsRdpExInstance* obj = NULL; + MsRdpEx_ArrayListIt* it = NULL; + + it = MsRdpEx_ArrayList_It(ctx->instances, MSRDPEX_ITERATOR_FLAG_EXCLUSIVE); + + while (!MsRdpEx_ArrayListIt_Done(it)) + { + obj = (CMsRdpExInstance*)MsRdpEx_ArrayListIt_Next(it); + + obj->GetCorePropsRawPtr(&pCorePropsRaw2); + + if (pCorePropsRaw1 == pCorePropsRaw2) + { + found = true; + break; + } + } + + MsRdpEx_ArrayListIt_Finish(it); + + if (found) { + obj->AttachInputWindow(hInputWnd, pUserData); + } + + return found ? obj : NULL; +} + CMsRdpExInstance* MsRdpEx_InstanceManager_FindBySessionId(GUID* sessionId) { MsRdpEx_InstanceManager* ctx = g_InstanceManager; diff --git a/dll/RdpSettings.cpp b/dll/RdpSettings.cpp index b446b6e..21710dc 100644 --- a/dll/RdpSettings.cpp +++ b/dll/RdpSettings.cpp @@ -425,6 +425,31 @@ HRESULT __stdcall CMsRdpExtendedSettings::put_Property(BSTR bstrPropertyName, VA } free(propValue); + hr = S_OK; + } + else if (MsRdpEx_StringEquals(propName, "EnableMouseJiggler")) + { + if (pValue->vt != VT_BOOL) + goto end; + + m_MouseJigglerEnabled = pValue->boolVal ? true : false; + hr = S_OK; + } + else if (MsRdpEx_StringEquals(propName, "MouseJigglerInterval")) + { + if ((pValue->vt != VT_UI4) && (pValue->vt != VT_I4)) + goto end; + + m_MouseJigglerInterval = (uint32_t) pValue->uintVal; + hr = S_OK; + } + else if (MsRdpEx_StringEquals(propName, "MouseJigglerMethod")) + { + if ((pValue->vt != VT_UI4) && (pValue->vt != VT_I4)) + goto end; + + m_MouseJigglerMethod = (uint32_t) pValue->uintVal; + hr = S_OK; } else { @@ -492,6 +517,21 @@ HRESULT __stdcall CMsRdpExtendedSettings::get_Property(BSTR bstrPropertyName, VA pValue->bstrVal = _com_util::ConvertStringToBSTR(kdcProxyUrl); hr = S_OK; } + else if (MsRdpEx_StringEquals(propName, "EnableMouseJiggler")) { + pValue->vt = VT_BOOL; + pValue->boolVal = m_MouseJigglerEnabled ? VARIANT_TRUE : VARIANT_FALSE; + hr = S_OK; + } + else if (MsRdpEx_StringEquals(propName, "MouseJigglerInterval")) { + pValue->vt = VT_I4; + pValue->intVal = (INT) m_MouseJigglerInterval; + hr = S_OK; + } + else if (MsRdpEx_StringEquals(propName, "MouseJigglerMethod")) { + pValue->vt = VT_I4; + pValue->intVal = (INT) m_MouseJigglerMethod; + hr = S_OK; + } else { hr = m_pMsRdpExtendedSettings->get_Property(bstrPropertyName, pValue); } @@ -660,31 +700,30 @@ HRESULT CMsRdpExtendedSettings::LoadRdpFile(const char* rdpFileName) while (!MsRdpEx_ArrayListIt_Done(it)) { + VARIANT value; + VariantInit(&value); + entry = (MsRdpEx_RdpFileEntry*) MsRdpEx_ArrayListIt_Next(it); if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "DisableCredentialsDelegation")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->PutProperty(propName, &value); } } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "RedirectedAuthentication")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->PutProperty(propName, &value); } } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "RestrictedLogon")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->PutProperty(propName, &value); } } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 's', "UserSpecifiedServerName")) { - VARIANT value; bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); bstr_t propValue = _com_util::ConvertStringToBSTR(entry->value); value.bstrVal = propValue; @@ -692,26 +731,47 @@ HRESULT CMsRdpExtendedSettings::LoadRdpFile(const char* rdpFileName) pMsRdpExtendedSettings->put_CoreProperty(propName, &value); } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "DisableUDPTransport")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->put_CoreProperty(propName, &value); } } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "ConnectToChildSession")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->put_CoreProperty(propName, &value); } } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "EnableHardwareMode")) { - VARIANT value; if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); pMsRdpExtendedSettings->put_Property(propName, &value); } } + else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "AllowBackgroundInput")) { + if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { + bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); + pMsRdpExtendedSettings->put_BaseProperty(propName, &value); + } + } + else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "EnableMouseJiggler")) { + if (MsRdpEx_RdpFileEntry_GetVBoolValue(entry, &value)) { + bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); + pMsRdpExtendedSettings->put_Property(propName, &value); + } + } + else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "MouseJigglerInterval")) { + if (MsRdpEx_RdpFileEntry_GetIntValue(entry, &value)) { + bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); + pMsRdpExtendedSettings->put_Property(propName, &value); + } + } + else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 'i', "MouseJigglerMethod")) { + if (MsRdpEx_RdpFileEntry_GetIntValue(entry, &value)) { + bstr_t propName = _com_util::ConvertStringToBSTR(entry->name); + pMsRdpExtendedSettings->put_Property(propName, &value); + } + } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 's', "ClearTextPassword")) { pMsRdpExtendedSettings->SetTargetPassword(entry->value); } @@ -719,7 +779,6 @@ HRESULT CMsRdpExtendedSettings::LoadRdpFile(const char* rdpFileName) pMsRdpExtendedSettings->SetGatewayPassword(entry->value); } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 's', "TargetUserName")) { - VARIANT value; bstr_t propName = _com_util::ConvertStringToBSTR("UserName"); bstr_t propValue = _com_util::ConvertStringToBSTR(entry->value); value.bstrVal = propValue; @@ -727,7 +786,6 @@ HRESULT CMsRdpExtendedSettings::LoadRdpFile(const char* rdpFileName) pMsRdpExtendedSettings->put_CoreProperty(propName, &value); } else if (MsRdpEx_RdpFileEntry_IsMatch(entry, 's', "TargetDomain")) { - VARIANT value; bstr_t propName = _com_util::ConvertStringToBSTR("Domain"); bstr_t propValue = _com_util::ConvertStringToBSTR(entry->value); value.bstrVal = propValue; @@ -774,6 +832,32 @@ HRESULT CMsRdpExtendedSettings::PrepareSspiSessionIdHack() return hr; } +HRESULT CMsRdpExtendedSettings::PrepareMouseJiggler() +{ + HRESULT hr = S_OK; + VARIANT enableMouseJiggler; + VARIANT allowBackgroundInput; + bstr_t enableMouseJigglerName = _com_util::ConvertStringToBSTR("EnableMouseJiggler"); + bstr_t allowBackgroundInputName = _com_util::ConvertStringToBSTR("AllowBackgroundInput"); + + if (!m_BaseProps) { + MsRdpEx_LogPrint(ERROR, "PrepareMouseJiggler - m_BaseProps is NULL!"); + return E_UNEXPECTED; + } + + VariantInit(&enableMouseJiggler); + VariantInit(&allowBackgroundInput); + hr = this->get_Property(enableMouseJigglerName, &enableMouseJiggler); + hr = this->get_BaseProperty(allowBackgroundInputName, &allowBackgroundInput); + + if (enableMouseJiggler.boolVal == VARIANT_TRUE) { + allowBackgroundInput.boolVal = VARIANT_TRUE; + m_BaseProps->put_Property(allowBackgroundInputName, &allowBackgroundInput); + } + + return hr; +} + char* CMsRdpExtendedSettings::GetKdcProxyUrl() { if (m_KdcProxyUrl) @@ -787,6 +871,21 @@ char* CMsRdpExtendedSettings::GetKdcProxyName() return MsRdpEx_KdcProxyUrlToName(m_KdcProxyUrl); } +bool CMsRdpExtendedSettings::GetMouseJigglerEnabled() +{ + return m_MouseJigglerEnabled; +} + +uint32_t CMsRdpExtendedSettings::GetMouseJigglerInterval() +{ + return m_MouseJigglerInterval; +} + +uint32_t CMsRdpExtendedSettings::GetMouseJigglerMethod() +{ + return m_MouseJigglerMethod; +} + CMsRdpExtendedSettings* CMsRdpExtendedSettings_New(IUnknown* pUnknown, IUnknown* pMsTscAx, GUID* pSessionId) { CMsRdpExtendedSettings* settings = new CMsRdpExtendedSettings(pUnknown, pSessionId); diff --git a/dotnet/Devolutions.MsRdpEx/Bindings.cs b/dotnet/Devolutions.MsRdpEx/Bindings.cs index f63699d..87378c0 100644 --- a/dotnet/Devolutions.MsRdpEx/Bindings.cs +++ b/dotnet/Devolutions.MsRdpEx/Bindings.cs @@ -117,10 +117,16 @@ public interface IMsRdpExInstance void SetCorePropsRawPtr(IntPtr pCorePropsRaw); + void AttachInputWindow(IntPtr hInputWnd, IntPtr pUserData); + void AttachOutputWindow(IntPtr hOutputWnd, IntPtr pUserData); void AttachExtendedSettings(IntPtr pExtendedSettings); + [MethodImpl(MethodImplOptions.PreserveSig)] + [return: MarshalAs(UnmanagedType.U1)] + bool GetExtendedSettings(out IntPtr ppExtendedSettings); + [MethodImpl(MethodImplOptions.PreserveSig)] [return: MarshalAs(UnmanagedType.U1)] bool GetShadowBitmap(ref IntPtr phDC, ref IntPtr phBitmap, ref IntPtr pBitmapData, @@ -131,6 +137,12 @@ bool GetShadowBitmap(ref IntPtr phDC, ref IntPtr phBitmap, ref IntPtr pBitmapDat [MethodImpl(MethodImplOptions.PreserveSig)] void UnlockShadowBitmap(); + + [MethodImpl(MethodImplOptions.PreserveSig)] + void GetLastMousePosition(ref Int32 posX, ref Int32 posY); + + [MethodImpl(MethodImplOptions.PreserveSig)] + void SetLastMousePosition(Int32 posX, Int32 posY); } public static class Bindings diff --git a/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj b/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj index d0ec384..b652876 100644 --- a/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj +++ b/dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj @@ -3,7 +3,7 @@ Devolutions.MsRdpEx Devolutions.MsRdpEx - 2023.9.19.0 + 2023.9.28.0 mamoreau@devolutions.net Devolutions Microsoft RDP Extensions diff --git a/dotnet/MsRdpEx_App/MainDlg.cs b/dotnet/MsRdpEx_App/MainDlg.cs index a7c3c8a..7a7898d 100644 --- a/dotnet/MsRdpEx_App/MainDlg.cs +++ b/dotnet/MsRdpEx_App/MainDlg.cs @@ -351,6 +351,22 @@ private void ParseRdpFile(string filename, AxMSTSCLib.AxMsRdpClient9NotSafeForSc case "use redirection server name": redirectionInfo.UseRedirectionServerName = bValue; break; + + case "allowbackgroundinput": + advancedSettings.allowBackgroundInput = bValue ? 1 : 0; + break; + + case "enablemousejiggler": + extendedSettings.set_Property("EnableMouseJiggler", bValue); + break; + + case "mousejigglerinterval": + extendedSettings.set_Property("MouseJigglerInterval", iValue); + break; + + case "mousejigglermethod": + extendedSettings.set_Property("MouseJigglerMethod", iValue); + break; } } } @@ -369,17 +385,17 @@ private void btnConnect_Click(object sender, EventArgs e) if (externalMode) { - string filename = this.mstscExecutable; + string executableFileName = this.mstscExecutable; if (axName.Equals("msrdc")) { - filename = this.msrdcExecutable; + executableFileName = this.msrdcExecutable; } - string workingDirectory = Path.GetDirectoryName(filename); + string workingDirectory = Path.GetDirectoryName(executableFileName); List args = new List(); - args.Add(filename); + args.Add('"' + executableFileName + '"'); if (this.rdpFileName != null) { @@ -389,7 +405,7 @@ private void btnConnect_Click(object sender, EventArgs e) string arguments = string.Join(' ', args.ToArray()); ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.FileName = filename; + startInfo.FileName = executableFileName; startInfo.UseShellExecute = false; startInfo.WorkingDirectory = workingDirectory; startInfo.Arguments = arguments; diff --git a/include/MsRdpEx/RdpFile.h b/include/MsRdpEx/RdpFile.h index 43a8b77..e7cd05a 100644 --- a/include/MsRdpEx/RdpFile.h +++ b/include/MsRdpEx/RdpFile.h @@ -22,7 +22,8 @@ typedef struct _MsRdpEx_RdpFileEntry MsRdpEx_RdpFileEntry; bool MsRdpEx_RdpFileEntry_IsMatch(MsRdpEx_RdpFileEntry* entry, char type, const char* name); bool MsRdpEx_RdpFileEntry_GetBoolValue(MsRdpEx_RdpFileEntry* entry, bool* pValue); -bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, VARIANT* pVariant); +bool MsRdpEx_RdpFileEntry_GetVBoolValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant); +bool MsRdpEx_RdpFileEntry_GetIntValue(MsRdpEx_RdpFileEntry* entry, _Out_ VARIANT* pVariant); MsRdpEx_RdpFileEntry* MsRdpEx_RdpFileEntry_New(char type, const char* name, const char* value); void MsRdpEx_RdpFileEntry_Free(MsRdpEx_RdpFileEntry* entry); diff --git a/include/MsRdpEx/RdpInstance.h b/include/MsRdpEx/RdpInstance.h index 4f03b65..e98d80b 100644 --- a/include/MsRdpEx/RdpInstance.h +++ b/include/MsRdpEx/RdpInstance.h @@ -23,12 +23,16 @@ struct __declspec(novtable) virtual HRESULT __stdcall SetDumpBitmapUpdates(bool dumpBitmapUpdates) = 0; virtual HRESULT __stdcall GetCorePropsRawPtr(LPVOID* ppCorePropsRaw) = 0; virtual HRESULT __stdcall SetCorePropsRawPtr(LPVOID pCorePropsRaw) = 0; + virtual HRESULT __stdcall AttachInputWindow(HWND hOutputWnd, void* pUserData) = 0; virtual HRESULT __stdcall AttachOutputWindow(HWND hOutputWnd, void* pUserData) = 0; virtual HRESULT __stdcall AttachExtendedSettings(CMsRdpExtendedSettings* pExtendedSettings) = 0; + virtual bool __stdcall GetExtendedSettings(CMsRdpExtendedSettings** ppExtendedSettings) = 0; virtual bool __stdcall GetShadowBitmap(HDC* phDC, HBITMAP* phBitmap, uint8_t** pBitmapData, uint32_t* pBitmapWidth, uint32_t* pBitmapHeight, uint32_t* pBitmapStep) = 0; virtual void __stdcall LockShadowBitmap() = 0; virtual void __stdcall UnlockShadowBitmap() = 0; + virtual void __stdcall GetLastMousePosition(int32_t* posX, int32_t* posY) = 0; + virtual void __stdcall SetLastMousePosition(int32_t posX, int32_t posY) = 0; }; class CMsRdpExInstance; @@ -47,6 +51,10 @@ CMsRdpExInstance* MsRdpEx_InstanceManager_FindByOutputPresenterHwnd(HWND hWnd); CMsRdpExInstance* MsRdpEx_InstanceManager_AttachOutputWindow(HWND hOutputWnd, void* pUserData); +CMsRdpExInstance* MsRdpEx_InstanceManager_FindByInputCaptureHwnd(HWND hWnd); + +CMsRdpExInstance* MsRdpEx_InstanceManager_AttachInputWindow(HWND hInputWnd, void* pUserData); + CMsRdpExInstance* MsRdpEx_InstanceManager_FindBySessionId(GUID* sessionId); CMsRdpExtendedSettings* MsRdpEx_FindExtendedSettingsBySessionId(GUID* sessionId); diff --git a/include/MsRdpEx/RdpSettings.h b/include/MsRdpEx/RdpSettings.h index ab9df40..6810f6f 100644 --- a/include/MsRdpEx/RdpSettings.h +++ b/include/MsRdpEx/RdpSettings.h @@ -7,6 +7,9 @@ #include +#define MOUSE_JIGGLER_METHOD_MOUSE_MOVE 0 +#define MOUSE_JIGGLER_METHOD_SPECIAL_KEY 1 + class CMsRdpExtendedSettings; class CMsRdpPropertySet; @@ -40,8 +43,12 @@ class CMsRdpExtendedSettings : public IMsRdpExtendedSettings HRESULT __stdcall LoadRdpFile(const char* rdpFileName); HRESULT __stdcall GetCorePropsRawPtr(LPVOID* ppCorePropsRaw); HRESULT __stdcall PrepareSspiSessionIdHack(); + HRESULT __stdcall PrepareMouseJiggler(); char* __stdcall GetKdcProxyUrl(); char* __stdcall GetKdcProxyName(); + bool GetMouseJigglerEnabled(); + uint32_t GetMouseJigglerInterval(); + uint32_t GetMouseJigglerMethod(); private: GUID m_sessionId; @@ -54,6 +61,9 @@ class CMsRdpExtendedSettings : public IMsRdpExtendedSettings CMsRdpPropertySet* m_BaseProps = NULL; CMsRdpPropertySet* m_TransportProps = NULL; char* m_KdcProxyUrl = NULL; + bool m_MouseJigglerEnabled = false; + uint32_t m_MouseJigglerInterval = 60; + uint32_t m_MouseJigglerMethod = 0; }; #ifdef __cplusplus