Commit 42f231ac authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

  So close we can taste it, this patch adds necessary delay timers for
  the win32 console window detection, gobs of documentation and some
  streamlined and more debuggable structure, pardon the reorg of the
  source for Win9xConHook.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87284 13f79535-47bb-0310-9956-ffa450edef68
parent f069225f
Loading
Loading
Loading
Loading
+335 −265
Original line number Diff line number Diff line
@@ -80,96 +80,106 @@
 * registered HandlerRoutine.
 */

/* #define DBG 1
 */
#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;

/* 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
 *  is_hooked is toggled to assure DllMain removes the subclass
 *  is_subclassed is toggled to assure DllMain removes the subclass on unload
 */
static int is_tty = -1;
static HWND hwtty = NULL;
static BOOL is_subclassed = 0;

/* 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;

static LPCTSTR origwndprop = NULL;
static LPCTSTR hookwndprop = NULL;
typedef struct {
    PHANDLER_ROUTINE phandler;
    HINSTANCE instance;
    HWND parent;
    LPCSTR name;
} tty_info;

static HWND monitor_hwnd = NULL;
/* 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

static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd);

LRESULT __declspec(dllexport) WINAPI RegisterWindows9xService(BOOL is_service)
{
    static BOOL is_registered = FALSE;
    HINSTANCE hkernel;
    DWORD (WINAPI *register_service_process)(DWORD, DWORD);
    BOOL rv;

    if (is_service == is_registered)
        return 1;

    /* 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, is_service != FALSE);
    
    /* Unload the kernel library */
    FreeLibrary(hkernel);
    return rv;
}

/* 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(PVOID hModule, ULONG ulReason, PCONTEXT pctx)
{
    if (ulReason == DLL_PROCESS_ATTACH) 
    {
        if (!hookwndmsg) {
            origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc"));
            hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd"));
            hookwndmsg = RegisterWindowMessage("Win9xConHookMsg");
        }
#ifdef DBG
        DbgPrintf("H ProcessAttach:%8.8x\r\n", GetCurrentProcessId());
#endif
        origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc"));
        hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd"));
    }
    else if ( ulReason == DLL_PROCESS_DETACH ) 
    {
        HWND parent;
#ifdef DBG
        DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId());                
#endif
        if (is_subclassed) {
        if (monitor_hwnd)
            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
        if (is_subclassed) 
        {
            WNDPROC origproc = (WNDPROC) GetProp(hwtty, origwndprop);
            HWND child = (HWND) GetProp(hwtty, hookwndprop);
            if (child) {
                RemoveProp(hwtty, hookwndprop);
                PostMessage(child, WM_DESTROY, 0, 0);
            }
            if (origproc) {
                SetWindowLong(hwtty, GWL_WNDPROC, (LONG)origproc);
                RemoveProp(hwtty, origwndprop);
                is_subclassed = 0;
            }
            RegisterWindows9xService(FALSE);
        }
        EnumWindows(EnumttyWindow, (LPARAM)&parent);
        if (parent) {
            HWND child = (HWND)GetProp(parent, hookwndprop);
            if (child)
                SendMessage(child, WM_DESTROY, 0, 0);
        }
        if (hmodHook)
        {
@@ -184,99 +194,155 @@ BOOL __declspec(dllexport) APIENTRY DllMain(PVOID hModule, ULONG ulReason, PCONT
            FreeLibrary(hmodHook);
            hmodHook = NULL;
        }
        if (is_service)
            RegisterWindows9xService(FALSE);
        if (hookwndmsg) {
            GlobalDeleteAtom((ATOM)origwndprop);
            GlobalDeleteAtom((ATOM)hookwndprop);
            hookwndmsg = 0;
        }
    }
    return TRUE;
}


typedef struct {
    PHANDLER_ROUTINE phandler;
    HINSTANCE instance;
    HWND parent;
    char *name;
} tty_info;
/*  This group of functions are provided for the service/console app
 *  to register itself a HandlerRoutine to accept tty or service messages
 */


