#ifdef WIN32
#include <windows.h>
static UINT hookwndmsg = 0;
static LPCTSTR origwndprop;
static LPCTSTR hookwndprop;
static BOOL is_service = 0;
static int is_tty = -1;
static HWND hwtty = NULL;
static BOOL is_subclassed = 0;
static HMODULE hmodHook = NULL;
static HHOOK hhkGetMessage;
static HWND monitor_hwnd = NULL;
typedef struct {
PHANDLER_ROUTINE phandler;
HINSTANCE instance;
HWND parent;
INT type;
LPCSTR name;
} tty_info;
#define gwltty_phandler 0
#define gwltty_ttywnd 4
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
BOOL __declspec(dllexport) APIENTRY DllMain(HINSTANCE hModule, ULONG ulReason,
LPVOID pctx)
{
if (ulReason == DLL_PROCESS_ATTACH)
{
if (!hookwndmsg) {
origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc"));
hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd"));
hookwndmsg = RegisterWindowMessage("Win9xConHookMsg");
}
#ifdef DBG
#endif
}
else if ( ulReason == DLL_PROCESS_DETACH )
{
#ifdef DBG
#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;
}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
if (is_service)
RegisterWindows9xService(FALSE);
if (hookwndmsg) {
GlobalDeleteAtom((ATOM)origwndprop);
GlobalDeleteAtom((ATOM)hookwndprop);
hookwndmsg = 0;
}
}
return TRUE;
}
BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler(
PHANDLER_ROUTINE phandler,
LPCSTR name)
{
FreeConsole();
if (name)
{
DWORD tid;
HANDLE hThread;
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
{
if (monitor_hwnd)
SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
RegisterWindows9xService(FALSE);
return TRUE;
}
return FALSE;
}
BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler(
PHANDLER_ROUTINE phandler,
INT add)
{
HWND parent;
if (add)
{
HANDLE hThread;
DWORD tid;
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);
}
return TRUE;
}
else
{
if (monitor_hwnd) {
SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
}
if (hmodHook)
{
if (hhkGetMessage) {
UnhookWindowsHookEx(hhkGetMessage);
hhkGetMessage = NULL;
}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
if (is_service)
RegisterWindows9xService(FALSE);
return TRUE;
}
return FALSE;
}
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;
}
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);
}
monitor_hwnd = NULL;
return 0;
}
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));
}
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)
{
hkernel = LoadLibrary("KERNEL32.DLL");
if (!hkernel)
return 0;
register_service_process = (DWORD (WINAPI *)(DWORD, DWORD))
GetProcAddress(hkernel, "RegisterServiceProcess");
if (register_service_process == NULL) {
FreeLibrary(hkernel);
return 0;
}
}
rv = register_service_process(0, set_service != FALSE);
if (rv)
is_service = set_service;
if (!is_service)
{
FreeLibrary(hkernel);
register_service_process = NULL;
}
return rv;
}
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;
}
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;
}
}
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);
}
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);
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;
}
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;
}
return CallNextHookEx(NULL, hc, wParam, lParam);
}
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;
}
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