Win9xConHook.c revision bc8fd1b0b1afdf89b8d28eefa8cd74e26ba97986
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 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
* 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
*
* 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
* #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 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 HHOOK hhkGetMessage;
//static HHOOK hhkCallWndProc;
typedef struct {
} 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
*/
#ifdef DBG
#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.
*/
{
if (ulReason == DLL_PROCESS_ATTACH)
{
//hmodThis = hModule;
if (!hookwndmsg) {
}
#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)
if (is_subclassed)
if (hmodHook)
{
if (hhkGetMessage) {
}
//if (hhkCallWndProc) {
// UnhookWindowsHookEx(hhkCallWndProc);
// hhkCallWndProc = NULL;
//}
}
if (is_service)
if (hookwndmsg) {
hookwndmsg = 0;
}
}
return TRUE;
}
* 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.
*/
{
/* If we have not yet done so */
FreeConsole();
if (name)
{
/* NOTE: this is static so the module can continue to
* access these args while we go on to other things
*/
if (hThread)
{
return TRUE;
}
}
else /* remove */
{
if (monitor_hwnd)
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
*/
{
if (add)
{
/* NOTE: this is static so the module can continue to
* access these args while we go on to other things
*/
if (!parent) {
#ifdef DBG
#endif
return FALSE;
}
if (add == 2) {
}
else
if (!hThread)
return FALSE;
if (hmodHook)
{
//hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
// (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
}
return TRUE;
}
else /* remove */
{
if (monitor_hwnd) {
}
if (hmodHook)
{
if (hhkGetMessage) {
}
//if (hhkCallWndProc) {
// UnhookWindowsHookEx(hhkCallWndProc);
// hhkCallWndProc = NULL;
//}
}
if (is_service)
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.
*/
{
wc.cbClsExtra = 0;
else
if (!RegisterClass(&wc)) {
#ifdef DBG
DbgPrintf("A proc %8.8x Error creating class %s (%d)\r\n",
#endif
return 0;
}
/* Create an invisible window */
if (!monitor_hwnd) {
#ifdef DBG
DbgPrintf("A proc %8.8x Error creating window %s %s (%d)\r\n",
#endif
return 0;
}
{
}
/* 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.
*/
{
{
#ifdef DBG
DbgPrintf("A proc %8.8x created %8.8x %s for tty wnd %8.8x\r\n",
#endif
}
return 0;
}
else if (msg == WM_DESTROY)
{
#ifdef DBG
DbgPrintf("A proc %8.8x destroyed %8.8x ttyConHookChild\r\n",
GetCurrentProcessId(), hwnd);
#endif
if (parent) {
}
monitor_hwnd = NULL;
}
{
#ifdef DBG
DbgPrintf("A proc %8.8x invoked CTRL_CLOSE_EVENT "
"returning %d\r\n",
GetCurrentProcessId(), rv);
#endif
if (rv)
return !rv;
}
{
if (lParam & ENDSESSION_LOGOFF)
{
#ifdef DBG
DbgPrintf("A proc %8.8x invoked CTRL_LOGOFF_EVENT "
"returning %d\r\n",
GetCurrentProcessId(), rv);
#endif
if (rv)
}
else
{
#ifdef DBG
DbgPrintf("A proc %8.8x invoked CTRL_SHUTDOWN_EVENT "
#endif
if (rv)
}
}
}
/* 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.
*/
{
if (set_service == is_service)
return 1;
#ifdef DBG
DbgPrintf("R %s proc %8.8x as a service\r\n",
#endif
if (!register_service_process)
{
/* Obtain a handle to the kernel library */
if (!hkernel)
return 0;
/* Find the RegisterServiceProcess function */
if (register_service_process == NULL) {
return 0;
}
}
/* Register this process as a service */
if (rv)
if (!is_service)
{
/* Unload the kernel library */
}
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.)
*/
{
char tmp[8];
{
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.
*/
{
if (!origproc)
return 0;
if (msg == WM_NCDESTROY
{
if (is_subclassed) {
#ifdef DBG
DbgPrintf("W proc %08x hwnd:%08x Subclass removed\r\n",
GetCurrentProcessId(), hwnd);
#endif
if (is_service)
//if (hmodLock)
// FreeLibrary(hmodLock);
//hmodLock = NULL;
}
}
|| msg == WM_QUERYENDSESSION)
{
if (child) {
#ifdef DBG
DbgPrintf("W proc %08x hwnd:%08x forwarded msg:%d\r\n",
#endif
}
}
}
/* 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.
*/
{
{
char ttybuf[8];
#ifdef DBG
if (is_tty)
DbgPrintf("H proc %08x tracking hwnd %08x\r\n",
GetCurrentProcessId(), hwtty);
#endif
}
{
//char myname[MAX_PATH];
//if (GetModuleFileName(hmodThis, myname, sizeof(myname)))
// hmodLock = LoadLibrary(myname);
#ifdef DBG
DbgPrintf("H proc %08x hwnd:%08x Subclassed\r\n",
GetCurrentProcessId(), hwtty);
#endif
}
return -1;
}
/*
* PostMessage Hook:
*/
{
if (pmsg) {
if (rv != -1)
return rv;
}
/*
* CallNextHookEx apparently ignores the hhook argument, so pass NULL
*/
}
/*
* SendMessage Hook:
*/
{
if (pcwps) {
if (rv != -1)
return rv;
}
/*
* CallNextHookEx apparently ignores the hhook argument, so pass NULL
*/
}
#ifdef DBG
...
)
{
DWORD t;
if (!mutex)
}
#endif
#endif /* WIN32 */