You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@locus.apache.org on 2000/12/11 17:48:29 UTC

cvs commit: apache-1.3/src/os/win32/win9xconhook Win9xConHook.c Win9xConHook.h

wrowe       00/12/11 08:48:29

  Modified:    src/os/win32 service.c
               src/os/win32/win9xconhook Win9xConHook.c Win9xConHook.h
  Log:
    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.
  
  Revision  Changes    Path
  1.33      +17 -16    apache-1.3/src/os/win32/service.c
  
  Index: service.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/os/win32/service.c,v
  retrieving revision 1.32
  retrieving revision 1.33
  diff -u -r1.32 -r1.33
  --- service.c	2000/12/11 02:21:04	1.32
  +++ service.c	2000/12/11 16:48:27	1.33
  @@ -170,7 +170,7 @@
   
   /* Console Control handler for processing Ctrl-C/Ctrl-Break and
    * on Windows NT also user logoff and system shutdown,
  - * this is not used for the WinNT or Win9x hidden service processes
  + * this also used for the Win9x hidden service and child process
    */
   static BOOL CALLBACK ap_control_handler(DWORD ctrl_type)
   {
  @@ -234,13 +234,8 @@
   
   void stop_child_monitor(void)
   {
  -    if (isWindowsNT()) {
  -        /* Nothing to unhook */
  -        return;
  -    }
  -    FixConsoleCtrlHandler(ap_control_handler, FALSE);
  -    if (!die_on_logoff)
  -        RegisterWindows9xService(FALSE);
  +    if (!isWindowsNT())
  +        FixConsoleCtrlHandler(ap_control_handler, 0);
   }
   
   /*
  @@ -252,6 +247,8 @@
    */
   void ap_start_child_console(int is_child_of_service)
   {
  +    int maxwait = 100;
  +
       /* The child is never exactly a service */
       is_service = 0;
       
  @@ -290,19 +287,23 @@
   
       FreeConsole();
       AllocConsole();
  -    while (!console_wnd)
  +    while (maxwait-- > 0) { 
           EnumWindows(EnumttyWindow, (long)(&console_wnd));
  -    ShowWindow(console_wnd, SW_HIDE);
  -    if (!die_on_logoff)
  -        RegisterWindows9xService(TRUE);    
  -    FixConsoleCtrlHandler(ap_control_handler, TRUE);
  +        if (console_wnd) {
  +            ShowWindow(console_wnd, SW_HIDE);
  +            break;
  +        }
  +        Sleep(100);
  +    }
  +    
  +    FixConsoleCtrlHandler(ap_control_handler, die_on_logoff ? 1 : 2);
   }
   
   
   void stop_console_monitor(void)
   {
       if (!isWindowsNT)
  -        FixConsoleCtrlHandler(ap_control_handler, FALSE);
  +        FixConsoleCtrlHandler(ap_control_handler, 0);
   
       /* Remove the control handler at the end of the day. */
       SetConsoleCtrlHandler(ap_control_handler, FALSE);
  @@ -343,7 +344,7 @@
       SetConsoleCtrlHandler(ap_control_handler, TRUE);
   
       if (!isWindowsNT())
  -        FixConsoleCtrlHandler(ap_control_handler, TRUE);
  +        FixConsoleCtrlHandler(ap_control_handler, die_on_logoff ? 1 : 2);
   
       atexit(stop_console_monitor);
   }
  @@ -371,7 +372,7 @@
       /* Prevent holding open the (hidden) console */
       real_exit_code = 0;
   
  -    Windows9xServiceCtrlHandler(ap_control_handler, TRUE);
  +    Windows9xServiceCtrlHandler(ap_control_handler, service_name);
   
       atexit(stop_service_monitor);
       
  
  
  
  1.3       +337 -267  apache-1.3/src/os/win32/win9xconhook/Win9xConHook.c
  
  Index: Win9xConHook.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/os/win32/win9xconhook/Win9xConHook.c,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Win9xConHook.c	2000/12/11 01:32:51	1.2
  +++ Win9xConHook.c	2000/12/11 16:48:28	1.3
  @@ -80,96 +80,235 @@
    * 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 @@
               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;
  +    }
   
  -#define gwltty_phandler 0
  -#define gwltty_ttywnd 4
  +    /* 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;
  +}
  +
  +
   /* 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 @@
           HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
           if (parent)
               RemoveProp(parent, hookwndprop);
  +        monitor_hwnd = NULL;
       }
       else if (msg == WM_CLOSE)
       {
  @@ -265,62 +456,45 @@
       return (DefWindowProc(hwnd, msg, wParam, lParam));
   }
   
  +
  +/*  The following internal helpers are invoked by the hooked tty and our app
  + */
   
  -DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
  + 
  +/*  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;
  -    }
  -
  -    while (GetMessage(&msg, NULL, 0, 0)) 
  -    {
  -        TranslateMessage(&msg);
  -        DispatchMessage(&msg);
  +    
  +    /* Find the RegisterServiceProcess function */
  +    register_service_process = (DWORD (WINAPI *)(DWORD, DWORD))
  +                     GetProcAddress(hkernel, "RegisterServiceProcess");
  +    if (register_service_process == NULL) {
  +        FreeLibrary(hkernel);
  +        return 0;
       }
  -    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;
   }
   
   
  @@ -344,153 +518,64 @@
       return TRUE;
   }
   
  -
  -/*
  - * 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 @@
               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);
  -#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;
  -        }
  +        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
  +        if (*wParam == 2)
  +            RegisterWindows9xService(TRUE);
       }
  +
       return -1;
   }
   
  
  
  
  1.3       +17 -16    apache-1.3/src/os/win32/win9xconhook/Win9xConHook.h
  
  Index: Win9xConHook.h
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/os/win32/win9xconhook/Win9xConHook.h,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Win9xConHook.h	2000/12/11 02:21:06	1.2
  +++ Win9xConHook.h	2000/12/11 16:48:29	1.3
  @@ -55,35 +55,36 @@
    *
    */
   
  -/* 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);
  -