MLTINST2.MS
PROJNAME = MLTINST2
$(PROJNAME).exe: $(PROJNAME).obj $(PROJNAME).res
link /ON:N /A:16 $(PROJNAME).obj, $(PROJNAME), , \
LIBW.LIB LLIBCEW.LIB PROCHOOK.LIB,$(PROJNAME).def
RC $(PROJNAME).res $(PROJNAME).exe
$(PROJNAME).obj: $(PROJNAME).cpp $(PROJNAME).h
CL /AL /W3 /GEea /GAs /G2 /c /Zi /YX $(PROJNAME).cpp
$(PROJNAME).res: $(PROJNAME).rc
RC -R -FO $(PROJNAME).res $(PROJNAME).rc
MLTINST2.BOR
.AUTODEPEND
PROJNAME = MLTINST2
CC_CFG = $(PROJNAME).CFG
LINK_OPTS = /A=16/x/c/Twe/v
OBJS = $(PROJNAME).OBJ
$(PROJNAME).EXE: $(OBJS) $(PROJNAME).DEF $(PROJNAME).RC
TLINK $(LINK_OPTS) @&&|
c0wl.obj $(OBJS), $(PROJNAME).EXE, , prochook.lib import.lib cwl.lib
$(PROJNAME).DEF
|
RC $(PROJNAME).RC $(PROJNAME).EXE
.cpp.obj:
bcc -c -2 -w -v -WE -ml {$< }
MLTINST2.DEF
NAME MLTINST2
DESCRIPTION 'Multiple Instance Workaround Demo'
EXETYPE WINDOWS
STUB 'winstub.exe'
CODE MOVEABLE DISCARDABLE PRELOAD
DATA PRELOAD MOVEABLE
HEAPSIZE 1024
STACKSIZE 5120
MLTINST2.RC
MLTINST2.H
#define IDC_TEXT_HINSTANCE 100
#define IDC_BUTTON_INVOKE_ANOTHER 101
#define IDC_BUTTON_ALLOW_ANOTHER 102
MLTINST2.CPP
//==================================
// MULTINST.C - Matt Pietrek 1993
//==================================
#include <windows.h>
#include <string.h>
#include <ctype.h>
#pragma hdrstop
#include "prochook.h"
#include "mltinst2.h"
#define MAX_PATH 260 // This really should be in WINDOWS.H, but is
// inexplicably hard-coded into the data structures
//########################################################################
// Prototypes for functions
//########################################################################
BOOL CALLBACK _export MultInstDlgProc(HWND hWndDlg, UINT msg,
WPARAM wParam, LPARAM lParam);
int MungeModuleHeader( HINSTANCE hInstance, BOOL fMunge );
BOOL SetupMultInst95( HINSTANCE hInstance );
BOOL ShutdownMultInst95(void);
HINSTANCE HInstance;
int far MyUselessVariable; // Far data induces multiple data segments
//########################################################################
// Start of user interface code
//########################################################################
int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow )
{
HInstance = hInstance;
MyUselessVariable = 0; // Reference the far variable (just in case)
FARPROC mpiDialog = MakeProcInstance((FARPROC)MultInstDlgProc, hInstance);
DialogBox( hInstance, "MultInstDlg", 0, (DLGPROC)mpiDialog );
return 0;
}
//
// Dialog procedure for the main dialog
//
BOOL CALLBACK __export MultInstDlgProc(HWND hWndDlg, UINT msg,
WPARAM wParam, LPARAM lParam)
{
char buffer[128];
if ( msg == WM_INITDIALOG )
{
wsprintf(buffer, "My hInstance is %04X", HInstance);
SetDlgItemText(hWndDlg, IDC_TEXT_HINSTANCE, buffer);
}
else if ( msg == WM_COMMAND )
{
if ( wParam == IDC_BUTTON_INVOKE_ANOTHER )
{ // Try to invoke another instance of the program
GetModuleFileName(HInstance, buffer, sizeof(buffer));
if ( WinExec( buffer, SW_NORMAL ) < 32 )
MessageBox(hWndDlg, "Unable to exec program", 0, MB_OK);
}
else if ( wParam == IDC_BUTTON_ALLOW_ANOTHER )
{
SetupMultInst95( HInstance );
EnableWindow( LOWORD(lParam), FALSE );
}
}
else if ( msg == WM_CLOSE )
{
ShutdownMultInst95();
EndDialog(hWndDlg, 0); return FALSE;
}
return FALSE;
}
//########################################################################
// 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 )
{
HMODULE hModuleSel;
LPSTR lpszModName, lpszFileName;
BYTE cbModuleName;
static BOOL fResidentNamesMunged = FALSE;
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 )
{
// 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;
}
//
// 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.
//########################################################################
NPHOOKCHILD npHookLoadModule = 0;
char szOurFileName[MAX_PATH];
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 SetupMultInst95( HINSTANCE hInstance )
{
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;
}
BOOL ShutdownMultInst95(void)
{
if ( !npHookLoadModule )
return FALSE;
SetProcRelease( npHookLoadModule );
npHookLoadModule = 0;
return TRUE;
}