--- sourceforge.net/trunk/seamlessrdp/ServerExe/HookDll/hookdll.c 2006/03/09 16:13:26 1081 +++ sourceforge.net/trunk/seamlessrdp/ServerExe/HookDll/hookdll.c 2006/03/20 14:35:02 1167 @@ -32,18 +32,43 @@ #define DLL_EXPORT __declspec(dllexport) +#ifdef __GNUC__ +#define SHARED __attribute__((section ("SHAREDDATA"), shared)) +#else +#define SHARED +#endif + // Shared DATA #pragma data_seg ( "SHAREDDATA" ) // this is the total number of processes this dll is currently attached to -int g_instance_count = 0; +int g_instance_count SHARED = 0; + +// blocks for locally generated events +HWND g_block_move_hwnd SHARED = NULL; +unsigned int g_block_move_serial SHARED = 0; +RECT g_block_move SHARED = { 0, 0, 0, 0 }; + +unsigned int g_blocked_zchange_serial SHARED = 0; +HWND g_blocked_zchange[2] SHARED = { NULL, NULL }; + +unsigned int g_blocked_focus_serial SHARED = 0; +HWND g_blocked_focus SHARED = NULL; + +unsigned int g_blocked_state_serial SHARED = 0; +HWND g_blocked_state_hwnd SHARED = NULL; +int g_blocked_state SHARED = -1; #pragma data_seg () #pragma comment(linker, "/section:SHAREDDATA,rws") +#define FOCUS_MSG_NAME "WM_SEAMLESS_FOCUS" +static UINT g_wm_seamless_focus; + static HHOOK g_cbt_hook = NULL; static HHOOK g_wndproc_hook = NULL; +static HHOOK g_wndprocret_hook = NULL; static HINSTANCE g_instance = NULL; @@ -52,23 +77,76 @@ static void update_position(HWND hwnd) { - RECT rect; + RECT rect, blocked; + HWND blocked_hwnd; + unsigned int serial; + + WaitForSingleObject(g_mutex, INFINITE); + blocked_hwnd = g_block_move_hwnd; + serial = g_block_move_serial; + memcpy(&blocked, &g_block_move, sizeof(RECT)); + ReleaseMutex(g_mutex); + + vchannel_block(); if (!GetWindowRect(hwnd, &rect)) { debug("GetWindowRect failed!\n"); - return; + goto end; } - vchannel_write("POSITION1,0x%p,%d,%d,%d,%d,0x%x", + if ((hwnd == blocked_hwnd) && (rect.left == blocked.left) && (rect.top == blocked.top) + && (rect.right == blocked.right) && (rect.bottom == blocked.bottom)) + goto end; + + vchannel_write("POSITION", "0x%p,%d,%d,%d,%d,0x%x", hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); + + end: + vchannel_unblock(); +} + +static void +update_zorder(HWND hwnd) +{ + HWND behind; + HWND block_hwnd, block_behind; + unsigned int serial; + + WaitForSingleObject(g_mutex, INFINITE); + serial = g_blocked_zchange_serial; + block_hwnd = g_blocked_zchange[0]; + block_behind = g_blocked_zchange[1]; + ReleaseMutex(g_mutex); + + vchannel_block(); + + behind = GetNextWindow(hwnd, GW_HWNDPREV); + while (behind) + { + LONG style; + + style = GetWindowLong(behind, GWL_STYLE); + + if ((!(style & WS_CHILD) || (style & WS_POPUP)) && (style & WS_VISIBLE)) + break; + + behind = GetNextWindow(behind, GW_HWNDPREV); + } + + if ((hwnd == block_hwnd) && (behind == block_behind)) + vchannel_write("ACK", "%u", serial); + else + vchannel_write("ZCHANGE", "0x%p,0x%p,0x%x", hwnd, behind, 0); + + vchannel_unblock(); } static LRESULT CALLBACK wndproc_hook_proc(int code, WPARAM cur_thread, LPARAM details) { - HWND hwnd; + HWND hwnd, parent; UINT msg; WPARAM wparam; LPARAM lparam; @@ -85,57 +163,78 @@ style = GetWindowLong(hwnd, GWL_STYLE); - if (style & WS_CHILD) + /* Docs say that WS_CHILD and WS_POPUP is an illegal combination, + but they exist nonetheless. */ + if ((style & WS_CHILD) && !(style & WS_POPUP)) goto end; - switch (msg) + if (style & WS_POPUP) { + parent = (HWND) GetWindowLong(hwnd, GWL_HWNDPARENT); + if (!parent) + parent = (HWND) - 1; + } + else + parent = NULL; + switch (msg) + { case WM_WINDOWPOSCHANGED: { WINDOWPOS *wp = (WINDOWPOS *) lparam; if (wp->flags & SWP_SHOWWINDOW) { - // FIXME: Now, just like create! - debug("SWP_SHOWWINDOW for %p!", hwnd); - vchannel_write("CREATE1,0x%p,0x%x", hwnd, 0); + unsigned short title[150]; + int state; + + vchannel_write("CREATE", "0x%p,0x%p,0x%x", hwnd, parent, 0); - // FIXME: SETSTATE + GetWindowTextW(hwnd, title, sizeof(title) / sizeof(*title)); + + vchannel_write("TITLE", "0x%x,%s,0x%x", hwnd, + vchannel_strfilter_unicode(title), 0); + + if (style & WS_MAXIMIZE) + state = 2; + else if (style & WS_MINIMIZE) + state = 1; + else + state = 0; update_position(hwnd); + + vchannel_write("STATE", "0x%p,0x%x,0x%x", hwnd, state, 0); } if (wp->flags & SWP_HIDEWINDOW) - vchannel_write("DESTROY1,0x%p,0x%x", hwnd, 0); + vchannel_write("DESTROY", "0x%p,0x%x", hwnd, 0); - if (!(style & WS_VISIBLE)) + if (!(style & WS_VISIBLE) || (style & WS_MINIMIZE)) break; if (!(wp->flags & SWP_NOMOVE && wp->flags & SWP_NOSIZE)) update_position(hwnd); - if (!(wp->flags & SWP_NOZORDER)) - { - vchannel_write("ZCHANGE1,0x%p,0x%p,0x%x", - hwnd, - wp->flags & SWP_NOACTIVATE ? wp-> - hwndInsertAfter : 0, 0); - } - break; } case WM_SIZE: + if (!(style & WS_VISIBLE) || (style & WS_MINIMIZE)) + break; update_position(hwnd); break; case WM_MOVE: + if (!(style & WS_VISIBLE) || (style & WS_MINIMIZE)) + break; update_position(hwnd); break; case WM_DESTROY: - vchannel_write("DESTROY1,0x%p,0x%x", hwnd, 0); + if (!(style & WS_VISIBLE)) + break; + vchannel_write("DESTROY", "0x%p,0x%x", hwnd, 0); break; default: @@ -147,41 +246,127 @@ } static LRESULT CALLBACK -cbt_hook_proc(int code, WPARAM wparam, LPARAM lparam) +wndprocret_hook_proc(int code, WPARAM cur_thread, LPARAM details) { - char title[150]; + HWND hwnd, parent; + UINT msg; + WPARAM wparam; + LPARAM lparam; + + LONG style; if (code < 0) goto end; + hwnd = ((CWPRETSTRUCT *) details)->hwnd; + msg = ((CWPRETSTRUCT *) details)->message; + wparam = ((CWPRETSTRUCT *) details)->wParam; + lparam = ((CWPRETSTRUCT *) details)->lParam; + + style = GetWindowLong(hwnd, GWL_STYLE); + + /* Docs say that WS_CHILD and WS_POPUP is an illegal combination, + but they exist nonetheless. */ + if ((style & WS_CHILD) && !(style & WS_POPUP)) + goto end; + + if (style & WS_POPUP) + { + parent = (HWND) GetWindowLong(hwnd, GWL_HWNDPARENT); + if (!parent) + parent = (HWND) - 1; + } + else + parent = NULL; + + switch (msg) + { + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *wp = (WINDOWPOS *) lparam; + + if (!(style & WS_VISIBLE) || (style & WS_MINIMIZE)) + break; + + if (!(wp->flags & SWP_NOZORDER)) + update_zorder(hwnd); + + break; + } + + + case WM_SETTEXT: + { + unsigned short title[150]; + if (!(style & WS_VISIBLE)) + break; + /* We cannot use the string in lparam because + we need unicode. */ + GetWindowTextW(hwnd, title, sizeof(title) / sizeof(*title)); + vchannel_write("TITLE", "0x%p,%s,0x%x", hwnd, + vchannel_strfilter_unicode(title), 0); + break; + } + + default: + break; + } + + if (msg == g_wm_seamless_focus) + { + /* FIXME: SetActiveWindow() kills menus. Need to find a clean + way to solve this. */ + if ((GetActiveWindow() != hwnd) && !parent) + SetActiveWindow(hwnd); + + vchannel_write("ACK", "%u", g_blocked_focus_serial); + } + + end: + return CallNextHookEx(g_wndprocret_hook, code, cur_thread, details); +} + +static LRESULT CALLBACK +cbt_hook_proc(int code, WPARAM wparam, LPARAM lparam) +{ + if (code < 0) + goto end; + switch (code) { case HCBT_MINMAX: { - int show, state; + int show, state, blocked; + HWND blocked_hwnd; + unsigned int serial; + + WaitForSingleObject(g_mutex, INFINITE); + blocked_hwnd = g_blocked_state_hwnd; + serial = g_blocked_state_serial; + blocked = g_blocked_state; + ReleaseMutex(g_mutex); show = LOWORD(lparam); - if ((show == SW_SHOWMINIMIZED) || (show == SW_MINIMIZE)) + if ((show == SW_NORMAL) || (show == SW_SHOWNORMAL) + || (show == SW_RESTORE)) + state = 0; + else if ((show == SW_MINIMIZE) || (show == SW_SHOWMINIMIZED)) + state = 1; + else if ((show == SW_MAXIMIZE) || (show == SW_SHOWMAXIMIZED)) + state = 2; + else { - MessageBox(0, - "Minimizing windows is not allowed in this version. Sorry!", - "SeamlessRDP", MB_OK); - return 1; + debug("Unexpected show: %d", show); + break; } - GetWindowText((HWND) wparam, title, sizeof(title)); - - /* FIXME: Strip title of dangerous characters */ + if ((blocked_hwnd == (HWND) wparam) && (blocked == state)) + vchannel_write("ACK", "%u", serial); + else + vchannel_write("STATE", "0x%p,0x%x,0x%x", (HWND) wparam, + state, 0); - if (show == SW_SHOWNORMAL) - state = 0; - else if (show == SW_SHOWMINIMIZED) - state = 1; - else if (show == SW_SHOWMAXIMIZED) - state = 2; - vchannel_write("SETSTATE1,0x%p,%s,0x%x,0x%x", - (HWND) wparam, title, state, 0); break; } @@ -201,6 +386,10 @@ if (!g_wndproc_hook) g_wndproc_hook = SetWindowsHookEx(WH_CALLWNDPROC, wndproc_hook_proc, g_instance, 0); + + if (!g_wndprocret_hook) + g_wndprocret_hook = + SetWindowsHookEx(WH_CALLWNDPROCRET, wndprocret_hook_proc, g_instance, 0); } DLL_EXPORT void @@ -211,6 +400,121 @@ if (g_wndproc_hook) UnhookWindowsHookEx(g_wndproc_hook); + + if (g_wndprocret_hook) + UnhookWindowsHookEx(g_wndprocret_hook); +} + +DLL_EXPORT void +SafeMoveWindow(unsigned int serial, HWND hwnd, int x, int y, int width, int height) +{ + RECT rect; + + WaitForSingleObject(g_mutex, INFINITE); + g_block_move_hwnd = hwnd; + g_block_move_serial = serial; + g_block_move.left = x; + g_block_move.top = y; + g_block_move.right = x + width; + g_block_move.bottom = y + height; + ReleaseMutex(g_mutex); + + SetWindowPos(hwnd, NULL, x, y, width, height, SWP_NOACTIVATE | SWP_NOZORDER); + + vchannel_write("ACK", "%u", serial); + + if (!GetWindowRect(hwnd, &rect)) + debug("GetWindowRect failed!\n"); + else if ((rect.left != x) || (rect.top != y) || (rect.right != x + width) + || (rect.bottom != y + height)) + update_position(hwnd); + + WaitForSingleObject(g_mutex, INFINITE); + g_block_move_hwnd = NULL; + memset(&g_block_move, 0, sizeof(RECT)); + ReleaseMutex(g_mutex); +} + +DLL_EXPORT void +SafeZChange(unsigned int serial, HWND hwnd, HWND behind) +{ + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_zchange_serial = serial; + g_blocked_zchange[0] = hwnd; + g_blocked_zchange[1] = behind; + ReleaseMutex(g_mutex); + + if (behind == NULL) + behind = HWND_TOP; + + SetWindowPos(hwnd, behind, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_zchange[0] = NULL; + g_blocked_zchange[1] = NULL; + ReleaseMutex(g_mutex); +} + +DLL_EXPORT void +SafeFocus(unsigned int serial, HWND hwnd) +{ + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_focus_serial = serial; + g_blocked_focus = hwnd; + ReleaseMutex(g_mutex); + + SendMessage(hwnd, g_wm_seamless_focus, 0, 0); + + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_focus = NULL; + ReleaseMutex(g_mutex); +} + +DLL_EXPORT void +SafeSetState(unsigned int serial, HWND hwnd, int state) +{ + LONG style; + int curstate; + + vchannel_block(); + + style = GetWindowLong(hwnd, GWL_STYLE); + + if (style & WS_MAXIMIZE) + curstate = 2; + else if (style & WS_MINIMIZE) + curstate = 1; + else + curstate = 0; + + if (state == curstate) + { + vchannel_write("ACK", "%u", serial); + vchannel_unblock(); + return; + } + + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_state_hwnd = hwnd; + g_blocked_state_serial = serial; + g_blocked_state = state; + ReleaseMutex(g_mutex); + + vchannel_unblock(); + + if (state == 0) + ShowWindow(hwnd, SW_RESTORE); + else if (state == 1) + ShowWindow(hwnd, SW_MINIMIZE); + else if (state == 2) + ShowWindow(hwnd, SW_MAXIMIZE); + else + debug("Invalid state %d sent.", state); + + WaitForSingleObject(g_mutex, INFINITE); + g_blocked_state_hwnd = NULL; + g_blocked_state = -1; + ReleaseMutex(g_mutex); } DLL_EXPORT int @@ -236,6 +540,8 @@ ++g_instance_count; ReleaseMutex(g_mutex); + g_wm_seamless_focus = RegisterWindowMessage(FOCUS_MSG_NAME); + vchannel_open(); break;