/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#ifdef WIN32
/*
* Win9xConHook.dll - a hook proc to clean up Win95/98 console behavior.
*
* It is well(?) documented by Microsoft that the Win9x HandlerRoutine
* hooked by the SetConsoleCtrlHandler never receives the CTRL_CLOSE_EVENT,
* CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT signals.
*
* It is possible to have a second window to monitor the WM_ENDSESSION
* message, but the close button still fails..
*
* There is a 16bit polling method for the close window option, but this
* is CPU intensive and requires thunking.
*
* Attempts to subclass the 'tty' console fail, since that message thread
* is actually owned by the 16 bit winoldap.mod process, although the
* window reports it is owned by the process/thread of the console app.
*
* Win9xConHook is thunks the WM_CLOSE and WM_ENDSESSION messages,
* first through a window hook procedure in the winoldap context, into
* a subclass WndProc, and on to a second hidden monitor window in the
* console application's context that dispatches them to the console app's
* registered HandlerRoutine.
*/
/* This debugging define turns on output to COM1, although you better init
* the port first (even using hyperterm). It's the only way to catch the
* goings on within system logoff/shutdown.
* #define DBG 1
*/
#include <windows.h>
/* Variables used within any process context:
* hookwndmsg is a shared message to send Win9xConHook signals
* origwndprop is a wndprop atom to store the orig wndproc of the tty
* hookwndprop is a wndprop atom to store the hwnd of the hidden child
* is_service reminds us to unmark this process on the way out
*/
static
UINT
hookwndmsg = 0;
static
LPCTSTR
origwndprop;
static
LPCTSTR
hookwndprop;
static
BOOL
is_service = 0;
//static HMODULE hmodThis = NULL;
/* Variables used within the tty processes' context:
* is_tty flags this process; -1 == unknown, 1 == if tty, 0 == if not
* hw_tty is the handle of the top level tty in this process context
* is_subclassed is toggled to assure DllMain removes the subclass on unload
* hmodLock is there to try and prevent this dll from being unloaded if the
* hook is removed while we are subclassed
*/
static
int
is_tty = -1;
static
HWND
hwtty = NULL;
static
BOOL
is_subclassed = 0;
// This simply causes a gpfault the moment it tries to FreeLibrary within
// the subclass procedure ... not good.
//static HMODULE hmodLock = NULL;
/* Variables used within the service or console app's context:
* hmodHook is the instance handle of this module for registering the hooks
* hhkGetMessage is the hook handle for catching Posted messages
* hhkGetMessage is the hook handle for catching Sent messages
* monitor_hwnd is the invisible window that handles our tty messages
* the tty_info strucure is used to pass args into the hidden window's thread
*/
static
HMODULE
hmodHook = NULL;
static
HHOOK
hhkGetMessage;
//static HHOOK hhkCallWndProc;
static
HWND
monitor_hwnd = NULL;
typedef
struct
{
PHANDLER_ROUTINE phandler;
HINSTANCE
instance;
HWND
parent;
INT
type;
LPCSTR
name;
} tty_info;
/* These are the GetWindowLong offsets for the hidden window's internal info
* gwltty_phandler is the address of the app's HandlerRoutine
* gwltty_ttywnd is the tty this hidden window will handle messages from
*/
#define gwltty_phandler 0
#define gwltty_ttywnd 4
/* Forward declaration prototypes for internal functions
*/
static
BOOL
CALLBACK EnumttyWindow(
HWND
wnd,
LPARAM
retwnd);
static
LRESULT
WINAPI RegisterWindows9xService(
BOOL
set_service);
static
LRESULT
CALLBACK ttyConsoleCtrlWndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam);
static
DWORD
WINAPI ttyConsoleCtrlThread(
LPVOID
tty);
static
LRESULT
CALLBACK WndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam);
static
int
HookProc(
int
hc,
HWND
*hwnd,
UINT
*msg,
WPARAM
*wParam,
LPARAM
*lParam);
#ifdef DBG
static
VOID
DbgPrintf(
LPTSTR
fmt, ...);
#endif
/* DllMain is invoked by every process in the entire system that is hooked
* by our window hooks, notably the tty processes' context, and by the user
* who wants tty messages (the app). Keep it light and simple.
*/
BOOL
__declspec
(
dllexport
) APIENTRY DllMain(
HINSTANCE
hModule,
ULONG
ulReason,
LPVOID
pctx)
{
if
(ulReason == DLL_PROCESS_ATTACH)
{
//hmodThis = hModule;
if
(!hookwndmsg) {
origwndprop = MAKEINTATOM(GlobalAddAtom(
"Win9xConHookOrigProc"
));
hookwndprop = MAKEINTATOM(GlobalAddAtom(
"Win9xConHookThunkWnd"
));
hookwndmsg = RegisterWindowMessage(
"Win9xConHookMsg"
);
}
#ifdef DBG
// DbgPrintf("H ProcessAttach:%8.8x\r\n",
// GetCurrentProcessId());
#endif
}
else
if
( ulReason == DLL_PROCESS_DETACH )
{
#ifdef DBG
// DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId());
#endif
if
(monitor_hwnd)
SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
if
(is_subclassed)
SendMessage(hwtty, hookwndmsg, 0, (
LPARAM
)hwtty);
if
(hmodHook)
{
if
(hhkGetMessage) {
UnhookWindowsHookEx(hhkGetMessage);
hhkGetMessage = NULL;
}
//if (hhkCallWndProc) {
// UnhookWindowsHookEx(hhkCallWndProc);
// hhkCallWndProc = NULL;
//}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
if
(is_service)
RegisterWindows9xService(FALSE);
if
(hookwndmsg) {
GlobalDeleteAtom((
ATOM
)origwndprop);
GlobalDeleteAtom((
ATOM
)hookwndprop);
hookwndmsg = 0;
}
}
return
TRUE;
}
/* This group of functions are provided for the service/console app
* to register itself a HandlerRoutine to accept tty or service messages
*/
/* Exported function that creates a Win9x 'service' via a hidden window,
* that notifies the process via the HandlerRoutine messages.
*/
BOOL
__declspec
(
dllexport
) WINAPI Windows9xServiceCtrlHandler(
PHANDLER_ROUTINE phandler,
LPCSTR
name)
{
/* If we have not yet done so */
FreeConsole();
if
(name)
{
DWORD
tid;
HANDLE
hThread;
/* NOTE: this is static so the module can continue to
* access these args while we go on to other things
*/
static
tty_info tty;
tty.instance = GetModuleHandle(NULL);
tty.phandler = phandler;
tty.parent = NULL;
tty.name = name;
tty.type = 2;
RegisterWindows9xService(TRUE);
hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
(
LPVOID
)&tty, 0, &tid);
if
(hThread)
{
CloseHandle(hThread);
return
TRUE;
}
}
else
/* remove */
{
if
(monitor_hwnd)
SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
RegisterWindows9xService(FALSE);
return
TRUE;
}
return
FALSE;
}
/* Exported function that registers a HandlerRoutine to accept missing
* Win9x CTRL_EVENTs from the tty window, as NT does without a hassle.
* If add is 1 or 2, register the handler, if 2 also mark it as a service.
* If add is 0 deregister the handler, and unmark if a service
*/
BOOL
__declspec
(
dllexport
) WINAPI FixConsoleCtrlHandler(
PHANDLER_ROUTINE phandler,
INT
add)
{
HWND
parent;
if
(add)
{
HANDLE
hThread;
DWORD
tid;
/* NOTE: this is static so the module can continue to
* access these args while we go on to other things
*/
static
tty_info tty;
EnumWindows(EnumttyWindow, (
LPARAM
)&parent);
if
(!parent) {
#ifdef DBG
DbgPrintf(
"A EnumttyWindow failed (%d)\r\n"
, GetLastError());
#endif
return
FALSE;
}
tty.instance = GetModuleHandle(NULL);
tty.phandler = phandler;
tty.parent = parent;
tty.type = add;
if
(add == 2) {
tty.name =
"ttyService"
;
RegisterWindows9xService(TRUE);
}
else
tty.name =
"ttyMonitor"
;
hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
(
LPVOID
)&tty, 0, &tid);
if
(!hThread)
return
FALSE;
CloseHandle(hThread);
hmodHook = LoadLibrary(
"Win9xConHook.dll"
);
if
(hmodHook)
{
hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
(HOOKPROC)GetProcAddress(hmodHook,
"GetMsgProc"
), hmodHook, 0);
//hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
// (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
}
return
TRUE;
}
else
/* remove */
{
if
(monitor_hwnd) {
SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
}
if
(hmodHook)
{
if
(hhkGetMessage) {
UnhookWindowsHookEx(hhkGetMessage);
hhkGetMessage = NULL;
}
//if (hhkCallWndProc) {
// UnhookWindowsHookEx(hhkCallWndProc);
// hhkCallWndProc = NULL;
//}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
if
(is_service)
RegisterWindows9xService(FALSE);
return
TRUE;
}
return
FALSE;
}
/* The following internal helpers are only used within the app's context
*/
/* ttyConsoleCreateThread is the process that runs within the user app's
* context. It creates and pumps the messages of a hidden monitor window,
* watching for messages from the system, or the associated subclassed tty
* window. Things can happen in our context that can't be done from the
* tty's context, and visa versa, so the subclass procedure and this hidden
* window work together to make it all happen.
*/
static
DWORD
WINAPI ttyConsoleCtrlThread(
LPVOID
tty)
{
WNDCLASS wc;
MSG msg;
wc.style = CS_GLOBALCLASS;
wc.lpfnWndProc = ttyConsoleCtrlWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 8;
wc.hInstance = NULL;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
if
(((tty_info*)tty)->parent)
wc.lpszClassName =
"ttyConHookChild"
;
else
wc.lpszClassName =
"ApacheWin95ServiceMonitor"
;
if
(!RegisterClass(&wc)) {
#ifdef DBG
DbgPrintf(
"A proc %8.8x Error creating class %s (%d)\r\n"
,
GetCurrentProcessId(), wc.lpszClassName, GetLastError());
#endif
return
0;
}
/* Create an invisible window */
monitor_hwnd = CreateWindow(wc.lpszClassName, ((tty_info*)tty)->name,
WS_OVERLAPPED & ~WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
((tty_info*)tty)->instance, tty);
if
(!monitor_hwnd) {
#ifdef DBG
DbgPrintf(
"A proc %8.8x Error creating window %s %s (%d)\r\n"
,
GetCurrentProcessId(), wc.lpszClassName,
((tty_info*)tty)->name, GetLastError());
#endif
return
0;
}
while
(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* Tag again as deleted, just in case we missed WM_DESTROY */
monitor_hwnd = NULL;
return
0;
}
/* This is the WndProc procedure for our invisible window.
* When our subclasssed tty window receives the WM_CLOSE, WM_ENDSESSION,
* or WM_QUERYENDSESSION messages, the message is dispatched to our hidden
* window (this message process), and we call the installed HandlerRoutine
* that was registered by the app.
*/
static
LRESULT
CALLBACK ttyConsoleCtrlWndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam)
{
if
(msg == WM_CREATE)
{
tty_info *tty = (tty_info*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLong(hwnd, gwltty_phandler, (
LONG
)tty->phandler);
SetWindowLong(hwnd, gwltty_ttywnd, (
LONG
)tty->parent);
#ifdef DBG
DbgPrintf(
"A proc %8.8x created %8.8x %s for tty wnd %8.8x\r\n"
,
GetCurrentProcessId(), hwnd,
tty->name, tty->parent);
#endif
if
(tty->parent) {
SetProp(tty->parent, hookwndprop, hwnd);
PostMessage(tty->parent, hookwndmsg,
tty->type, (
LPARAM
)tty->parent);
}
return
0;
}
else
if
(msg == WM_DESTROY)
{
HWND
parent = (
HWND
)GetWindowLong(hwnd, gwltty_ttywnd);
#ifdef DBG
DbgPrintf(
"A proc %8.8x destroyed %8.8x ttyConHookChild\r\n"
,
GetCurrentProcessId(), hwnd);
#endif
if
(parent) {
RemoveProp(parent, hookwndprop);
SendMessage(parent, hookwndmsg, 0, (
LPARAM
)parent);
}
monitor_hwnd = NULL;
}
else
if
(msg == WM_CLOSE)
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
LRESULT
rv = phandler(CTRL_CLOSE_EVENT);
#ifdef DBG
DbgPrintf(
"A proc %8.8x invoked CTRL_CLOSE_EVENT "
"returning %d\r\n"
,
GetCurrentProcessId(), rv);
#endif
if
(rv)
return
!rv;
}
else
if
((msg == WM_QUERYENDSESSION) || (msg == WM_ENDSESSION))
{
if
(lParam & ENDSESSION_LOGOFF)
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
LRESULT
rv = phandler(CTRL_LOGOFF_EVENT);
#ifdef DBG
DbgPrintf(
"A proc %8.8x invoked CTRL_LOGOFF_EVENT "
"returning %d\r\n"
,
GetCurrentProcessId(), rv);
#endif
if
(rv)
return
((msg == WM_QUERYENDSESSION) ? rv : !rv);
}
else
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
LRESULT
rv = phandler(CTRL_SHUTDOWN_EVENT);
#ifdef DBG
DbgPrintf(
"A proc %8.8x invoked CTRL_SHUTDOWN_EVENT "
"returning %d\r\n"
, GetCurrentProcessId(), rv);
#endif
if
(rv)
return
((msg == WM_QUERYENDSESSION) ? rv : !rv);
}
}
return
(DefWindowProc(hwnd, msg, wParam, lParam));
}
/* The following internal helpers are invoked by the hooked tty and our app
*/
/* Register or deregister the current process as a Windows9x style service.
* Experience shows this call is ignored across processes, so the second
* arg to RegisterServiceProcess (process group id) is effectively useless.
*/
static
LRESULT
WINAPI RegisterWindows9xService(
BOOL
set_service)
{
static
HINSTANCE
hkernel;
static
DWORD
(WINAPI *register_service_process)(
DWORD
,
DWORD
) = NULL;
BOOL
rv;
if
(set_service == is_service)
return
1;
#ifdef DBG
DbgPrintf(
"R %s proc %8.8x as a service\r\n"
,
set_service ?
"installing"
:
"removing"
,
GetCurrentProcessId());
#endif
if
(!register_service_process)
{
/* Obtain a handle to the kernel library */
hkernel = LoadLibrary(
"KERNEL32.DLL"
);
if
(!hkernel)
return
0;
/* Find the RegisterServiceProcess function */
register_service_process = (
DWORD
(WINAPI *)(
DWORD
,
DWORD
))
GetProcAddress(hkernel,
"RegisterServiceProcess"
);
if
(register_service_process == NULL) {
FreeLibrary(hkernel);
return
0;
}
}
/* Register this process as a service */
rv = register_service_process(0, set_service != FALSE);
if
(rv)
is_service = set_service;
if
(!is_service)
{
/* Unload the kernel library */
FreeLibrary(hkernel);
register_service_process = NULL;
}
return
rv;
}
/*
* This function only works when this process is the active process
* (e.g. once it is running a child process, it can no longer determine
* which console window is its own.)
*/
static
BOOL
CALLBACK EnumttyWindow(
HWND
wnd,
LPARAM
retwnd)
{
char
tmp[8];
if
(GetClassName(wnd, tmp,
sizeof
(tmp)) && !
strcmp
(tmp,
"tty"
))
{
DWORD
wndproc, thisproc = GetCurrentProcessId();
GetWindowThreadProcessId(wnd, &wndproc);
if
(wndproc == thisproc) {
*((
HWND
*)retwnd) = wnd;
return
FALSE;
}
}
return
TRUE;
}
/* The remaining code all executes --in the tty's own process context--
*
* That means special attention must be paid to what it's doing...
*/
/* Subclass message process for the tty window
*
* This code -handles- WM_CLOSE, WM_ENDSESSION and WM_QUERYENDSESSION
* by dispatching them to the window identified by the hookwndprop
* property atom set against our window. Messages are then dispatched
* to origwndprop property atom we set against the window when we
* injected this subclass. This trick did not work with simply a hook.
*/
static
LRESULT
CALLBACK WndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam)
{
WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop);
if
(!origproc)
return
0;
if
(msg == WM_NCDESTROY
|| (msg == hookwndmsg && !LOWORD(wParam) && (
HWND
)lParam == hwnd))
{
if
(is_subclassed) {
#ifdef DBG
DbgPrintf(
"W proc %08x hwnd:%08x Subclass removed\r\n"
,
GetCurrentProcessId(), hwnd);
#endif
if
(is_service)
RegisterWindows9xService(FALSE);
SetWindowLong(hwnd, GWL_WNDPROC, (
LONG
)origproc);
RemoveProp(hwnd, origwndprop);
RemoveProp(hwnd, hookwndprop);
is_subclassed = FALSE;
//if (hmodLock)
// FreeLibrary(hmodLock);
//hmodLock = NULL;
}
}
else
if
(msg == WM_CLOSE || msg == WM_ENDSESSION
|| msg == WM_QUERYENDSESSION)
{
HWND
child = (
HWND
)GetProp(hwnd, hookwndprop);
if
(child) {
#ifdef DBG
DbgPrintf(
"W proc %08x hwnd:%08x forwarded msg:%d\r\n"
,
GetCurrentProcessId(), hwnd, msg);
#endif
return
SendMessage(child, msg, wParam, lParam);
}
}
return
CallWindowProc(origproc, hwnd, msg, wParam, lParam);
}
/* HookProc, once installed, is responsible for subclassing the system
* tty windows. It generally does nothing special itself, since
* research indicates that it cannot deal well with the messages we are
* interested in, that is, WM_CLOSE, WM_QUERYSHUTDOWN and WM_SHUTDOWN
* of the tty process.
*
* Respond and subclass only when a WM_NULL is received by the window.
*/
int
HookProc(
int
hc,
HWND
*hwnd,
UINT
*msg,
WPARAM
*wParam,
LPARAM
*lParam)
{
if
(is_tty == -1 && *hwnd)
{
char
ttybuf[8];
HWND
htty;
hwtty = *hwnd;
while
(htty = GetParent(hwtty))
hwtty = htty;
is_tty = (GetClassName(hwtty, ttybuf,
sizeof
(ttybuf))
&& !
strcmp
(ttybuf,
"tty"
));
#ifdef DBG
if
(is_tty)
DbgPrintf(
"H proc %08x tracking hwnd %08x\r\n"
,
GetCurrentProcessId(), hwtty);
#endif
}
if
(*msg == hookwndmsg && *wParam && *lParam == (
LPARAM
)hwtty && is_tty)
{
WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
//char myname[MAX_PATH];
//if (GetModuleFileName(hmodThis, myname, sizeof(myname)))
// hmodLock = LoadLibrary(myname);
SetProp(hwtty, origwndprop, origproc);
SetWindowLong(hwtty, GWL_WNDPROC, (
LONG
)WndProc);
is_subclassed = TRUE;
#ifdef DBG
DbgPrintf(
"H proc %08x hwnd:%08x Subclassed\r\n"
,
GetCurrentProcessId(), hwtty);
#endif
if
(LOWORD(*wParam) == 2)
RegisterWindows9xService(TRUE);
}
return
-1;
}
/*
* PostMessage Hook:
*/
LRESULT
__declspec
(
dllexport
) CALLBACK GetMsgProc(
INT
hc,
WPARAM
wParam,
LPARAM
lParam)
{
PMSG pmsg;
pmsg = (PMSG)lParam;
if
(pmsg) {
int
rv = HookProc(hc, &pmsg->hwnd, &pmsg->message,
&pmsg->wParam, &pmsg->lParam);
if
(rv != -1)
return
rv;
}
/*
* CallNextHookEx apparently ignores the hhook argument, so pass NULL
*/
return
CallNextHookEx(NULL, hc, wParam, lParam);
}
/*
* SendMessage Hook:
*/
LRESULT
__declspec
(
dllexport
) CALLBACK CallWndProc(
INT
hc,
WPARAM
wParam,
LPARAM
lParam)
{
PCWPSTRUCT pcwps = (PCWPSTRUCT)lParam;
if
(pcwps) {
int
rv = HookProc(hc, &pcwps->hwnd, &pcwps->message,
&pcwps->wParam, &pcwps->lParam);
if
(rv != -1)
return
rv;
}
/*
* CallNextHookEx apparently ignores the hhook argument, so pass NULL
*/
return
CallNextHookEx(NULL, hc, wParam, lParam);
}
#ifdef DBG
VOID
DbgPrintf(
LPTSTR
fmt,
...
)
{
static
HANDLE
mutex;
va_list
marker;
TCHAR
szBuf[256];
DWORD
t;
HANDLE
gDbgOut;
va_start
(marker, fmt);
wvsprintf(szBuf, fmt, marker);
va_end
(marker);
if
(!mutex)
mutex = CreateMutex(NULL, FALSE,
"Win9xConHookDbgOut"
);
WaitForSingleObject(mutex, INFINITE);
gDbgOut = CreateFile(
"COM1"
, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
WriteFile(gDbgOut, szBuf,
strlen
(szBuf), &t, NULL);
CloseHandle(gDbgOut);
ReleaseMutex(mutex);
}
#endif
#endif /* WIN32 */