#define gwltty_phandler 0
#define gwltty_ttywnd 4
/*  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();

/* This is the WndProc procedure for our invisible window.
 * When our tty subclass WndProc recieves the WM_CLOSE, 
 * or WM_QUERYENDSESSION messages, we call the installed
 * HandlerRoutine that was registered with 
 * If a user logs off, the window is sent WM_QUERYENDSESSION 
 * as well, but with lParam != 0. We ignore this case.
    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
         */
LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
        static tty_info tty;
        tty.instance = GetModuleHandle(NULL);
        tty.phandler = phandler;
        tty.parent = NULL;
        tty.name = name;
        RegisterWindows9xService(TRUE);
        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
                               (LPVOID)&tty, 0, &tid);
        if (hThread)
        {
    if (msg == WM_CREATE)
            CloseHandle(hThread);
            return TRUE;
        }
    }
    else /* remove */
    {
        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("S Created ttyConHookChild:%8.8x\r\n", hwnd);
#endif
        if (((tty_info*)tty)->parent)
            SetProp(((tty_info*)tty)->parent, hookwndprop, hwnd);
        return 0;
        if (monitor_hwnd)
            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
        RegisterWindows9xService(FALSE);
        return TRUE;
    }
    else if (msg == WM_DESTROY)
    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 = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
        if (parent)
            RemoveProp(parent, hookwndprop);
    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)
            return FALSE;
        tty.instance = GetModuleHandle(NULL);
        tty.phandler = phandler;
        tty.parent = parent;
        if (add == 2) {
            tty.name = "ttyService";
            RegisterWindows9xService(TRUE);
        }
    else if (msg == WM_CLOSE)
        else 
            tty.name = "ttyMonitor";
        hmodHook = LoadLibrary("Win9xConHook.dll");
        if (hmodHook)
        {
        PHANDLER_ROUTINE phandler = 
            (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
        DbgPrintf("S Invoking CTRL_CLOSE_EVENT:%8.8x\r\n",
                  GetCurrentProcessId());
#endif
        return !phandler(CTRL_CLOSE_EVENT);
            hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
                (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0);
            hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
                (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
        }        
    else if (msg == WM_QUERYENDSESSION)
        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
                               (LPVOID)&tty, 0, &tid);
        if (hThread)
        {
        if (lParam & ENDSESSION_LOGOFF) 
            CloseHandle(hThread);
            /* All is set ... wake up the tty window */
            PostMessage(parent, hookwndmsg, add, 0);
            return TRUE;
        }
    }
    else /* remove */
    {
            PHANDLER_ROUTINE phandler = 
                (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
            DbgPrintf("S Invoking CTRL_LOGOFF_EVENT:%8.8x\r\n",
                      GetCurrentProcessId());
#endif
            return !phandler(CTRL_LOGOFF_EVENT);
        if (monitor_hwnd) {
            parent = (HWND) GetWindowLong(monitor_hwnd, gwltty_ttywnd);
            if (parent)
                PostMessage(parent, hookwndmsg, add, 0);
            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
        }
        else
        if (hmodHook)
        {
            PHANDLER_ROUTINE phandler = 
                (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
            DbgPrintf("S Invoking CTRL_SHUTDOWN_EVENT:%8.8x\r\n",
                      GetCurrentProcessId());
#endif
            return !phandler(CTRL_SHUTDOWN_EVENT);
            if (hhkGetMessage) {
                UnhookWindowsHookEx(hhkGetMessage);
                hhkGetMessage = NULL;
            }
            if (hhkCallWndProc) {
                UnhookWindowsHookEx(hhkCallWndProc);
                hhkCallWndProc = NULL;
            }
    return (DefWindowProc(hwnd, msg, wParam, lParam));
            FreeLibrary(hmodHook);
            hmodHook = NULL;
        }
        if (is_service)
            RegisterWindows9xService(FALSE);
        return TRUE;
    }
    return FALSE;
}


DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
{
    /* When running as a service under Windows 9x, ConsoleCtrlHandler 
     * does not respond properly when the user logs off or the system 
     * is shutdown.  If the WatchWindow thread is created with a NULL
     * service_name argument, then the ...SystemMonitor window class is
     * used to create the "Apache" window to watch for logoff and shutdown.
     * If the service_name is provided, the ...ServiceMonitor window class
     * is used to create the window named by the service_name argument,
     * and the logoff message is ignored.
/*  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;
@@ -320,177 +386,196 @@ DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    /* Tag again as deleted, just in case we missed WM_DESTROY */
    monitor_hwnd = NULL;
    return 0;
}


/* 
 * 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.)
/* 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 BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd)
static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    char tmp[4];
    if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty")) 
    if (msg == WM_CREATE)
    {
        DWORD wndproc, thisproc = GetCurrentProcessId();
        GetWindowThreadProcessId(wnd, &wndproc);
        if (wndproc == thisproc) {
            *((HWND*)retwnd) = wnd;
            return FALSE;
        }
        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("S Created ttyConHookChild:%8.8x\r\n", hwnd);
#endif
        if (((tty_info*)tty)->parent)
            SetProp(((tty_info*)tty)->parent, hookwndprop, hwnd);
        return 0;
    }
    return TRUE;
    else if (msg == WM_DESTROY)
    {
        HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
        if (parent)
            RemoveProp(parent, hookwndprop);
        monitor_hwnd = NULL;
    }


/*
 * Exported function that sets up the fixup child window 
 */
BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler(
        PHANDLER_ROUTINE phandler,
        BOOL add)
    else if (msg == WM_CLOSE)
    {
    /* If we have not yet done so */
    FreeConsole();

    if (add)
        PHANDLER_ROUTINE phandler = 
            (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
        DbgPrintf("S Invoking CTRL_CLOSE_EVENT:%8.8x\r\n",
                  GetCurrentProcessId());
#endif
        return !phandler(CTRL_CLOSE_EVENT);
    }
    else if (msg == WM_QUERYENDSESSION)
    {
        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 = "Apache Service";
        RegisterWindows9xService(TRUE);
        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
                               (LPVOID)&tty, 0, &tid);
        if (hThread)
        if (lParam & ENDSESSION_LOGOFF) 
        {
            CloseHandle(hThread);
            return TRUE;
        }
            PHANDLER_ROUTINE phandler = 
                (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
            DbgPrintf("S Invoking CTRL_LOGOFF_EVENT:%8.8x\r\n",
                      GetCurrentProcessId());
#endif
            return !phandler(CTRL_LOGOFF_EVENT);
        }
    else /* remove */
        else
        {
        HWND child = FindWindowEx(NULL, NULL, "ttyConHookChild", NULL);
        if (monitor_hwnd)
            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
        RegisterWindows9xService(FALSE);
        return TRUE;
            PHANDLER_ROUTINE phandler = 
                (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
            DbgPrintf("S Invoking CTRL_SHUTDOWN_EVENT:%8.8x\r\n",
                      GetCurrentProcessId());
#endif
            return !phandler(CTRL_SHUTDOWN_EVENT);
        }
    return FALSE;
    }
    return (DefWindowProc(hwnd, msg, wParam, lParam));
}


/*
 * Exported function that sets up the fixup child window and dispatch
/*  The following internal helpers are invoked by the hooked tty and our app
 */
BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler(
        PHANDLER_ROUTINE phandler,
        BOOL add)
{
    HWND parent;
    EnumWindows(EnumttyWindow, (LPARAM)&parent);

    if (!parent)
        return FALSE;
 
    if (add)
    {
        HANDLE hThread;
        DWORD tid;
        static tty_info tty;
        tty.instance = GetModuleHandle(NULL);
        tty.phandler = phandler;
        tty.parent = parent;
        tty.name = "ttyListener";
        hmodHook = LoadLibrary("Win9xConHook.dll");
        if (hmodHook)
/*  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)
{
            hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
                (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0);
            hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
                (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
        }
    HINSTANCE hkernel;
    DWORD (WINAPI *register_service_process)(DWORD, DWORD);
    BOOL rv;

        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
                               (LPVOID)&tty, 0, &tid);
        if (hThread)
        {
            CloseHandle(hThread);
            return TRUE;
    if (set_service == is_service)
        return 1;

    /* 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, is_service != FALSE);
    if (rv)
        is_service = set_service;
    
    /* Unload the kernel library */
    FreeLibrary(hkernel);
    return rv;
}
    else /* remove */


/* 
 * 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)
{
        HWND child = FindWindowEx(parent, NULL, "ttyConHookChild", NULL);
        if (child)
            SendMessage(child, WM_DESTROY, 0, 0);
        if (hmodHook)
    char tmp[4];
    if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty")) 
    {
            if (hhkGetMessage) {
                UnhookWindowsHookEx(hhkGetMessage);
                hhkGetMessage = NULL;
        DWORD wndproc, thisproc = GetCurrentProcessId();
        GetWindowThreadProcessId(wnd, &wndproc);
        if (wndproc == thisproc) {
            *((HWND*)retwnd) = wnd;
            return FALSE;
        }
            if (hhkCallWndProc) {
                UnhookWindowsHookEx(hhkCallWndProc);
                hhkCallWndProc = NULL;
            }
            FreeLibrary(hmodHook);
            hmodHook = NULL;
    }
    return TRUE;
}
    return FALSE;
}


/*
 * Subclass message process for the tty window
/* 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.
 */
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop);
    if (!origproc)
        return 0;

    switch (msg)
    if (msg == WM_NCDESTROY || msg == hookwndmsg)
    {
        case WM_NCDESTROY:
#ifdef DBG
        DbgPrintf("W Proc %08x hwnd:%08x Subclass removed\r\n", 
                  GetCurrentProcessId(), hwnd);
#endif
            is_subclassed = FALSE;
        if (is_subclassed) {
            if (is_service)
                RegisterWindows9xService(FALSE);
            SetWindowLong(hwnd, GWL_WNDPROC, (LONG)origproc);
            RemoveProp(hwnd, origwndprop);
            break;

        case WM_CLOSE:
        case WM_ENDSESSION:
        case WM_QUERYENDSESSION:
            RemoveProp(hwnd, hookwndprop);
            is_subclassed = FALSE;
        }
    }
    else if (msg == WM_CLOSE || msg == WM_ENDSESSION 
                             || msg == WM_QUERYENDSESSION)
    {
        HWND child = (HWND)GetProp(hwnd, hookwndprop);
#ifdef DBG
        DbgPrintf("W Proc %08x hwnd:%08x msg:%d\r\n", 
                  GetCurrentProcessId(), hwnd, msg);
#endif
            if (!child)
                break;
        if (child)
            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) 
@@ -502,7 +587,13 @@ int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam)
            hwtty = htty;
        is_tty = (GetClassName(hwtty, ttybuf, sizeof(ttybuf)) 
                  && !strcmp(ttybuf, "tty"));
        if (is_tty)
