1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#ifdef WIN32 18 19/* 20 * Win9xConHook.dll - a hook proc to clean up Win95/98 console behavior. 21 * 22 * It is well(?) documented by Microsoft that the Win9x HandlerRoutine 23 * hooked by the SetConsoleCtrlHandler never receives the CTRL_CLOSE_EVENT, 24 * CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT signals. 25 * 26 * It is possible to have a second window to monitor the WM_ENDSESSION 27 * message, but the close button still fails.. 28 * 29 * There is a 16bit polling method for the close window option, but this 30 * is CPU intensive and requires thunking. 31 * 32 * Attempts to subclass the 'tty' console fail, since that message thread 33 * is actually owned by the 16 bit winoldap.mod process, although the 34 * window reports it is owned by the process/thread of the console app. 35 * 36 * Win9xConHook is thunks the WM_CLOSE and WM_ENDSESSION messages, 37 * first through a window hook procedure in the winoldap context, into 38 * a subclass WndProc, and on to a second hidden monitor window in the 39 * console application's context that dispatches them to the console app's 40 * registered HandlerRoutine. 41 */ 42 43/* This debugging define turns on output to COM1, although you better init 44 * the port first (even using hyperterm). It's the only way to catch the 45 * goings on within system logoff/shutdown. 46 * #define DBG 1 47 */ 48 49#include <windows.h> 50 51/* Variables used within any process context: 52 * hookwndmsg is a shared message to send Win9xConHook signals 53 * origwndprop is a wndprop atom to store the orig wndproc of the tty 54 * hookwndprop is a wndprop atom to store the hwnd of the hidden child 55 * is_service reminds us to unmark this process on the way out 56 */ 57static UINT hookwndmsg = 0; 58static LPCTSTR origwndprop; 59static LPCTSTR hookwndprop; 60static BOOL is_service = 0; 61//static HMODULE hmodThis = NULL; 62 63/* Variables used within the tty processes' context: 64 * is_tty flags this process; -1 == unknown, 1 == if tty, 0 == if not 65 * hw_tty is the handle of the top level tty in this process context 66 * is_subclassed is toggled to assure DllMain removes the subclass on unload 67 * hmodLock is there to try and prevent this dll from being unloaded if the 68 * hook is removed while we are subclassed 69 */ 70static int is_tty = -1; 71static HWND hwtty = NULL; 72static BOOL is_subclassed = 0; 73 74// This simply causes a gpfault the moment it tries to FreeLibrary within 75// the subclass procedure ... not good. 76//static HMODULE hmodLock = NULL; 77 78/* Variables used within the service or console app's context: 79 * hmodHook is the instance handle of this module for registering the hooks 80 * hhkGetMessage is the hook handle for catching Posted messages 81 * hhkGetMessage is the hook handle for catching Sent messages 82 * monitor_hwnd is the invisible window that handles our tty messages 83 * the tty_info strucure is used to pass args into the hidden window's thread 84 */ 85static HMODULE hmodHook = NULL; 86static HHOOK hhkGetMessage; 87//static HHOOK hhkCallWndProc; 88static HWND monitor_hwnd = NULL; 89 90typedef struct { 91 PHANDLER_ROUTINE phandler; 92 HINSTANCE instance; 93 HWND parent; 94 INT type; 95 LPCSTR name; 96} tty_info; 97 98/* These are the GetWindowLong offsets for the hidden window's internal info 99 * gwltty_phandler is the address of the app's HandlerRoutine 100 * gwltty_ttywnd is the tty this hidden window will handle messages from 101 */ 102#define gwltty_phandler 0 103#define gwltty_ttywnd 4 104 105/* Forward declaration prototypes for internal functions 106 */ 107static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd); 108static LRESULT WINAPI RegisterWindows9xService(BOOL set_service); 109static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, 110 WPARAM wParam, LPARAM lParam); 111static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty); 112static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 113 WPARAM wParam, LPARAM lParam); 114static int HookProc(int hc, HWND *hwnd, UINT *msg, 115 WPARAM *wParam, LPARAM *lParam); 116#ifdef DBG 117static VOID DbgPrintf(LPTSTR fmt, ...); 118#endif 119 120 121/* DllMain is invoked by every process in the entire system that is hooked 122 * by our window hooks, notably the tty processes' context, and by the user 123 * who wants tty messages (the app). Keep it light and simple. 124 */ 125BOOL __declspec(dllexport) APIENTRY DllMain(HINSTANCE hModule, ULONG ulReason, 126 LPVOID pctx) 127{ 128 if (ulReason == DLL_PROCESS_ATTACH) 129 { 130 //hmodThis = hModule; 131 if (!hookwndmsg) { 132 origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc")); 133 hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd")); 134 hookwndmsg = RegisterWindowMessage("Win9xConHookMsg"); 135 } 136#ifdef DBG 137// DbgPrintf("H ProcessAttach:%8.8x\r\n", 138// GetCurrentProcessId()); 139#endif 140 } 141 else if ( ulReason == DLL_PROCESS_DETACH ) 142 { 143#ifdef DBG 144// DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId()); 145#endif 146 if (monitor_hwnd) 147 SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); 148 if (is_subclassed) 149 SendMessage(hwtty, hookwndmsg, 0, (LPARAM)hwtty); 150 if (hmodHook) 151 { 152 if (hhkGetMessage) { 153 UnhookWindowsHookEx(hhkGetMessage); 154 hhkGetMessage = NULL; 155 } 156 //if (hhkCallWndProc) { 157 // UnhookWindowsHookEx(hhkCallWndProc); 158 // hhkCallWndProc = NULL; 159 //} 160 FreeLibrary(hmodHook); 161 hmodHook = NULL; 162 } 163 if (is_service) 164 RegisterWindows9xService(FALSE); 165 if (hookwndmsg) { 166 GlobalDeleteAtom((ATOM)origwndprop); 167 GlobalDeleteAtom((ATOM)hookwndprop); 168 hookwndmsg = 0; 169 } 170 } 171 return TRUE; 172} 173 174 175/* This group of functions are provided for the service/console app 176 * to register itself a HandlerRoutine to accept tty or service messages 177 */ 178 179 180/* Exported function that creates a Win9x 'service' via a hidden window, 181 * that notifies the process via the HandlerRoutine messages. 182 */ 183BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler( 184 PHANDLER_ROUTINE phandler, 185 LPCSTR name) 186{ 187 /* If we have not yet done so */ 188 FreeConsole(); 189 190 if (name) 191 { 192 DWORD tid; 193 HANDLE hThread; 194 /* NOTE: this is static so the module can continue to 195 * access these args while we go on to other things 196 */ 197 static tty_info tty; 198 tty.instance = GetModuleHandle(NULL); 199 tty.phandler = phandler; 200 tty.parent = NULL; 201 tty.name = name; 202 tty.type = 2; 203 RegisterWindows9xService(TRUE); 204 hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread, 205 (LPVOID)&tty, 0, &tid); 206 if (hThread) 207 { 208 CloseHandle(hThread); 209 return TRUE; 210 } 211 } 212 else /* remove */ 213 { 214 if (monitor_hwnd) 215 SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); 216 RegisterWindows9xService(FALSE); 217 return TRUE; 218 } 219 return FALSE; 220} 221 222 223/* Exported function that registers a HandlerRoutine to accept missing 224 * Win9x CTRL_EVENTs from the tty window, as NT does without a hassle. 225 * If add is 1 or 2, register the handler, if 2 also mark it as a service. 226 * If add is 0 deregister the handler, and unmark if a service 227 */ 228BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler( 229 PHANDLER_ROUTINE phandler, 230 INT add) 231{ 232 HWND parent; 233 234 if (add) 235 { 236 HANDLE hThread; 237 DWORD tid; 238 /* NOTE: this is static so the module can continue to 239 * access these args while we go on to other things 240 */ 241 static tty_info tty; 242 EnumWindows(EnumttyWindow, (LPARAM)&parent); 243 if (!parent) { 244#ifdef DBG 245 DbgPrintf("A EnumttyWindow failed (%d)\r\n", GetLastError()); 246#endif 247 return FALSE; 248 } 249 tty.instance = GetModuleHandle(NULL); 250 tty.phandler = phandler; 251 tty.parent = parent; 252 tty.type = add; 253 if (add == 2) { 254 tty.name = "ttyService"; 255 RegisterWindows9xService(TRUE); 256 } 257 else 258 tty.name = "ttyMonitor"; 259 hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread, 260 (LPVOID)&tty, 0, &tid); 261 if (!hThread) 262 return FALSE; 263 CloseHandle(hThread); 264 hmodHook = LoadLibrary("Win9xConHook.dll"); 265 if (hmodHook) 266 { 267 hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE, 268 (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0); 269 //hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, 270 // (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0); 271 } 272 return TRUE; 273 } 274 else /* remove */ 275 { 276 if (monitor_hwnd) { 277 SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); 278 } 279 if (hmodHook) 280 { 281 if (hhkGetMessage) { 282 UnhookWindowsHookEx(hhkGetMessage); 283 hhkGetMessage = NULL; 284 } 285 //if (hhkCallWndProc) { 286 // UnhookWindowsHookEx(hhkCallWndProc); 287 // hhkCallWndProc = NULL; 288 //} 289 FreeLibrary(hmodHook); 290 hmodHook = NULL; 291 } 292 if (is_service) 293 RegisterWindows9xService(FALSE); 294 return TRUE; 295 } 296 return FALSE; 297} 298 299 300/* The following internal helpers are only used within the app's context 301 */ 302 303/* ttyConsoleCreateThread is the process that runs within the user app's 304 * context. It creates and pumps the messages of a hidden monitor window, 305 * watching for messages from the system, or the associated subclassed tty 306 * window. Things can happen in our context that can't be done from the 307 * tty's context, and visa versa, so the subclass procedure and this hidden 308 * window work together to make it all happen. 309 */ 310static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty) 311{ 312 WNDCLASS wc; 313 MSG msg; 314 wc.style = CS_GLOBALCLASS; 315 wc.lpfnWndProc = ttyConsoleCtrlWndProc; 316 wc.cbClsExtra = 0; 317 wc.cbWndExtra = 8; 318 wc.hInstance = NULL; 319 wc.hIcon = NULL; 320 wc.hCursor = NULL; 321 wc.hbrBackground = NULL; 322 wc.lpszMenuName = NULL; 323 if (((tty_info*)tty)->parent) 324 wc.lpszClassName = "ttyConHookChild"; 325 else 326 wc.lpszClassName = "ApacheWin95ServiceMonitor"; 327 328 if (!RegisterClass(&wc)) { 329#ifdef DBG 330 DbgPrintf("A proc %8.8x Error creating class %s (%d)\r\n", 331 GetCurrentProcessId(), wc.lpszClassName, GetLastError()); 332#endif 333 return 0; 334 } 335 336 /* Create an invisible window */ 337 monitor_hwnd = CreateWindow(wc.lpszClassName, ((tty_info*)tty)->name, 338 WS_OVERLAPPED & ~WS_VISIBLE, 339 CW_USEDEFAULT, CW_USEDEFAULT, 340 CW_USEDEFAULT, CW_USEDEFAULT, 341 NULL, NULL, 342 ((tty_info*)tty)->instance, tty); 343 344 if (!monitor_hwnd) { 345#ifdef DBG 346 DbgPrintf("A proc %8.8x Error creating window %s %s (%d)\r\n", 347 GetCurrentProcessId(), wc.lpszClassName, 348 ((tty_info*)tty)->name, GetLastError()); 349#endif 350 return 0; 351 } 352 353 while (GetMessage(&msg, NULL, 0, 0)) 354 { 355 TranslateMessage(&msg); 356 DispatchMessage(&msg); 357 } 358 359 /* Tag again as deleted, just in case we missed WM_DESTROY */ 360 monitor_hwnd = NULL; 361 return 0; 362} 363 364 365/* This is the WndProc procedure for our invisible window. 366 * When our subclasssed tty window receives the WM_CLOSE, WM_ENDSESSION, 367 * or WM_QUERYENDSESSION messages, the message is dispatched to our hidden 368 * window (this message process), and we call the installed HandlerRoutine 369 * that was registered by the app. 370 */ 371static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, 372 WPARAM wParam, LPARAM lParam) 373{ 374 if (msg == WM_CREATE) 375 { 376 tty_info *tty = (tty_info*)(((LPCREATESTRUCT)lParam)->lpCreateParams); 377 SetWindowLong(hwnd, gwltty_phandler, (LONG)tty->phandler); 378 SetWindowLong(hwnd, gwltty_ttywnd, (LONG)tty->parent); 379#ifdef DBG 380 DbgPrintf("A proc %8.8x created %8.8x %s for tty wnd %8.8x\r\n", 381 GetCurrentProcessId(), hwnd, 382 tty->name, tty->parent); 383#endif 384 if (tty->parent) { 385 SetProp(tty->parent, hookwndprop, hwnd); 386 PostMessage(tty->parent, hookwndmsg, 387 tty->type, (LPARAM)tty->parent); 388 } 389 return 0; 390 } 391 else if (msg == WM_DESTROY) 392 { 393 HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd); 394#ifdef DBG 395 DbgPrintf("A proc %8.8x destroyed %8.8x ttyConHookChild\r\n", 396 GetCurrentProcessId(), hwnd); 397#endif 398 if (parent) { 399 RemoveProp(parent, hookwndprop); 400 SendMessage(parent, hookwndmsg, 0, (LPARAM)parent); 401 } 402 monitor_hwnd = NULL; 403 } 404 else if (msg == WM_CLOSE) 405 { 406 PHANDLER_ROUTINE phandler = 407 (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); 408 LRESULT rv = phandler(CTRL_CLOSE_EVENT); 409#ifdef DBG 410 DbgPrintf("A proc %8.8x invoked CTRL_CLOSE_EVENT " 411 "returning %d\r\n", 412 GetCurrentProcessId(), rv); 413#endif 414 if (rv) 415 return !rv; 416 } 417 else if ((msg == WM_QUERYENDSESSION) || (msg == WM_ENDSESSION)) 418 { 419 if (lParam & ENDSESSION_LOGOFF) 420 { 421 PHANDLER_ROUTINE phandler = 422 (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); 423 LRESULT rv = phandler(CTRL_LOGOFF_EVENT); 424#ifdef DBG 425 DbgPrintf("A proc %8.8x invoked CTRL_LOGOFF_EVENT " 426 "returning %d\r\n", 427 GetCurrentProcessId(), rv); 428#endif 429 if (rv) 430 return ((msg == WM_QUERYENDSESSION) ? rv : !rv); 431 } 432 else 433 { 434 PHANDLER_ROUTINE phandler = 435 (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); 436 LRESULT rv = phandler(CTRL_SHUTDOWN_EVENT); 437#ifdef DBG 438 DbgPrintf("A proc %8.8x invoked CTRL_SHUTDOWN_EVENT " 439 "returning %d\r\n", GetCurrentProcessId(), rv); 440#endif 441 if (rv) 442 return ((msg == WM_QUERYENDSESSION) ? rv : !rv); 443 } 444 } 445 return (DefWindowProc(hwnd, msg, wParam, lParam)); 446} 447 448 449/* The following internal helpers are invoked by the hooked tty and our app 450 */ 451 452 453/* Register or deregister the current process as a Windows9x style service. 454 * Experience shows this call is ignored across processes, so the second 455 * arg to RegisterServiceProcess (process group id) is effectively useless. 456 */ 457static LRESULT WINAPI RegisterWindows9xService(BOOL set_service) 458{ 459 static HINSTANCE hkernel; 460 static DWORD (WINAPI *register_service_process)(DWORD, DWORD) = NULL; 461 BOOL rv; 462 463 if (set_service == is_service) 464 return 1; 465 466#ifdef DBG 467 DbgPrintf("R %s proc %8.8x as a service\r\n", 468 set_service ? "installing" : "removing", 469 GetCurrentProcessId()); 470#endif 471 472 if (!register_service_process) 473 { 474 /* Obtain a handle to the kernel library */ 475 hkernel = LoadLibrary("KERNEL32.DLL"); 476 if (!hkernel) 477 return 0; 478 479 /* Find the RegisterServiceProcess function */ 480 register_service_process = (DWORD (WINAPI *)(DWORD, DWORD)) 481 GetProcAddress(hkernel, "RegisterServiceProcess"); 482 if (register_service_process == NULL) { 483 FreeLibrary(hkernel); 484 return 0; 485 } 486 } 487 488 /* Register this process as a service */ 489 rv = register_service_process(0, set_service != FALSE); 490 if (rv) 491 is_service = set_service; 492 493 if (!is_service) 494 { 495 /* Unload the kernel library */ 496 FreeLibrary(hkernel); 497 register_service_process = NULL; 498 } 499 return rv; 500} 501 502 503/* 504 * This function only works when this process is the active process 505 * (e.g. once it is running a child process, it can no longer determine 506 * which console window is its own.) 507 */ 508static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) 509{ 510 char tmp[8]; 511 if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty")) 512 { 513 DWORD wndproc, thisproc = GetCurrentProcessId(); 514 GetWindowThreadProcessId(wnd, &wndproc); 515 if (wndproc == thisproc) { 516 *((HWND*)retwnd) = wnd; 517 return FALSE; 518 } 519 } 520 return TRUE; 521} 522 523 524/* The remaining code all executes --in the tty's own process context-- 525 * 526 * That means special attention must be paid to what it's doing... 527 */ 528 529/* Subclass message process for the tty window 530 * 531 * This code -handles- WM_CLOSE, WM_ENDSESSION and WM_QUERYENDSESSION 532 * by dispatching them to the window identified by the hookwndprop 533 * property atom set against our window. Messages are then dispatched 534 * to origwndprop property atom we set against the window when we 535 * injected this subclass. This trick did not work with simply a hook. 536 */ 537static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 538 WPARAM wParam, LPARAM lParam) 539{ 540 WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop); 541 if (!origproc) 542 return 0; 543 544 if (msg == WM_NCDESTROY 545 || (msg == hookwndmsg && !LOWORD(wParam) && (HWND)lParam == hwnd)) 546 { 547 if (is_subclassed) { 548#ifdef DBG 549 DbgPrintf("W proc %08x hwnd:%08x Subclass removed\r\n", 550 GetCurrentProcessId(), hwnd); 551#endif 552 if (is_service) 553 RegisterWindows9xService(FALSE); 554 SetWindowLong(hwnd, GWL_WNDPROC, (LONG)origproc); 555 RemoveProp(hwnd, origwndprop); 556 RemoveProp(hwnd, hookwndprop); 557 is_subclassed = FALSE; 558 //if (hmodLock) 559 // FreeLibrary(hmodLock); 560 //hmodLock = NULL; 561 } 562 } 563 else if (msg == WM_CLOSE || msg == WM_ENDSESSION 564 || msg == WM_QUERYENDSESSION) 565 { 566 HWND child = (HWND)GetProp(hwnd, hookwndprop); 567 if (child) { 568#ifdef DBG 569 DbgPrintf("W proc %08x hwnd:%08x forwarded msg:%d\r\n", 570 GetCurrentProcessId(), hwnd, msg); 571#endif 572 return SendMessage(child, msg, wParam, lParam); 573 } 574 } 575 return CallWindowProc(origproc, hwnd, msg, wParam, lParam); 576} 577 578 579/* HookProc, once installed, is responsible for subclassing the system 580 * tty windows. It generally does nothing special itself, since 581 * research indicates that it cannot deal well with the messages we are 582 * interested in, that is, WM_CLOSE, WM_QUERYSHUTDOWN and WM_SHUTDOWN 583 * of the tty process. 584 * 585 * Respond and subclass only when a WM_NULL is received by the window. 586 */ 587int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam) 588{ 589 if (is_tty == -1 && *hwnd) 590 { 591 char ttybuf[8]; 592 HWND htty; 593 hwtty = *hwnd; 594 while (htty = GetParent(hwtty)) 595 hwtty = htty; 596 is_tty = (GetClassName(hwtty, ttybuf, sizeof(ttybuf)) 597 && !strcmp(ttybuf, "tty")); 598#ifdef DBG 599 if (is_tty) 600 DbgPrintf("H proc %08x tracking hwnd %08x\r\n", 601 GetCurrentProcessId(), hwtty); 602#endif 603 } 604 605 if (*msg == hookwndmsg && *wParam && *lParam == (LPARAM)hwtty && is_tty) 606 { 607 WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC); 608 //char myname[MAX_PATH]; 609 //if (GetModuleFileName(hmodThis, myname, sizeof(myname))) 610 // hmodLock = LoadLibrary(myname); 611 SetProp(hwtty, origwndprop, origproc); 612 SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc); 613 is_subclassed = TRUE; 614#ifdef DBG 615 DbgPrintf("H proc %08x hwnd:%08x Subclassed\r\n", 616 GetCurrentProcessId(), hwtty); 617#endif 618 if (LOWORD(*wParam) == 2) 619 RegisterWindows9xService(TRUE); 620 } 621 622 return -1; 623} 624 625 626/* 627 * PostMessage Hook: 628 */ 629LRESULT __declspec(dllexport) CALLBACK GetMsgProc(INT hc, WPARAM wParam, 630 LPARAM lParam) 631{ 632 PMSG pmsg; 633 634 pmsg = (PMSG)lParam; 635 636 if (pmsg) { 637 int rv = HookProc(hc, &pmsg->hwnd, &pmsg->message, 638 &pmsg->wParam, &pmsg->lParam); 639 if (rv != -1) 640 return rv; 641 } 642 /* 643 * CallNextHookEx apparently ignores the hhook argument, so pass NULL 644 */ 645 return CallNextHookEx(NULL, hc, wParam, lParam); 646} 647 648 649/* 650 * SendMessage Hook: 651 */ 652LRESULT __declspec(dllexport) CALLBACK CallWndProc(INT hc, WPARAM wParam, 653 LPARAM lParam) 654{ 655 PCWPSTRUCT pcwps = (PCWPSTRUCT)lParam; 656 657 if (pcwps) { 658 int rv = HookProc(hc, &pcwps->hwnd, &pcwps->message, 659 &pcwps->wParam, &pcwps->lParam); 660 if (rv != -1) 661 return rv; 662 } 663 /* 664 * CallNextHookEx apparently ignores the hhook argument, so pass NULL 665 */ 666 return CallNextHookEx(NULL, hc, wParam, lParam); 667} 668 669 670#ifdef DBG 671VOID DbgPrintf( 672 LPTSTR fmt, 673 ... 674 ) 675{ 676 static HANDLE mutex; 677 va_list marker; 678 TCHAR szBuf[256]; 679 DWORD t; 680 HANDLE gDbgOut; 681 682 va_start(marker, fmt); 683 wvsprintf(szBuf, fmt, marker); 684 va_end(marker); 685 686 if (!mutex) 687 mutex = CreateMutex(NULL, FALSE, "Win9xConHookDbgOut"); 688 WaitForSingleObject(mutex, INFINITE); 689 gDbgOut = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 690 NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); 691 WriteFile(gDbgOut, szBuf, strlen(szBuf), &t, NULL); 692 CloseHandle(gDbgOut); 693 ReleaseMutex(mutex); 694} 695#endif 696 697#endif /* WIN32 */ 698