diff --git a/include/xvtility.cpp b/include/xvtility.cpp index d3f804ea3..e032f44ad 100755 --- a/include/xvtility.cpp +++ b/include/xvtility.cpp @@ -6,6 +6,17 @@ #include #include +#if XVT_OS == XVT_OS_WIN +#include +#include +#include +#pragma hdrstop +#include "prochook.h" +#define MAX_PATH 260 // This really should be in WINDOWS.H, but is + // inexplicably hard-coded into the data structures +int MungeModuleHeader( HINSTANCE hInstance, BOOL fMunge ); +#endif + #if XVT_OS == XVT_OS_SCOUNIX extern "C" { long nap(long period); } #endif @@ -55,56 +66,156 @@ BOOLEAN CAMPI_SCAVATI = FALSE; const word WM_WAKEUP = RegisterWindowMessage("WAKEUP"); // By Matt Pietrek -bool allow_another_instance() +//######################################################################## +// Code that does the real work +//######################################################################## + +// +// Central function that modifies a module table to trick the loader +// into letting a second instance of a multiple data segment program run. +// +int MungeModuleHeader( HINSTANCE hInstance, BOOL fMunge ) { - // Convert the HINSTANCE to an HMODULE - HINSTANCE hInstance = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE); - LPCSTR sModuleName = (LPCSTR)MAKELP(0, hInstance); - HMODULE hModule = GetModuleHandle(sModuleName); - void FAR* mem_handle = GlobalLock(hModule); - HMODULE hModuleSel = (HMODULE)SELECTOROF(mem_handle); - if (hModuleSel == 0) // Make sure we succeeded. - return FALSE; - - // Make pointers to the resident names table and the OFSTRUCT - LPSTR moduleName = (LPSTR)MAKELP( hModuleSel, *(WORD FAR *)MAKELP(hModuleSel, 0x26)); - LPSTR fileName = (LPSTR)MAKELP( hModuleSel, *(WORD FAR *)MAKELP(hModuleSel, 0x0A)); - - // Get the module name length, and advance to the actual string - BYTE cbModuleName = *moduleName++; // First byte is a length byte - - // Convert the first uppercase letter of the modulename to lowercase - while ( cbModuleName ) - { - if ( isupper(*moduleName) ) - { - *moduleName = tolower(*moduleName); break; - } - cbModuleName--; moduleName++; - } - - if ( cbModuleName == 0 ) // Make sure we succeeded - return FALSE; - - // Position to the end of the filename. First byte is a length byte - fileName += *fileName - 1; - - // Find the first uppercase letter in the filename. Convert to lowercase - while ( TRUE ) - { - // Stop when we come to a directory separator or colon - if ( (*fileName=='\\') || (*fileName=='/') || (*fileName==':') ) - return FALSE; + HMODULE hModuleSel; + LPSTR lpszModName, lpszFileName; + BYTE cbModuleName; + static BOOL fResidentNamesMunged = FALSE; - if ( isupper(*fileName) ) + hModuleSel = SELECTOROF( // Convert the HINSTANCE to an HMODULE + GlobalLock(GetModuleHandle((LPSTR)MAKELP(0,hInstance)))); + + if ( hModuleSel == 0 ) // Make sure we succeeded. + return 0; + + // + // First, we'll take care of the resident names table + // + if ( FALSE == fResidentNamesMunged ) { - *fileName = tolower(*fileName); break; + // Make pointers to the module name in the resident names table + lpszModName = (LPSTR)MAKELP(hModuleSel, + *(WORD FAR *)MAKELP(hModuleSel, 0x26) ); + + // Get the module name length, and advance to the actual string + cbModuleName = *lpszModName++; // First byte is a length byte + + // Convert the first uppercase letter of the modulename to lowercase + while ( cbModuleName ) + { + if ( isupper(*lpszModName) ) + { + *lpszModName = tolower(*lpszModName); break; + } + cbModuleName--; lpszModName++; + } + + if ( cbModuleName == 0 ) // Make sure we succeeded + return 0; + + // Remember that we've done this, so that we don't bother doing + // it in the future. + fResidentNamesMunged = TRUE; } - fileName--; - } + + // + // Now, we'll turn our attention to the module file name in the OFSTRUCT + // + lpszFileName = (LPSTR)MAKELP(hModuleSel, + *(WORD FAR *)MAKELP(hModuleSel, 0x0A)); + + // Position to the end of the filename. First byte is a length byte + lpszFileName += *lpszFileName - 1; + + // If we're munging, added 0x30 to the last character value, otherwise + // subtract 0x30. 0x30 is chosen completely at random. + if ( fMunge ) + *lpszFileName += 0x30; + else + *lpszFileName -= 0x30; + return 1; +} + +//######################################################################## +// This section watches calls to LoadModule and munges the EXE's module +// database as needed. +//######################################################################## + +HIDDEN NPHOOKCHILD npHookLoadModule = 0; +HIDDEN char szOurFileName[MAX_PATH]; +HIDDEN HINSTANCE HInstance; + +HINSTANCE +WINAPI +__export MultInst95LoadModule( LPCSTR lpszModuleName, + LPVOID lpvParameterBlock ) +{ + HINSTANCE retValue; + + // Uppercase the name of the module name that was passed to LoadModule + char szNewFileName[MAX_PATH]; + lstrcpy( szNewFileName, lpszModuleName ); + strupr( szNewFileName ); + + // Compare the incoming filename to our EXE's module name. If they + // don't match, we don't need to bother munging the module database + BOOL fSecondInstance = strstr(szOurFileName, szNewFileName) ? TRUE:FALSE; + + // Unhook our LoadModule hook so that we can call the real LoadModule + ProcUnhook( npHookLoadModule ); + + // Munge module database if needed + if ( fSecondInstance ) + MungeModuleHeader( HInstance, TRUE ); + + // Call the original LoadModule code + retValue = LoadModule( lpszModuleName, lpvParameterBlock ); + + // Unmunge module database if needed + if ( fSecondInstance ) + MungeModuleHeader( HInstance, FALSE ); + + // Reinstall our LoadModule hook so that we see future loads + ProcHook( npHookLoadModule ); + + return retValue; +} + +BOOL deny_another_instance() +{ + if ( !npHookLoadModule ) + return FALSE; + + SetProcRelease( npHookLoadModule ); + npHookLoadModule = 0; + return TRUE; } +BOOL allow_another_instance() +{ + + if ( npHookLoadModule ) + return TRUE; + + // Get the EXE's filename into a global string variable and uppercase it + GetModuleFileName( HInstance, szOurFileName, sizeof(szOurFileName) ); + strupr( szOurFileName ); + + // Create a MakeProcInstance thunk so that our callback function + // will always be using the correct DS selector + FARPROC lpfnMPI + = MakeProcInstance( (FARPROC)MultInst95LoadModule, HInstance ); + + if ( !lpfnMPI ) + return FALSE; + + // Call PROCHOOK.DLL to hook calls to LoadModule + npHookLoadModule = SetProcAddress( (FARPROC)LoadModule, + lpfnMPI, FALSE ); + + return (BOOL)npHookLoadModule; +} + static BOOLEAN event_hook(HWND hwnd, UINT msg, UINT wparam, @@ -122,9 +233,10 @@ static BOOLEAN event_hook(HWND hwnd, WINDOW win = cur_win(); if (win != NULL_WIN) { - TWindow* w = (TWindow*)xvt_vobj_get_data(win); +// TWindow* w = (TWindow*)xvt_vobj_get_data(win); const KEY key = toupper(wparam)+K_CTRL; - w->on_key(key); +// w->on_key(key); + dispatch_e_char(win, key); } } break; @@ -502,11 +614,10 @@ void customize_controls( #if XVT_OS == XVT_OS_WIN xvt_vobj_set_attr(NULL_WIN,ATTR_EVENT_HOOK, (long)event_hook); xvt_vobj_set_attr(NULL_WIN,ATTR_ERRMSG_HANDLER, (long)error_hook); - allow_another_instance(); - - HINSTANCE hInstance = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE); - Ctl3dRegister(hInstance); - Ctl3dAutoSubclass(hInstance); + HInstance = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE); + + Ctl3dRegister(HInstance); + Ctl3dAutoSubclass(HInstance); #endif customize_colors(); init_controls(); @@ -514,8 +625,9 @@ void customize_controls( else { #if XVT_OS == XVT_OS_WIN - HINSTANCE _hInstance = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE); - Ctl3dUnregister(_hInstance); +// HINSTANCE _hInstance = (HINSTANCE)xvt_vobj_get_attr(NULL_WIN, ATTR_WIN_INSTANCE); + Ctl3dUnregister(HInstance); + deny_another_instance(); #endif free_controls(); } diff --git a/include/xvtility.h b/include/xvtility.h index f50bebf70..265cb1f06 100755 --- a/include/xvtility.h +++ b/include/xvtility.h @@ -29,6 +29,9 @@ WINDOW xvt_create_window long app_data ); + int allow_another_instance(); + int deny_another_instance(); + WINDOW xvt_create_statbar(); void xvt_statbar_set(const char* text, bool def = FALSE); void xvt_statbar_refresh();