#ifdef DBG
        DbgPrintf("H Proc %08x %s %08x\r\n", GetCurrentProcessId(), 
                  is_tty ? "tracking" : "ignoring", hwtty);
#endif
    }

    if (*msg == hookwndmsg && is_tty)
    {
        WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
        SetProp(hwtty, origwndprop, origproc);
@@ -512,31 +603,10 @@ int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam)
        DbgPrintf("W Proc %08x hwnd:%08x Subclassed\r\n", 
                  GetCurrentProcessId(), hwtty);
#endif
        if (*wParam == 2)
            RegisterWindows9xService(TRUE);
    }
#ifdef DBG
        DbgPrintf("H Proc %08x %s %08x\r\n", GetCurrentProcessId(), 
                  is_tty ? "tracking" : "ignoring", hwtty);
#endif
    }

    if (hc >= 0 && is_tty && *hwnd == hwtty)
    {
        if ((*msg == WM_CLOSE)
         || (*msg == WM_ENDSESSION)) {
            DWORD apppid, ttypid = GetCurrentProcessId();
            GetWindowThreadProcessId(*hwnd, &apppid);
#ifdef DBG
            DbgPrintf("H Proc %08x hwnd:%08x owned by %08x msg:%d\r\n", ttypid, *hwnd, apppid, *msg);
#endif
            //*msg = WM_NULL;
            /*
             * Experimental, return 0 or 1 will bypass the next hook and return that
             * value from the hook procedure, -1 continues to call the next hook.
             */
            return -1;
        }
    }
    return -1;
}

+17 −16

File changed.

Preview size limit exceeded, changes collapsed.