]> granicus.if.org Git - apache/commitdiff
So close we can taste it, this patch adds necessary delay timers for
authorWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 11 Dec 2000 16:48:29 +0000 (16:48 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Mon, 11 Dec 2000 16:48:29 +0000 (16:48 +0000)
  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

server/mpm/winnt/Win9xConHook.c
server/mpm/winnt/Win9xConHook.h

index 70c738a0dc6a1fb8fd5982e01b038fb0a324adf9..4cc4e442ea50870a09bb2d3ca5b6d7207d2a9693 100644 (file)
  * 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;
             }
+        }
+        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;
+        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) {
-            HWND child = (HWND)GetProp(parent, hookwndprop);
-            if (child)
-                SendMessage(child, WM_DESTROY, 0, 0);
+        if (!parent)
+            return FALSE;
+        tty.instance = GetModuleHandle(NULL);
+        tty.phandler = phandler;
+        tty.parent = parent;
+        if (add == 2) {
+            tty.name = "ttyService";
+            RegisterWindows9xService(TRUE);
+        }
+        else 
+            tty.name = "ttyMonitor";
+        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);
+        }        
+        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
+                               (LPVOID)&tty, 0, &tid);
+        if (hThread)
+        {
+            CloseHandle(hThread);
+            /* All is set ... wake up the tty window */
+            PostMessage(parent, hookwndmsg, add, 0);
+            return TRUE;
+        }
+    }
+    else /* remove */
+    {
+        if (monitor_hwnd) {
+            parent = (HWND) GetWindowLong(monitor_hwnd, gwltty_ttywnd);
+            if (parent)
+                PostMessage(parent, hookwndmsg, add, 0);
+            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
         }
         if (hmodHook)
         {
@@ -184,32 +323,83 @@ BOOL __declspec(dllexport) APIENTRY DllMain(PVOID hModule, ULONG ulReason, PCONT
             FreeLibrary(hmodHook);
             hmodHook = NULL;
         }
-        GlobalDeleteAtom((ATOM)origwndprop);
-        GlobalDeleteAtom((ATOM)hookwndprop);
+        if (is_service)
+            RegisterWindows9xService(FALSE);
+        return TRUE;
     }
-    return TRUE;
+    return FALSE;
 }
 
 
-typedef struct {
-    PHANDLER_ROUTINE phandler;
-    HINSTANCE instance;
-    HWND parent;
-    char *name;
-} tty_info;
+/*  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("S Created ttyConHookChild class\r\n");
+#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("S Error Creating ttyConHookChild:%d\r\n", 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;
+}
 
-#define gwltty_phandler 0
-#define gwltty_ttywnd 4
 
 /* 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.
+ * 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.
  */
-LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     if (msg == WM_CREATE)
     {
@@ -228,6 +418,7 @@ LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARA
         HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
         if (parent)
             RemoveProp(parent, hookwndprop);
+        monitor_hwnd = NULL;
     }
     else if (msg == WM_CLOSE)
     {
@@ -266,61 +457,44 @@ LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARA
 }
 
 
-DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
+/*  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)
 {
-    /* 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.
-     */
-    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("S Created ttyConHookChild class\r\n");
-#endif
-        return 0;
-    }
+    HINSTANCE hkernel;
+    DWORD (WINAPI *register_service_process)(DWORD, DWORD);
+    BOOL rv;
 
-    /* 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 (set_service == is_service)
+        return 1;
 
-    if (!monitor_hwnd) {
-#ifdef DBG
-        DbgPrintf("S Error Creating ttyConHookChild:%d\r\n", GetLastError());
-#endif
+    /* 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;
     }
-
-    while (GetMessage(&msg, NULL, 0, 0)) 
-    {
-        TranslateMessage(&msg);
-        DispatchMessage(&msg);
-    }
-    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;
 }
 
 
@@ -345,152 +519,63 @@ static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd)
 }
 
 
-/*
- * Exported function that sets up the fixup child window 
- */
-BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler(
-        PHANDLER_ROUTINE phandler,
-        BOOL add)
-{
-    /* If we have not yet done so */
-    FreeConsole();
-
-    if (add)
-    {
-        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)
-        {
-            CloseHandle(hThread);
-            return TRUE;
-        }
-    }
-    else /* remove */
-    {
-        HWND child = FindWindowEx(NULL, NULL, "ttyConHookChild", NULL);
-        if (monitor_hwnd)
-            SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
-        RegisterWindows9xService(FALSE);
-        return TRUE;
-    }
-    return FALSE;
-}
-
-
-/*
- * Exported function that sets up the fixup child window and dispatch
+/* The remaining code all executes --in the tty's own process context--
+ *
+ * That means special attention must be paid to what it's doing...
  */
