diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/server/mpm/winnt/Win9xConHook.c')
-rw-r--r-- | rubbos/app/httpd-2.0.64/server/mpm/winnt/Win9xConHook.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/server/mpm/winnt/Win9xConHook.c b/rubbos/app/httpd-2.0.64/server/mpm/winnt/Win9xConHook.c new file mode 100644 index 00000000..a352dd1b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/server/mpm/winnt/Win9xConHook.c @@ -0,0 +1,697 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WIN32 + +/* + * Win9xConHook.dll - a hook proc to clean up Win95/98 console behavior. + * + * It is well(?) documented by Microsoft that the Win9x HandlerRoutine + * hooked by the SetConsoleCtrlHandler never receives the CTRL_CLOSE_EVENT, + * CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT signals. + * + * It is possible to have a second window to monitor the WM_ENDSESSION + * message, but the close button still fails.. + * + * There is a 16bit polling method for the close window option, but this + * is CPU intensive and requires thunking. + * + * Attempts to subclass the 'tty' console fail, since that message thread + * is actually owned by the 16 bit winoldap.mod process, although the + * window reports it is owned by the process/thread of the console app. + * + * Win9xConHook is thunks the WM_CLOSE and WM_ENDSESSION messages, + * first through a window hook procedure in the winoldap context, into + * a subclass WndProc, and on to a second hidden monitor window in the + * console application's context that dispatches them to the console app's + * registered HandlerRoutine. + */ + +/* This debugging define turns on output to COM1, although you better init + * the port first (even using hyperterm). It's the only way to catch the + * goings on within system logoff/shutdown. + * #define DBG 1 + */ + +#include <windows.h> + +/* Variables used within any process context: + * hookwndmsg is a shared message to send Win9xConHook signals + * origwndprop is a wndprop atom to store the orig wndproc of the tty + * hookwndprop is a wndprop atom to store the hwnd of the hidden child + * is_service reminds us to unmark this process on the way out + */ +static UINT hookwndmsg = 0; +static LPCTSTR origwndprop; +static LPCTSTR hookwndprop; +static BOOL is_service = 0; +//static HMODULE hmodThis = NULL; + +/* Variables used within the tty processes' context: + * is_tty flags this process; -1 == unknown, 1 == if tty, 0 == if not + * hw_tty is the handle of the top level tty in this process context + * is_subclassed is toggled to assure DllMain removes the subclass on unload + * hmodLock is there to try and prevent this dll from being unloaded if the + * hook is removed while we are subclassed + */ +static int is_tty = -1; +static HWND hwtty = NULL; +static BOOL is_subclassed = 0; + +// This simply causes a gpfault the moment it tries to FreeLibrary within +// the subclass procedure ... not good. +//static HMODULE hmodLock = NULL; + +/* Variables used within the service or console app's context: + * hmodHook is the instance handle of this module for registering the hooks + * hhkGetMessage is the hook handle for catching Posted messages + * hhkGetMessage is the hook handle for catching Sent messages + * monitor_hwnd is the invisible window that handles our tty messages + * the tty_info strucure is used to pass args into the hidden window's thread + */ +static HMODULE hmodHook = NULL; +static HHOOK hhkGetMessage; +//static HHOOK hhkCallWndProc; +static HWND monitor_hwnd = NULL; + +typedef struct { + PHANDLER_ROUTINE phandler; + HINSTANCE instance; + HWND parent; + INT type; + LPCSTR name; +} tty_info; + +/* These are the GetWindowLong offsets for the hidden window's internal info + * gwltty_phandler is the address of the app's HandlerRoutine + * gwltty_ttywnd is the tty this hidden window will handle messages from + */ +#define gwltty_phandler 0 +#define gwltty_ttywnd 4 + +/* Forward declaration prototypes for internal functions + */ +static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd); +static LRESULT WINAPI RegisterWindows9xService(BOOL set_service); +static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam); +static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty); +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam); +static int HookProc(int hc, HWND *hwnd, UINT *msg, + WPARAM *wParam, LPARAM *lParam); +#ifdef DBG +static VOID DbgPrintf(LPTSTR fmt, ...); +#endif + + +/* DllMain is invoked by every process in the entire system that is hooked + * by our window hooks, notably the tty processes' context, and by the user + * who wants tty messages (the app). Keep it light and simple. + */ +BOOL __declspec(dllexport) APIENTRY DllMain(HINSTANCE hModule, ULONG ulReason, + LPVOID pctx) +{ + if (ulReason == DLL_PROCESS_ATTACH) + { + //hmodThis = hModule; + if (!hookwndmsg) { + origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc")); + hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd")); + hookwndmsg = RegisterWindowMessage("Win9xConHookMsg"); + } +#ifdef DBG +// DbgPrintf("H ProcessAttach:%8.8x\r\n", +// GetCurrentProcessId()); +#endif + } + else if ( ulReason == DLL_PROCESS_DETACH ) + { +#ifdef DBG +// DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId()); +#endif + if (monitor_hwnd) + SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); + if (is_subclassed) + SendMessage(hwtty, hookwndmsg, 0, (LPARAM)hwtty); + if (hmodHook) + { + if (hhkGetMessage) { + UnhookWindowsHookEx(hhkGetMessage); + hhkGetMessage = NULL; + } + //if (hhkCallWndProc) { + // UnhookWindowsHookEx(hhkCallWndProc); + // hhkCallWndProc = NULL; + //} + FreeLibrary(hmodHook); + hmodHook = NULL; + } + if (is_service) + RegisterWindows9xService(FALSE); + if (hookwndmsg) { + GlobalDeleteAtom((ATOM)origwndprop); + GlobalDeleteAtom((ATOM)hookwndprop); + hookwndmsg = 0; + } + } + return TRUE; +} + + +/* This group of functions are provided for the service/console app + * to register itself a HandlerRoutine to accept tty or service messages + */ + + +/* Exported function that creates a Win9x 'service' via a hidden window, + * that notifies the process via the HandlerRoutine messages. + */ +BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler( + PHANDLER_ROUTINE phandler, + LPCSTR name) +{ + /* If we have not yet done so */ + FreeConsole(); + + if (name) + { + DWORD tid; + HANDLE hThread; + /* NOTE: this is static so the module can continue to + * access these args while we go on to other things + */ + static tty_info tty; + tty.instance = GetModuleHandle(NULL); + tty.phandler = phandler; + tty.parent = NULL; + tty.name = name; + tty.type = 2; + RegisterWindows9xService(TRUE); + hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread, + (LPVOID)&tty, 0, &tid); + if (hThread) + { + CloseHandle(hThread); + return TRUE; + } + } + else /* remove */ + { + if (monitor_hwnd) + SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); + RegisterWindows9xService(FALSE); + return TRUE; + } + return FALSE; +} + + +/* Exported function that registers a HandlerRoutine to accept missing + * Win9x CTRL_EVENTs from the tty window, as NT does without a hassle. + * If add is 1 or 2, register the handler, if 2 also mark it as a service. + * If add is 0 deregister the handler, and unmark if a service + */ +BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler( + PHANDLER_ROUTINE phandler, + INT add) +{ + HWND parent; + + if (add) + { + HANDLE hThread; + DWORD tid; + /* NOTE: this is static so the module can continue to + * access these args while we go on to other things + */ + static tty_info tty; + EnumWindows(EnumttyWindow, (LPARAM)&parent); + if (!parent) { +#ifdef DBG + DbgPrintf("A EnumttyWindow failed (%d)\r\n", GetLastError()); +#endif + return FALSE; + } + tty.instance = GetModuleHandle(NULL); + tty.phandler = phandler; + tty.parent = parent; + tty.type = add; + if (add == 2) { + tty.name = "ttyService"; + RegisterWindows9xService(TRUE); + } + else + tty.name = "ttyMonitor"; + hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread, + (LPVOID)&tty, 0, &tid); + if (!hThread) + return FALSE; + CloseHandle(hThread); + hmodHook = LoadLibrary("Win9xConHook.dll"); + if (hmodHook) + { + hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE, + (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0); + //hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, + // (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0); + } + return TRUE; + } + else /* remove */ + { + if (monitor_hwnd) { + SendMessage(monitor_hwnd, WM_DESTROY, 0, 0); + } + if (hmodHook) + { + if (hhkGetMessage) { + UnhookWindowsHookEx(hhkGetMessage); + hhkGetMessage = NULL; + } + //if (hhkCallWndProc) { + // UnhookWindowsHookEx(hhkCallWndProc); + // hhkCallWndProc = NULL; + //} + FreeLibrary(hmodHook); + hmodHook = NULL; + } + if (is_service) + RegisterWindows9xService(FALSE); + return TRUE; + } + return FALSE; +} + + +/* The following internal helpers are only used within the app's context + */ + +/* ttyConsoleCreateThread is the process that runs within the user app's + * context. It creates and pumps the messages of a hidden monitor window, + * watching for messages from the system, or the associated subclassed tty + * window. Things can happen in our context that can't be done from the + * tty's context, and visa versa, so the subclass procedure and this hidden + * window work together to make it all happen. + */ +static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty) +{ + WNDCLASS wc; + MSG msg; + wc.style = CS_GLOBALCLASS; + wc.lpfnWndProc = ttyConsoleCtrlWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 8; + wc.hInstance = NULL; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + if (((tty_info*)tty)->parent) + wc.lpszClassName = "ttyConHookChild"; + else + wc.lpszClassName = "ApacheWin95ServiceMonitor"; + + if (!RegisterClass(&wc)) { +#ifdef DBG + DbgPrintf("A proc %8.8x Error creating class %s (%d)\r\n", + GetCurrentProcessId(), wc.lpszClassName, GetLastError()); +#endif + return 0; + } + + /* Create an invisible window */ + monitor_hwnd = CreateWindow(wc.lpszClassName, ((tty_info*)tty)->name, + WS_OVERLAPPED & ~WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, + ((tty_info*)tty)->instance, tty); + + if (!monitor_hwnd) { +#ifdef DBG + DbgPrintf("A proc %8.8x Error creating window %s %s (%d)\r\n", + GetCurrentProcessId(), wc.lpszClassName, + ((tty_info*)tty)->name, GetLastError()); +#endif + return 0; + } + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* Tag again as deleted, just in case we missed WM_DESTROY */ + monitor_hwnd = NULL; + return 0; +} + + +/* This is the WndProc procedure for our invisible window. + * When our subclasssed tty window receives the WM_CLOSE, WM_ENDSESSION, + * or WM_QUERYENDSESSION messages, the message is dispatched to our hidden + * window (this message process), and we call the installed HandlerRoutine + * that was registered by the app. + */ +static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_CREATE) + { + tty_info *tty = (tty_info*)(((LPCREATESTRUCT)lParam)->lpCreateParams); + SetWindowLong(hwnd, gwltty_phandler, (LONG)tty->phandler); + SetWindowLong(hwnd, gwltty_ttywnd, (LONG)tty->parent); +#ifdef DBG + DbgPrintf("A proc %8.8x created %8.8x %s for tty wnd %8.8x\r\n", + GetCurrentProcessId(), hwnd, + tty->name, tty->parent); +#endif + if (tty->parent) { + SetProp(tty->parent, hookwndprop, hwnd); + PostMessage(tty->parent, hookwndmsg, + tty->type, (LPARAM)tty->parent); + } + return 0; + } + else if (msg == WM_DESTROY) + { + HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd); +#ifdef DBG + DbgPrintf("A proc %8.8x destroyed %8.8x ttyConHookChild\r\n", + GetCurrentProcessId(), hwnd); +#endif + if (parent) { + RemoveProp(parent, hookwndprop); + SendMessage(parent, hookwndmsg, 0, (LPARAM)parent); + } + monitor_hwnd = NULL; + } + else if (msg == WM_CLOSE) + { + PHANDLER_ROUTINE phandler = + (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); + LRESULT rv = phandler(CTRL_CLOSE_EVENT); +#ifdef DBG + DbgPrintf("A proc %8.8x invoked CTRL_CLOSE_EVENT " + "returning %d\r\n", + GetCurrentProcessId(), rv); +#endif + if (rv) + return !rv; + } + else if ((msg == WM_QUERYENDSESSION) || (msg == WM_ENDSESSION)) + { + if (lParam & ENDSESSION_LOGOFF) + { + PHANDLER_ROUTINE phandler = + (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); + LRESULT rv = phandler(CTRL_LOGOFF_EVENT); +#ifdef DBG + DbgPrintf("A proc %8.8x invoked CTRL_LOGOFF_EVENT " + "returning %d\r\n", + GetCurrentProcessId(), rv); +#endif + if (rv) + return ((msg == WM_QUERYENDSESSION) ? rv : !rv); + } + else + { + PHANDLER_ROUTINE phandler = + (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler); + LRESULT rv = phandler(CTRL_SHUTDOWN_EVENT); +#ifdef DBG + DbgPrintf("A proc %8.8x invoked CTRL_SHUTDOWN_EVENT " + "returning %d\r\n", GetCurrentProcessId(), rv); +#endif + if (rv) + return ((msg == WM_QUERYENDSESSION) ? rv : !rv); + } + } + return (DefWindowProc(hwnd, msg, wParam, lParam)); +} + + +/* The following internal helpers are invoked by the hooked tty and our app + */ + + +/* Register or deregister the current process as a Windows9x style service. + * Experience shows this call is ignored across processes, so the second + * arg to RegisterServiceProcess (process group id) is effectively useless. + */ +static LRESULT WINAPI RegisterWindows9xService(BOOL set_service) +{ + static HINSTANCE hkernel; + static DWORD (WINAPI *register_service_process)(DWORD, DWORD) = NULL; + BOOL rv; + + if (set_service == is_service) + return 1; + +#ifdef DBG + DbgPrintf("R %s proc %8.8x as a service\r\n", + set_service ? "installing" : "removing", + GetCurrentProcessId()); +#endif + + if (!register_service_process) + { + /* Obtain a handle to the kernel library */ + hkernel = LoadLibrary("KERNEL32.DLL"); + if (!hkernel) + return 0; + + /* Find the RegisterServiceProcess function */ + register_service_process = (DWORD (WINAPI *)(DWORD, DWORD)) + GetProcAddress(hkernel, "RegisterServiceProcess"); + if (register_service_process == NULL) { + FreeLibrary(hkernel); + return 0; + } + } + + /* Register this process as a service */ + rv = register_service_process(0, set_service != FALSE); + if (rv) + is_service = set_service; + + if (!is_service) + { + /* Unload the kernel library */ + FreeLibrary(hkernel); + register_service_process = NULL; + } + return rv; +} + + +/* + * This function only works when this process is the active process + * (e.g. once it is running a child process, it can no longer determine + * which console window is its own.) + */ +static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) +{ + char tmp[8]; + if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty")) + { + DWORD wndproc, thisproc = GetCurrentProcessId(); + GetWindowThreadProcessId(wnd, &wndproc); + if (wndproc == thisproc) { + *((HWND*)retwnd) = wnd; + return FALSE; + } + } + return TRUE; +} + + +/* The remaining code all executes --in the tty's own process context-- + * + * That means special attention must be paid to what it's doing... + */ + +/* Subclass message process for the tty window + * + * This code -handles- WM_CLOSE, WM_ENDSESSION and WM_QUERYENDSESSION + * by dispatching them to the window identified by the hookwndprop + * property atom set against our window. Messages are then dispatched + * to origwndprop property atom we set against the window when we + * injected this subclass. This trick did not work with simply a hook. + */ +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop); + if (!origproc) + return 0; + + if (msg == WM_NCDESTROY + || (msg == hookwndmsg && !LOWORD(wParam) && (HWND)lParam == hwnd)) + { + if (is_subclassed) { +#ifdef DBG + DbgPrintf("W proc %08x hwnd:%08x Subclass removed\r\n", + GetCurrentProcessId(), hwnd); +#endif + if (is_service) + RegisterWindows9xService(FALSE); + SetWindowLong(hwnd, GWL_WNDPROC, (LONG)origproc); + RemoveProp(hwnd, origwndprop); + RemoveProp(hwnd, hookwndprop); + is_subclassed = FALSE; + //if (hmodLock) + // FreeLibrary(hmodLock); + //hmodLock = NULL; + } + } + else if (msg == WM_CLOSE || msg == WM_ENDSESSION + || msg == WM_QUERYENDSESSION) + { + HWND child = (HWND)GetProp(hwnd, hookwndprop); + if (child) { +#ifdef DBG + DbgPrintf("W proc %08x hwnd:%08x forwarded msg:%d\r\n", + GetCurrentProcessId(), hwnd, msg); +#endif + return SendMessage(child, msg, wParam, lParam); + } + } + return CallWindowProc(origproc, hwnd, msg, wParam, lParam); +} + + +/* HookProc, once installed, is responsible for subclassing the system + * tty windows. It generally does nothing special itself, since + * research indicates that it cannot deal well with the messages we are + * interested in, that is, WM_CLOSE, WM_QUERYSHUTDOWN and WM_SHUTDOWN + * of the tty process. + * + * Respond and subclass only when a WM_NULL is received by the window. + */ +int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam) +{ + if (is_tty == -1 && *hwnd) + { + char ttybuf[8]; + HWND htty; + hwtty = *hwnd; + while (htty = GetParent(hwtty)) + hwtty = htty; + is_tty = (GetClassName(hwtty, ttybuf, sizeof(ttybuf)) + && !strcmp(ttybuf, "tty")); +#ifdef DBG + if (is_tty) + DbgPrintf("H proc %08x tracking hwnd %08x\r\n", + GetCurrentProcessId(), hwtty); +#endif + } + + if (*msg == hookwndmsg && *wParam && *lParam == (LPARAM)hwtty && is_tty) + { + WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC); + //char myname[MAX_PATH]; + //if (GetModuleFileName(hmodThis, myname, sizeof(myname))) + // hmodLock = LoadLibrary(myname); + SetProp(hwtty, origwndprop, origproc); + SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc); + is_subclassed = TRUE; +#ifdef DBG + DbgPrintf("H proc %08x hwnd:%08x Subclassed\r\n", + GetCurrentProcessId(), hwtty); +#endif + if (LOWORD(*wParam) == 2) + RegisterWindows9xService(TRUE); + } + + return -1; +} + + +/* + * PostMessage Hook: + */ +LRESULT __declspec(dllexport) CALLBACK GetMsgProc(INT hc, WPARAM wParam, + LPARAM lParam) +{ + PMSG pmsg; + + pmsg = (PMSG)lParam; + + if (pmsg) { + int rv = HookProc(hc, &pmsg->hwnd, &pmsg->message, + &pmsg->wParam, &pmsg->lParam); + if (rv != -1) + return rv; + } + /* + * CallNextHookEx apparently ignores the hhook argument, so pass NULL + */ + return CallNextHookEx(NULL, hc, wParam, lParam); +} + + +/* + * SendMessage Hook: + */ +LRESULT __declspec(dllexport) CALLBACK CallWndProc(INT hc, WPARAM wParam, + LPARAM lParam) +{ + PCWPSTRUCT pcwps = (PCWPSTRUCT)lParam; + + if (pcwps) { + int rv = HookProc(hc, &pcwps->hwnd, &pcwps->message, + &pcwps->wParam, &pcwps->lParam); + if (rv != -1) + return rv; + } + /* + * CallNextHookEx apparently ignores the hhook argument, so pass NULL + */ + return CallNextHookEx(NULL, hc, wParam, lParam); +} + + +#ifdef DBG +VOID DbgPrintf( + LPTSTR fmt, + ... + ) +{ + static HANDLE mutex; + va_list marker; + TCHAR szBuf[256]; + DWORD t; + HANDLE gDbgOut; + + va_start(marker, fmt); + wvsprintf(szBuf, fmt, marker); + va_end(marker); + + if (!mutex) + mutex = CreateMutex(NULL, FALSE, "Win9xConHookDbgOut"); + WaitForSingleObject(mutex, INFINITE); + gDbgOut = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); + WriteFile(gDbgOut, szBuf, strlen(szBuf), &t, NULL); + CloseHandle(gDbgOut); + ReleaseMutex(mutex); +} +#endif + +#endif /* WIN32 */ |