📄 win9xconhook.c
字号:
/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -