Sample code to perform a synchronous spawn is given below. The code
is divided into three source files:
- The main application.
- The 32-bit side of the thunk.
- The 16-bit side of the thunk.
You can use the thunking code as is, calling SynchSpawn() in your own
application as demonstrated in the main application below. For information
on Universal thunks (including which header files and libraries to use),
please see the "Win32s Programmer's Reference."
In all three modules, use the following header file SPAWN.H:
/*** Function Prototypes ****/
DWORD APIENTRY SynchSpawn( LPCSTR lpszCmdLine, UINT nCmdShow );
/*** Constants for Dispatcher ***/
#define SYNCHSPAWN 1
Main Application
This application attempts to synchronously spawn NOTEPAD under Windows NT
and Win32s. NOTE: Under Win32s, NOTEPAD is a 16-bit application.
GetVersion() is used to detect the platform. Under Windows NT,
CreateProcess() and WaitForSingleObject() perform the spawn. Under Win32s,
the thunked routine SynchSpawn() is called.
/*** Main application code ***/
#include <windows.h>
#include "spawn.h"
void main()
{
DWORD dwVersion;
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
dwVersion = GetVersion();
if( !(dwVersion & 0x80000000) ) // Windows NT
{
si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpReserved2 = NULL;
si.cbReserved2 = 0;
si.lpDesktop = NULL;
si.dwFlags = 0;
CreateProcess( NULL,
"notepad",
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi );
WaitForSingleObject( pi.hProcess, INFINITE );
}
else if( LOBYTE(LOWORD(dwVersion)) < 4 ) // Win32s
{
SynchSpawn( "notepad.exe", SW_SHOWNORMAL );
}
MessageBox( NULL, "Return from SynchSpawn", " ", MB_OK );
}
32-bit Side of Thunk
This DLL provides the 32-bit side of the thunk. If the DLL is loaded
under Win32s, it initializes the thunk in its DllMain() by calling
UTRegister(). The entry point SynchSpawn() packages up the arguments
and calls the 16-bit side through the thunk.
/*** Code for 32-bit side of thunk ***/
#define W32SUT_32 // Needed for w32sut.h in 32-bit code
#include <windows.h>
#include "w32sut.h"
#include "spawn.h"
typedef BOOL (WINAPI * PUTREGISTER) ( HANDLE hModule,
LPCSTR lpsz16BitDLL,
LPCSTR lpszInitName,
LPCSTR lpszProcName,
UT32PROC * ppfn32Thunk,
FARPROC pfnUT32Callback,
LPVOID lpBuff
);
typedef VOID (WINAPI * PUTUNREGISTER) (HANDLE hModule);
typedef DWORD (APIENTRY *PUT32CBPROC) (LPVOID lpBuff, DWORD dwUserDefined);
UT32PROC pfnUTProc = NULL;
PUTREGISTER pUTRegister = NULL;
PUTUNREGISTER pUTUnRegister = NULL;
PUT32CBPROC pfnUT32CBProc = NULL;
int cProcessesAttached = 0;
BOOL fWin32s = FALSE;
HANDLE hKernel32 = 0;
/********************************************************************\
* Function: BOOL APIENTRY DllMain(HANDLE, DWORD, LPVOID) *
* *
* Purpose: DLL entry point. Establishes thunk. *
\********************************************************************/
BOOL APIENTRY DllMain(HANDLE hInst, DWORD fdwReason, LPVOID lpReserved)
{
DWORD dwVersion;
if ( fdwReason == DLL_PROCESS_ATTACH )
{
/*
* Registration of UT need to be done only once for first
* attaching process. At that time set the fWin32s flag
* to indicate if the DLL is executing under Win32s or not.
*/
if( cProcessesAttached++ )
{
return(TRUE); // Not the first initialization.
}
// Find out if we're running on Win32s
dwVersion = GetVersion();
fWin32s = (BOOL) (!(dwVersion < 0x80000000))
&& (LOBYTE(LOWORD(dwVersion)) < 4);
if( !fWin32s )
return(TRUE); // Win32s - no further initialization needed
hKernel32 = LoadLibrary( "Kernel32.Dll" ); // Get Kernel32.Dll handle
pUTRegister = (PUTREGISTER) GetProcAddress( hKernel32, "UTRegister"
);
if( !pUTRegister )
return(FALSE); // Error- Win32s, but can't find UTRegister
pUTUnRegister = (PUTUNREGISTER) GetProcAddress(hKernel32,
"UTUnRegister");
if( !pUTUnRegister )
return(FALSE); // Error- Win32s, but can't find
UTUnRegister
return (*pUTRegister)( hInst, // Spawn32.DLL module handle
"SPAWN16.DLL", // 16-bit thunk dll
"UTInit", // init routine
"UTProc", // 16-bit dispatch routine
&pfnUTProc, // Receives thunk address
pfnUT32CBProc, // callback function
NULL ); // no shared memroy
}
if((fdwReason==DLL_PROCESS_DETACH)&&(0==--cProcessesAttached)&&fWin32s)
{
(*pUTUnRegister)( hInst );
FreeLibrary( hKernel32 );
}
} // DllMain()
/********************************************************************\
* Function: DWORD APIENTRY SynchSpawn(LPTSTR, UINT) *
* *
* Purpose: Thunk to 16-bit code *
\********************************************************************/
DWORD APIENTRY SynchSpawn( LPCSTR lpszCmdLine, UINT nCmdShow )
{
DWORD Args[2];
PVOID Translist[2];
Args[0] = (DWORD) lpszCmdLine;
Args[1] = (DWORD) nCmdShow;
Translist[0] = &Args[0];
Translist[1] = NULL;
return( (* pfnUTProc)( Args, SYNCHSPAWN, Translist) );
}
16-bit Side of Thunk
This DLL provides the 16-bit side of the thunk. The LibMain() and WEP()
of this 16-bit DLL perform no special initialization. The UTInit()
function is called during thunk initialization; it stores the callback
procedure address in a global variable. The UTProc() function is called
with a code that indicates which thunk was called as its second
parameter. In this example, the only thunk provided is for SynchSpawn().
The synchronous spawn is performed in the SYNCHSPAWN case of the switch
statement in the UTProc().
NOTE: UTInit() and UTProc() must be exported. This can be done in the
module definition (.DEF) file.
/* Code for 16-bit side of thunk. */
/* Requires linking with TOOLHELP.LIB, for ModuleFindHandle(). */
#ifndef APIENTRY
#define APIENTRY
#endif
#define W32SUT_16 // Needed for w32sut.h in 16-bit code
#include <windows.h>
#include <toolhelp.h>
#include <malloc.h>
#include "w32sut.h"
#include "spawn.h"
UT16CBPROC glpfnUT16CallBack;
/********************************************************************\
* Function: LRESULT CALLBACK LibMain(HANDLE, WORD, WORD, LPSTR) *
* *
* Purpose: DLL entry point *
\********************************************************************/
int FAR PASCAL LibMain( HANDLE hLibInst, WORD wDataSeg,
WORD cbHeapSize, LPSTR lpszCmdLine)
{
return (1);
} // LibMain()
/********************************************************************\
* Function: DWORD FAR PASCAL UTInit(UT16CBPROC, LPVOID) *
* *
* Purpose: Universal Thunk initialization procedure *
\********************************************************************/
DWORD FAR PASCAL UTInit( UT16CBPROC lpfnUT16CallBack, LPVOID lpBuf )
{
glpfnUT16CallBack = lpfnUT16CallBack;
return(1); // Return Success
} // UTInit()
/********************************************************************\
* Function: DWORD FAR PASCAL UTProc(LPVOID, DWORD) *
* *
* Purpose: Dispatch routine called by 32-bit UT DLL *
\********************************************************************/
DWORD FAR PASCAL UTProc( LPVOID lpBuf, DWORD dwFunc)
{
switch (dwFunc)
{
case SYNCHSPAWN:
{
HMODULE hMod;
MODULEENTRY FAR *me;
UINT hInst;
LPCSTR lpszCmdLine;
UINT nCmdShow;
MSG msg;
BOOL again=TRUE;
/* Retrieve the command line arguments stored in buffer */
lpszCmdLine = (LPSTR) ((LPDWORD)lpBuf)[0];
nCmdShow = (UINT) ((LPDWORD)lpBuf)[1];
/* Start the application with WinExec() */
hInst = WinExec( lpszCmdLine, nCmdShow );
if( hInst < 32 )
return 0;
/* Loop until the application is terminated. The Toolhelp API
* ModuleFindHandle() returns NULL when the application is
* terminated. NOTE: PeekMessage() is used to yield the
* processor; otherwise, nothing else could execute on the
* system.
* /
hMod = GetModuleHandle( lpszCmdLine );
me = (MODULEENTRY FAR *) _fcalloc( 1, sizeof(MODULEENTRY) );
me->dwSize = sizeof( MODULEENTRY );
while( NULL != ModuleFindHandle( me, hMod ) && again )
{
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) && again )
{
if(msg.message == WM_QUIT)
{
PostQuitMessage(msg.wParam);
again=FALSE;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 1;
}
} // switch (dwFunc)
return( (DWORD)-1L ); // We should never get here.
} // UTProc()
/********************************************************************\
* Function: int FAR PASCAL _WEP(int) *
* *
* Purpose: Windows exit procedure *
\********************************************************************/
int FAR PASCAL _WEP( int bSystemExit )
{
return (1);
} // WEP()