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