-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)
-        {
-            hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
-                (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0);
-            hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
-                (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
-        }
-        
-        hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
-                               (LPVOID)&tty, 0, &tid);
-        if (hThread)
-        {
-            CloseHandle(hThread);
-            return TRUE;
-        }
-    }
-    else /* remove */
-    {
-        HWND child = FindWindowEx(parent, NULL, "ttyConHookChild", NULL);
-        if (child)
-            SendMessage(child, WM_DESTROY, 0, 0);
-        if (hmodHook)
-        {
-            if (hhkGetMessage) {
-                UnhookWindowsHookEx(hhkGetMessage);
-                hhkGetMessage = NULL;
-            }
-            if (hhkCallWndProc) {
-                UnhookWindowsHookEx(hhkCallWndProc);
-                hhkCallWndProc = NULL;
-            }
-            FreeLibrary(hmodHook);
-            hmodHook = NULL;
-        }
-        return TRUE;
-    }
-    return FALSE;
-}
-
-
-/*
- * Subclass message process for the tty window
+/* 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);
+        DbgPrintf("W Proc %08x hwnd:%08x Subclass removed\r\n", 
+                  GetCurrentProcessId(), hwnd);
 #endif
-            is_subclassed = FALSE;
-            RegisterWindows9xService(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:
-        {
-            HWND child = (HWND)GetProp(hwnd, hookwndprop);
+            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);
+        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,41 +587,26 @@ 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)
-        {
-            WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
-            SetProp(hwtty, origwndprop, origproc);
-            SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc);
-            is_subclassed = TRUE;
-#ifdef DBG
-            DbgPrintf("W Proc %08x hwnd:%08x Subclassed\r\n", 
-                      GetCurrentProcessId(), hwtty);
-#endif
-            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 == hookwndmsg && is_tty)
     {
-        if ((*msg == WM_CLOSE)
-         || (*msg == WM_ENDSESSION)) {
-            DWORD apppid, ttypid = GetCurrentProcessId();
-            GetWindowThreadProcessId(*hwnd, &apppid);
+        WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
+        SetProp(hwtty, origwndprop, origproc);
+        SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc);
+        is_subclassed = TRUE;
 #ifdef DBG
-            DbgPrintf("H Proc %08x hwnd:%08x owned by %08x msg:%d\r\n", ttypid, *hwnd, apppid, *msg);
+        DbgPrintf("W Proc %08x hwnd:%08x Subclassed\r\n", 
+                  GetCurrentProcessId(), hwtty);
 #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;
-        }
+        if (*wParam == 2)
+            RegisterWindows9xService(TRUE);
     }
+
     return -1;
 }
 
index 620d3f39d2487e56a98919c5357b1758c23dfa1a..19470756731d4beac3bf31a0fe266c434e8e87ad 100644 (file)
  *
  */
 
-/* You should (generally) not need this function, as the full
- * Windows9xServiceCtrlHandler will register the application as a
- * service, allowing it to survive user logoff.
- */
-LRESULT WINAPI RegisterWindows9xService(BOOL is_service);
 
-/* Windows9xServiceCtrlHandler registers a handler routine, freeing
- * the console window, creating a hidden window and passing the 
- * WM_SHUTDOWN message through the CTRL_SHUTDOWN event.
+/* Windows9xServiceCtrlHandler registers a handler routine, frees the
+ * console window, and registers this process as a service in Win9x.
+ * It creats a hidden window of class "ApacheWin95ServiceMonitor"
+ * and titled by the name passed, which passes the WM_SHUTDOWN message 
+ * through the given HandlerRoutine's CTRL_SHUTDOWN event.
+ * Call with name of NULL to remove the Service handler.
  */
-BOOL WINAPI Windows9xServiceCtrlHandler(PHANDLER_ROUTINE phandler, BOOL add);
+BOOL WINAPI Windows9xServiceCtrlHandler(PHANDLER_ROUTINE phandler, LPCSTR name);
+
 
 /* FixConsoleControlHandler registers a handler routine with the
  * Win9xConHook.dll, creating a hidden window and forwarding the
- * WM_ENDSESSION and WM_CLOSE messages to the registered handler
+ * WM_ENDSESSION and WM_CLOSE messages to the given HandlerRoutine
  * as CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT and CTRL_CLOSE_EVENT. 
  * The application should still use SetConsoleCtrlHandler to grab
- * the CTRL_BREAK_EVENT and CTRL_C_EVENT.
+ * the CTRL_BREAK_EVENT and CTRL_C_EVENT, if desired.
  */
 BOOL WINAPI FixConsoleCtrlHandler(PHANDLER_ROUTINE phandler, BOOL add);
 
+
 /*
- * PostMessage Hook, don't use this directly:
+ * Exported PostMessage Hook, never use this directly:
+ *
+ * LRESULT CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam);
  */
-LRESULT CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam);
 
 
 /*
- * SendMessage Hook, don't use this directly:
+ * Exported SendMessage Hook, never use this directly:
+ *
+ * LRESULT CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam);
  */
-LRESULT CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam);
-