1 |
//********************************************************************************* |
2 |
// |
3 |
//Title: Terminal Services Window Clipper |
4 |
// |
5 |
//Author: Martin Wickett |
6 |
// |
7 |
//Date: 2004 |
8 |
// |
9 |
//********************************************************************************* |
10 |
|
11 |
#define TSDLL |
12 |
|
13 |
#include "clipper.h" |
14 |
|
15 |
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
16 |
{ |
17 |
UNREFERENCED_PARAMETER(lpvReserved); |
18 |
UNREFERENCED_PARAMETER(hinstDLL); |
19 |
|
20 |
switch (fdwReason) { |
21 |
case DLL_PROCESS_ATTACH: |
22 |
break; |
23 |
|
24 |
case DLL_THREAD_ATTACH: |
25 |
break; |
26 |
|
27 |
case DLL_THREAD_DETACH: |
28 |
break; |
29 |
|
30 |
case DLL_PROCESS_DETACH: |
31 |
break; |
32 |
|
33 |
default: |
34 |
break; |
35 |
} |
36 |
return TRUE; |
37 |
} |
38 |
|
39 |
void WINAPI VirtualChannelOpenEvent(DWORD openHandle, UINT event, |
40 |
LPVOID pdata, UINT32 dataLength, |
41 |
UINT32 totalLength, UINT32 dataFlags) |
42 |
{ |
43 |
LPDWORD pdwControlCode = (LPDWORD) pdata; |
44 |
CHAR ourData[1600]; |
45 |
UINT ui = 0; |
46 |
|
47 |
UNREFERENCED_PARAMETER(openHandle); |
48 |
UNREFERENCED_PARAMETER(dataFlags); |
49 |
|
50 |
ZeroMemory(ourData, sizeof(ourData)); |
51 |
|
52 |
//copy the send string (with the same lenth of the data) |
53 |
strncpy(ourData, (LPSTR) pdata, dataLength / sizeof(char)); |
54 |
|
55 |
if (OUTPUT_DEBUG_INFO == 1) { |
56 |
OutputDebugString |
57 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Virtual channel data received"); |
58 |
OutputDebugString(ourData); |
59 |
} |
60 |
|
61 |
if (dataLength == totalLength) { |
62 |
switch (event) { |
63 |
case CHANNEL_EVENT_DATA_RECEIVED: { |
64 |
CTokenizer tok(_T((LPSTR) ourData), _T(";")); |
65 |
CStdString cs; |
66 |
|
67 |
CWindowData *wid = new CWindowData(""); |
68 |
CStdString messageType; |
69 |
int mixMaxType = 0; |
70 |
|
71 |
while (tok.Next(cs)) { |
72 |
CStdString msg; |
73 |
CTokenizer msgTok(cs, _T("=")); |
74 |
|
75 |
msgTok.Next(msg); |
76 |
|
77 |
if (strcmp(msg, "MSG") == 0) { |
78 |
msgTok.Next(msg); |
79 |
messageType = msg; |
80 |
} |
81 |
|
82 |
if (strcmp(msg, "ID") == 0) { |
83 |
msgTok.Next(msg); |
84 |
wid->SetId(msg); |
85 |
} else if (strcmp(msg, "TITLE") == 0) { |
86 |
msgTok.Next(msg); |
87 |
wid->SetTitle(msg); |
88 |
} else if (strcmp(msg, "POS") == 0) { |
89 |
msgTok.Next(msg); |
90 |
|
91 |
CStdString pos; |
92 |
CTokenizer posTok(msg, _T("~")); |
93 |
|
94 |
posTok.Next(pos); |
95 |
|
96 |
|
97 |
// check bounds, coords can be negative if window top left point is moved off the screen. |
98 |
// we don't care about that since the window can't be see so just use zero. |
99 |
|
100 |
if (strchr(pos, '-') == NULL) { |
101 |
wid->SetX1(atoi(pos)); |
102 |
} else { |
103 |
wid->SetX1(0); |
104 |
} |
105 |
|
106 |
posTok.Next(pos); |
107 |
|
108 |
if (strchr(pos, '-') == NULL) { |
109 |
wid->SetY1(atoi(pos)); |
110 |
} else { |
111 |
wid->SetY1(0); |
112 |
} |
113 |
|
114 |
posTok.Next(pos); |
115 |
|
116 |
if (strchr(pos, '-') == NULL) { |
117 |
wid->SetX2(atoi(pos)); |
118 |
} else { |
119 |
wid->SetX2(0); |
120 |
} |
121 |
|
122 |
posTok.Next(pos); |
123 |
|
124 |
if (strchr(pos, '-') == NULL) { |
125 |
wid->SetY2(atoi(pos)); |
126 |
} else { |
127 |
wid->SetY2(0); |
128 |
} |
129 |
} else if (strcmp(msg, "TYPE") == 0) { |
130 |
msgTok.Next(msg); |
131 |
mixMaxType = atoi(msg); |
132 |
} |
133 |
} |
134 |
|
135 |
if (strcmp(messageType, "HSHELL_WINDOWCREATED") == 0) { |
136 |
if (OUTPUT_DEBUG_INFO == 1) { |
137 |
OutputDebugString |
138 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Message was of type HSHELL_WINDOWCREATED window title is:"); |
139 |
OutputDebugString(wid->GetTitle()); |
140 |
} |
141 |
|
142 |
CStdString s = wid->GetId(); |
143 |
char *ptr; |
144 |
int length = s.GetLength(); |
145 |
ptr = s.GetBufferSetLength(length); |
146 |
|
147 |
hash_insert(ptr, wid, &m_ht); |
148 |
|
149 |
CreateAndShowWindow(wid); |
150 |
|
151 |
DoClipping(1); |
152 |
} else if (strcmp(messageType, "HSHELL_WINDOWDESTROYED") == 0) { |
153 |
if (OUTPUT_DEBUG_INFO == 1) { |
154 |
OutputDebugString |
155 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Message was of type HSHELL_WINDOWDISTROYED window title is:"); |
156 |
OutputDebugString(wid->GetTitle()); |
157 |
} |
158 |
|
159 |
CStdString s = wid->GetId(); |
160 |
char *ptr; |
161 |
int length = s.GetLength(); |
162 |
ptr = s.GetBufferSetLength(length); |
163 |
|
164 |
CWindowData *oldWinData = |
165 |
(CWindowData *) hash_del(ptr, &m_ht); |
166 |
|
167 |
DestroyTaskbarWindow(oldWinData); |
168 |
|
169 |
delete oldWinData; |
170 |
|
171 |
DoClipping(1); |
172 |
} else if (strcmp(messageType, "HCBT_MINMAX") == 0) { |
173 |
if (OUTPUT_DEBUG_INFO == 1) { |
174 |
OutputDebugString |
175 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Message was of type HCBT_MINMAX"); |
176 |
} |
177 |
|
178 |
|
179 |
//TODO |
180 |
|
181 |
} else if (strcmp(messageType, "HCBT_MOVESIZE") == 0) { |
182 |
if (OUTPUT_DEBUG_INFO == 1) { |
183 |
OutputDebugString |
184 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Message was of type HCBT_MOVESIZE window title is:"); |
185 |
OutputDebugString(wid->GetTitle()); |
186 |
} |
187 |
|
188 |
CStdString s = wid->GetId(); |
189 |
char *ptr; |
190 |
int length = s.GetLength(); |
191 |
ptr = s.GetBufferSetLength(length); |
192 |
|
193 |
CWindowData *movedWinData = |
194 |
(CWindowData *) hash_lookup(ptr, &m_ht); |
195 |
|
196 |
if (movedWinData != NULL) { |
197 |
movedWinData->SetX1(wid->GetX1()); |
198 |
movedWinData->SetX2(wid->GetX2()); |
199 |
movedWinData->SetY1(wid->GetY1()); |
200 |
movedWinData->SetY2(wid->GetY2()); |
201 |
|
202 |
DoClipping(1); |
203 |
} |
204 |
|
205 |
delete wid; |
206 |
} else if (strcmp(messageType, "CALLWNDPROC_WM_MOVING") == 0) { |
207 |
if (OUTPUT_DEBUG_INFO == 1) { |
208 |
OutputDebugString |
209 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Message was of type CALLWNDPROC_WM_MOVING window title is:"); |
210 |
OutputDebugString(wid->GetTitle()); |
211 |
} |
212 |
|
213 |
CStdString s = wid->GetId(); |
214 |
char *ptr; |
215 |
int length = s.GetLength(); |
216 |
ptr = s.GetBufferSetLength(length); |
217 |
|
218 |
CWindowData *movedWinData = |
219 |
(CWindowData *) hash_lookup(ptr, &m_ht); |
220 |
|
221 |
if (movedWinData != NULL) { |
222 |
movedWinData->SetX1(wid->GetX1()); |
223 |
movedWinData->SetX2(wid->GetX2()); |
224 |
movedWinData->SetY1(wid->GetY1()); |
225 |
movedWinData->SetY2(wid->GetY2()); |
226 |
|
227 |
////might be too much of an overhead forcing the redraw here. Might be better to do 'DoClipping(0)' instead? |
228 |
DoClipping(1); |
229 |
} |
230 |
|
231 |
delete wid; |
232 |
} |
233 |
} |
234 |
break; |
235 |
|
236 |
case CHANNEL_EVENT_WRITE_COMPLETE: {} |
237 |
break; |
238 |
|
239 |
case CHANNEL_EVENT_WRITE_CANCELLED: {} |
240 |
break; |
241 |
|
242 |
default: {} |
243 |
break; |
244 |
} |
245 |
} else {} |
246 |
} |
247 |
|
248 |
|
249 |
VOID VCAPITYPE VirtualChannelInitEventProc(LPVOID pInitHandle, UINT event, |
250 |
LPVOID pData, UINT dataLength) |
251 |
{ |
252 |
UINT ui; |
253 |
|
254 |
UNREFERENCED_PARAMETER(pInitHandle); |
255 |
UNREFERENCED_PARAMETER(dataLength); |
256 |
|
257 |
switch (event) { |
258 |
case CHANNEL_EVENT_INITIALIZED: {} |
259 |
break; |
260 |
|
261 |
case CHANNEL_EVENT_CONNECTED: { |
262 |
// |
263 |
// open channel |
264 |
// |
265 |
ui = gpEntryPoints->pVirtualChannelOpen(gphChannel, |
266 |
&gdwOpenChannel, |
267 |
CHANNELNAME, |
268 |
(PCHANNEL_OPEN_EVENT_FN) |
269 |
VirtualChannelOpenEvent); |
270 |
|
271 |
if (ui == CHANNEL_RC_OK) {} |
272 |
else { |
273 |
MessageBox(NULL, TEXT("Open of RDP virtual channel failed"), |
274 |
TEXT("TS Window Clipper"), MB_OK); |
275 |
} |
276 |
|
277 |
if (ui != CHANNEL_RC_OK) { |
278 |
return; |
279 |
} |
280 |
} |
281 |
break; |
282 |
|
283 |
case CHANNEL_EVENT_V1_CONNECTED: { |
284 |
MessageBox(NULL, |
285 |
TEXT |
286 |
("Connecting to a non Windows 2000 Terminal Server"), |
287 |
TEXT("TS Window Clipper"), MB_OK); |
288 |
} |
289 |
break; |
290 |
|
291 |
case CHANNEL_EVENT_DISCONNECTED: {} |
292 |
break; |
293 |
|
294 |
case CHANNEL_EVENT_TERMINATED: { |
295 |
// |
296 |
// free the entry points table |
297 |
// |
298 |
LocalFree((HLOCAL) gpEntryPoints); |
299 |
} |
300 |
break; |
301 |
|
302 |
default: {} |
303 |
break; |
304 |
} |
305 |
} |
306 |
|
307 |
BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) |
308 |
{ |
309 |
CHANNEL_DEF cd; |
310 |
UINT uRet; |
311 |
|
312 |
size_t s = 10; |
313 |
hash_construct_table(&m_ht, s); |
314 |
|
315 |
// |
316 |
// allocate memory |
317 |
// |
318 |
gpEntryPoints = |
319 |
(PCHANNEL_ENTRY_POINTS) LocalAlloc(LPTR, pEntryPoints->cbSize); |
320 |
|
321 |
memcpy(gpEntryPoints, pEntryPoints, pEntryPoints->cbSize); |
322 |
|
323 |
// |
324 |
// initialize CHANNEL_DEF structure |
325 |
// |
326 |
ZeroMemory(&cd, sizeof(CHANNEL_DEF)); |
327 |
strcpy(cd.name, CHANNELNAME); // ANSI ONLY |
328 |
|
329 |
// |
330 |
// register channel |
331 |
// |
332 |
uRet = |
333 |
gpEntryPoints->pVirtualChannelInit((LPVOID *) & gphChannel, |
334 |
(PCHANNEL_DEF) & cd, 1, |
335 |
VIRTUAL_CHANNEL_VERSION_WIN2000, |
336 |
(PCHANNEL_INIT_EVENT_FN) |
337 |
VirtualChannelInitEventProc); |
338 |
|
339 |
if (uRet == CHANNEL_RC_OK) { |
340 |
if (ALWAYS__CLIP) { |
341 |
DoClipping(1); |
342 |
} |
343 |
} else { |
344 |
MessageBox(NULL, TEXT("RDP Virtual channel Init Failed"), |
345 |
TEXT("TS Window Clipper"), MB_OK); |
346 |
} |
347 |
|
348 |
if (uRet != CHANNEL_RC_OK) { |
349 |
return FALSE; |
350 |
} |
351 |
|
352 |
// |
353 |
// make sure channel was initialized |
354 |
// |
355 |
if (cd.options != CHANNEL_OPTION_INITIALIZED) { |
356 |
return FALSE; |
357 |
} |
358 |
|
359 |
return TRUE; |
360 |
} |
361 |
|
362 |
|
363 |
// data structure to transfer informations |
364 |
typedef struct _WindowFromProcessOrThreadID |
365 |
{ |
366 |
union |
367 |
{ |
368 |
DWORD procId; |
369 |
DWORD threadId; |
370 |
}; |
371 |
HWND hWnd; |
372 |
} |
373 |
Wnd4PTID; |
374 |
|
375 |
// Callback procedure |
376 |
BOOL CALLBACK PrivateEnumWindowsProc(HWND hwnd, LPARAM lParam) |
377 |
{ |
378 |
DWORD procId; |
379 |
DWORD threadId; |
380 |
Wnd4PTID *tmp = (Wnd4PTID *) lParam; |
381 |
// get the process/thread id of current window |
382 |
threadId = GetWindowThreadProcessId(hwnd, &procId); |
383 |
// check if the process/thread id equal to the one passed by lParam? |
384 |
if (threadId == tmp->threadId || procId == tmp->procId) { |
385 |
// check if the window is a main window |
386 |
// because there lots of windows belong to the same process/thread |
387 |
LONG dwStyle = GetWindowLong(hwnd, GWL_STYLE); |
388 |
if (dwStyle & WS_SYSMENU) { |
389 |
tmp->hWnd = hwnd; |
390 |
return FALSE; // break the enumeration |
391 |
} |
392 |
} |
393 |
return TRUE; // continue the enumeration |
394 |
} |
395 |
|
396 |
// Enumarate all the MainWindow of the system |
397 |
HWND FindProcessMainWindow(DWORD procId) |
398 |
{ |
399 |
Wnd4PTID tempWnd4ID; |
400 |
tempWnd4ID.procId = procId; |
401 |
if (!EnumWindows |
402 |
((WNDENUMPROC) PrivateEnumWindowsProc, (LPARAM) & tempWnd4ID)) { |
403 |
|
404 |
if (OUTPUT_DEBUG_INFO == 1) { |
405 |
OutputDebugString |
406 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Found main process window"); |
407 |
} |
408 |
|
409 |
return tempWnd4ID.hWnd; |
410 |
} |
411 |
|
412 |
|
413 |
if (OUTPUT_DEBUG_INFO == 1) { |
414 |
OutputDebugString |
415 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Could not find main process window"); |
416 |
} |
417 |
|
418 |
return NULL; |
419 |
} |
420 |
|
421 |
|
422 |
void DoClipping(int forceRedraw) |
423 |
{ |
424 |
//if main window handle is null, try to get it |
425 |
if (m_mainWindowHandle == NULL) { |
426 |
m_mainWindowHandle = FindProcessMainWindow(GetCurrentProcessId()); |
427 |
|
428 |
//hide the window from taskbar and put at the back of the z order |
429 |
if (HIDE_TSAC_WINDOW == 1) { |
430 |
ShowWindow(m_mainWindowHandle, SW_HIDE); |
431 |
SetWindowLongPtr(m_mainWindowHandle, GWL_EXSTYLE, |
432 |
GetWindowLong(m_mainWindowHandle, |
433 |
GWL_EXSTYLE) | WS_EX_TOOLWINDOW); |
434 |
ShowWindow(m_mainWindowHandle, SW_SHOW); |
435 |
} |
436 |
|
437 |
SetWindowPos(m_mainWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, |
438 |
SWP_NOMOVE | SWP_NOSIZE); |
439 |
} |
440 |
|
441 |
//if we have the handle, lets use it for the clipping |
442 |
if (m_mainWindowHandle != NULL) { |
443 |
RECT wRect; |
444 |
GetWindowRect(m_mainWindowHandle, &wRect); |
445 |
|
446 |
if (OUTPUT_DEBUG_INFO == 1) { |
447 |
OutputDebugString |
448 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Restarting clipping..."); |
449 |
} |
450 |
|
451 |
m_regionResult = NULL; |
452 |
|
453 |
if (OUTPUT_WINDOW_TABLE_DEBUG_INFO == 1) { |
454 |
OutputDebugString |
455 |
("-----------------------------------------------------------------------------"); |
456 |
OutputDebugString |
457 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> starting printing of window table"); |
458 |
} |
459 |
|
460 |
//enumerate though hashtable |
461 |
if (&m_ht != NULL) { |
462 |
hash_enumerate(&m_ht, CreateRegionFromWindowData); |
463 |
} |
464 |
|
465 |
if (OUTPUT_WINDOW_TABLE_DEBUG_INFO == 1) { |
466 |
OutputDebugString |
467 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> finished printing of window table"); |
468 |
OutputDebugString |
469 |
("-----------------------------------------------------------------------------"); |
470 |
} |
471 |
|
472 |
if (m_regionResult == NULL) { |
473 |
if (ALWAYS__CLIP) { |
474 |
m_regionResult = CreateRectRgn(0, 0, 0, 0); |
475 |
} else { |
476 |
m_regionResult = |
477 |
CreateRectRgn(0, 0, wRect.right, wRect.bottom); |
478 |
} |
479 |
} |
480 |
|
481 |
SetWindowRgn(m_mainWindowHandle, (HRGN__ *) m_regionResult, TRUE); |
482 |
|
483 |
if (forceRedraw == 1) { |
484 |
// invalidate the window and force it to redraw |
485 |
RedrawWindow(m_mainWindowHandle, NULL, NULL, |
486 |
RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); |
487 |
} |
488 |
} else { |
489 |
if (OUTPUT_DEBUG_INFO == 1) { |
490 |
OutputDebugString |
491 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Coulf not find window to clip"); |
492 |
} |
493 |
} |
494 |
} |
495 |
|
496 |
void CreateRegionFromWindowData(char *key, void *value) |
497 |
{ |
498 |
CWindowData *wd; |
499 |
wd = (CWindowData *) value; |
500 |
int x1 = 0, x2 = 0, y1 = 0, y2 = 0; |
501 |
|
502 |
char strB[5]; |
503 |
char strT[5]; |
504 |
char strL[5]; |
505 |
char strR[5]; |
506 |
|
507 |
if (m_regionResult == NULL) { |
508 |
m_regionResult = CreateRectRgn(0, 0, 0, 0); |
509 |
} |
510 |
|
511 |
if (OUTPUT_DEBUG_INFO == 1 && OUTPUT_WINDOW_TABLE_DEBUG_INFO != 1) { |
512 |
OutputDebugString |
513 |
("TS WINDOW CLIPPER :: CLIENT DLL :: Info --> Adding this window to cliping region"); |
514 |
OutputDebugString(wd->GetTitle()); |
515 |
} |
516 |
if (OUTPUT_WINDOW_TABLE_DEBUG_INFO == 1) { |
517 |
ltoa(wd->GetY2(), strB, 10); |
518 |
ltoa(wd->GetY1(), strT, 10); |
519 |
ltoa(wd->GetX2(), strR, 10); |
520 |
ltoa(wd->GetX1(), strL, 10); |
521 |
|
522 |
OutputDebugString("This window is in the table:"); |
523 |
OutputDebugString(wd->GetTitle()); |
524 |
OutputDebugString(wd->GetId()); |
525 |
OutputDebugString(strL); |
526 |
OutputDebugString(strT); |
527 |
OutputDebugString(strR); |
528 |
OutputDebugString(strB); |
529 |
OutputDebugString("*******************"); |
530 |
} |
531 |
|
532 |
HRGN newRegion = |
533 |
CreateRectRgn(wd->GetX1(), wd->GetY1(), wd->GetX2(), wd->GetY2()); |
534 |
|
535 |
CombineRgn(m_regionResult, newRegion, m_regionResult, RGN_OR); |
536 |
} |
537 |
|
538 |
/* |
539 |
Dummy procedure to catch when window is being maximised. |
540 |
|
541 |
Need to tell the window on the server to do the same. |
542 |
*/ |
543 |
LRESULT CALLBACK DummyWindowCallbackProc(HWND hwnd, UINT uMsg, WPARAM wParam, |
544 |
LPARAM lParam) |
545 |
{ |
546 |
//TODO |
547 |
|
548 |
return DefWindowProc(hwnd, uMsg, wParam, lParam); |
549 |
} |
550 |
|
551 |
void CreateAndShowWindow(CWindowData * wd) |
552 |
{ |
553 |
if (classAlreadyRegistered == 0) { |
554 |
static const char *szWndName = "WTSWinClipperDummy"; |
555 |
WNDCLASS wc; |
556 |
|
557 |
wc.style = 0; |
558 |
wc.lpfnWndProc = DummyWindowCallbackProc; |
559 |
wc.cbClsExtra = 0; |
560 |
wc.cbWndExtra = 0; |
561 |
wc.hInstance = 0; |
562 |
wc.hIcon = 0; |
563 |
wc.hCursor = 0; |
564 |
wc.hbrBackground = 0; |
565 |
wc.lpszMenuName = 0; |
566 |
wc.lpszClassName = szWndName; |
567 |
|
568 |
if (RegisterClass(&wc)) { |
569 |
classAlreadyRegistered = 1; |
570 |
} |
571 |
} |
572 |
|
573 |
if (classAlreadyRegistered = 1) { |
574 |
HWND hWnd = |
575 |
CreateWindow(TEXT("WTSWinClipperDummy"), wd->GetTitle(), WS_POPUP, |
576 |
0, 0, 0, 0, 0, 0, 0, 0); |
577 |
ShowWindow(hWnd, 3); |
578 |
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOREDRAW); |
579 |
wd->TaskbarWindowHandle = hWnd; |
580 |
SetFocus(m_mainWindowHandle); |
581 |
} |
582 |
} |
583 |
|
584 |
void DestroyTaskbarWindow(CWindowData * wd) |
585 |
{ |
586 |
if (wd->TaskbarWindowHandle != NULL) { |
587 |
DestroyWindow(wd->TaskbarWindowHandle); |
588 |
} |
589 |
} |