upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / server / mpm / winnt / Win9xConHook.c
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  */
57 static UINT hookwndmsg = 0;
58 static LPCTSTR origwndprop;
59 static LPCTSTR hookwndprop;
60 static 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  */
70 static int is_tty = -1;
71 static HWND hwtty = NULL;
72 static 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  */
85 static HMODULE hmodHook = NULL;
86 static HHOOK hhkGetMessage;
87 //static HHOOK hhkCallWndProc;
88 static HWND monitor_hwnd = NULL;
89
90 typedef 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  */
107 static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd);
108 static LRESULT WINAPI RegisterWindows9xService(BOOL set_service);
109 static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, 
110                                               WPARAM wParam, LPARAM lParam);
111 static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty);
112 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 
113                                 WPARAM wParam, LPARAM lParam);
114 static int HookProc(int hc, HWND *hwnd, UINT *msg, 
115                     WPARAM *wParam, LPARAM *lParam);
116 #ifdef DBG
117 static 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  */
125 BOOL __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  */
183 BOOL __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  */
228 BOOL __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  */
310 static 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  */
371 static 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  */
457 static 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  */
508 static 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  */
537 static 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  */
587 int 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  */
629 LRESULT __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  */
652 LRESULT __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
671 VOID 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 */