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/11/30 02:39:39 UTC
cvs commit: apache-1.3/src/os/win32/win9xconhook Win9xConHook.def Win9xConHook.h Win9xConHook.c Win9xConHook.dsp
wrowe 00/11/29 17:39:33
Added: src/os/win32/win9xconhook Win9xConHook.def Win9xConHook.h
Win9xConHook.c Win9xConHook.dsp
Log:
This is the Win9x console hook handler ... much code to be pulled now
from the src/os/win32/services.c for Win9x.
Revision Changes Path
1.1 apache-1.3/src/os/win32/win9xconhook/Win9xConHook.def
Index: Win9xConHook.def
===================================================================
LIBRARY Win9xConHook
EXETYPE WINDOWS
EXPORTS
DllMain
GetMsgProc
CallWndProc
FixConsoleCtrlHandler
1.1 apache-1.3/src/os/win32/win9xconhook/Win9xConHook.h
Index: Win9xConHook.h
===================================================================
/*
* FixConsoleControlHandler will register a handler routine with the
* Win9xConHook.dll, creating a hidden window and forwarding the
* WM_ENDSESSION and WM_CLOSE messages to the registered handler
* as CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT and CTRL_CLOSE_EVENT.
*/
BOOL WINAPI FixConsoleCtrlHandler(
PHANDLER_ROUTINE phandler,
BOOL add);
/*
* PostMessage Hook:
*/
LRESULT CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam);
/*
* SendMessage Hook:
*/
LRESULT CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam);
1.1 apache-1.3/src/os/win32/win9xconhook/Win9xConHook.c
Index: Win9xConHook.c
===================================================================
/* ====================================================================
* Copyright (c) 1995-2000 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://httpd.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://httpd.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/*
* 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.
*/
#define DBG 1
#include <windows.h>
/*
* 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
*/
static int is_tty = -1;
static HWND hwtty = NULL;
static BOOL is_subclassed = 0;
static HMODULE hmodHook = NULL;
static HHOOK hhkGetMessage;
//static HHOOK hhkCallWndProc;
static LPCTSTR origwndprop = NULL;
static LPCTSTR hookwndprop = NULL;
#ifdef DBG
static VOID DbgPrintf(LPTSTR fmt, ...);
#endif
static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd);
BOOL __declspec(dllexport) APIENTRY DllMain(PVOID hModule, ULONG ulReason, PCONTEXT pctx)
{
if (ulReason == DLL_PROCESS_ATTACH)
{
#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) {
WNDPROC origproc = (WNDPROC) GetProp(hwtty, origwndprop);
if (origproc) {
SetWindowLong(hwtty, GWL_WNDPROC, (LONG)origproc);
RemoveProp(hwtty, origwndprop);
}
}
EnumWindows(EnumttyWindow, (LPARAM)&parent);
if (parent) {
HWND child = (HWND)GetProp(parent, hookwndprop);
if (child)
SendMessage(child, WM_DESTROY, 0, 0);
}
if (hmodHook)
{
if (hhkGetMessage) {
UnhookWindowsHookEx(hhkGetMessage);
hhkGetMessage = NULL;
}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
GlobalDeleteAtom((ATOM)origwndprop);
GlobalDeleteAtom((ATOM)hookwndprop);
}
return TRUE;
}
typedef struct {
PHANDLER_ROUTINE phandler;
HINSTANCE instance;
HWND parent;
} tty_info;
#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.
*/
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("S Created ttyConHookChild:%8.8x\r\n", hwnd);
#endif
SetProp(((tty_info*)tty)->parent, hookwndprop, hwnd);
return 0;
}
else if (msg == WM_DESTROY)
{
HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
RemoveProp(parent, hookwndprop);
}
else if (msg == WM_CLOSE)
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
DbgPrintf("S Invoking CTRL_CLOSE_EVENT:%8.8x\r\n",
GetCurrentProcessId());
#endif
return !phandler(CTRL_CLOSE_EVENT);
}
else if (msg == WM_QUERYENDSESSION)
{
if (lParam & ENDSESSION_LOGOFF)
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
DbgPrintf("S Invoking CTRL_LOGOFF_EVENT:%8.8x\r\n",
GetCurrentProcessId());
#endif
return !phandler(CTRL_LOGOFF_EVENT);
}
else
{
PHANDLER_ROUTINE phandler =
(PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
#ifdef DBG
DbgPrintf("S Invoking CTRL_SHUTDOWN_EVENT:%8.8x\r\n",
GetCurrentProcessId());
#endif
return !phandler(CTRL_SHUTDOWN_EVENT);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
{
/* 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;
HWND hwnd;
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;
wc.lpszClassName = "ttyConHookChild";
if (!RegisterClass(&wc)) {
#ifdef DBG
DbgPrintf("S Created ttyConHookChild class\r\n");
#endif
return 0;
}
/* Create an invisible window */
hwnd = CreateWindow(wc.lpszClassName, "",
WS_OVERLAPPED & ~WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
((tty_info*)tty)->instance, tty);
if (!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);
if (msg.message == WM_DESTROY)
DestroyWindow(hwnd);
}
return 0;
}
/*
* 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[4];
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;
}
/*
* Exported function that sets up the fixup child window and dispatch
*/
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.phandler = phandler;
tty.parent = parent;
tty.instance = GetModuleHandle(NULL);
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;
}
FreeLibrary(hmodHook);
hmodHook = NULL;
}
return TRUE;
}
return FALSE;
}
/*
* Subclass message process for the tty window
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop);
if (!origproc)
return 0;
switch (msg)
{
case WM_NCDESTROY:
#ifdef DBG
DbgPrintf("W Proc %08x hwnd:%08x Subclass removed\r\n",
GetCurrentProcessId(), hwnd);
#endif
is_subclassed = 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);
#ifdef DBG
DbgPrintf("W Proc %08x hwnd:%08x msg:%d\r\n",
GetCurrentProcessId(), hwnd, msg);
#endif
if (!child)
break;
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[4];
HWND htty;
hwtty = *hwnd;
while (htty = GetParent(hwtty))
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
}
#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 == 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;
}
}
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->lParam, &pmsg->wParam);
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,
...
)
{
va_list marker;
TCHAR szBuf[256];
DWORD t;
HANDLE gDbgOut;
va_start(marker, fmt);
wvsprintf(szBuf, fmt, marker);
va_end(marker);
gDbgOut = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
WriteFile(gDbgOut, szBuf, strlen(szBuf), &t, NULL);
CloseHandle(gDbgOut);
}
#endif
1.1 apache-1.3/src/os/win32/win9xconhook/Win9xConHook.dsp
Index: Win9xConHook.dsp
===================================================================
# Microsoft Developer Studio Project File - Name="Win9xConHook" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=Win9xConHook - Win32 Release
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "Win9xConHook.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "Win9xConHook.mak" CFG="Win9xConHook - Win32 Release"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "Win9xConHook - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "Win9xConHook - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "Win9xConHook - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir ".\Release"
# PROP BASE Intermediate_Dir ".\Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Win9xConHookR"
# PROP Intermediate_Dir "Win9xConHookR"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /c
# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /base:@"BaseAddr.ref",mod_status
# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x1c0f0000" /subsystem:windows /dll /map /machine:I386
!ELSEIF "$(CFG)" == "Win9xConHook - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir ".\Debug"
# PROP BASE Intermediate_Dir ".\Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Win9xConHookD"
# PROP Intermediate_Dir "Win9xConHookD"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /base:@"BaseAddr.ref",mod_status
# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x1c0f0000" /subsystem:windows /dll /incremental:no /map /debug /machine:I386
!ENDIF
# Begin Target
# Name "Win9xConHook - Win32 Release"
# Name "Win9xConHook - Win32 Debug"
# Begin Source File
SOURCE=.\Win9xConHook.c
# End Source File
# Begin Source File
SOURCE=.\Win9xConHook.def
# End Source File
# Begin Source File
SOURCE=.\Win9xConHook.h
# End Source File
# End Target
# End Project