Notice: This website is an unofficial Microsoft Knowledge Base (hereinafter KB) archive and is intended to provide a reliable access to deleted content from Microsoft KB. All KB articles are owned by Microsoft Corporation. Read full disclaimer for more details.

Performing a Synchronous Spawn Under Win32s

View products that this article applies to.


Under Windows NT, you can synchronously spawn an application (that is, spawn an application and wait until the spawned application is terminated before continuing). To do so, call CreateProcess() to start the application, and pass the handle returned to WaitForSingleObject() to wait for the application to terminate, or you could use the C run-time (CRT) function, _spawnl() with _P_WAIT from a Win32-based application. The former approach is shown in the sample code in the "More Information" section in this article.

However, this method does not work under Win32s. Under Win32s, CreateProcess() does not return the process handle for 16-bit Windows-based applications, only for Win32-based application. Even if it did, the method described in the proceeding paragraph would not work under Win32s because WaitForSingleObject() returns TRUE immediately under Win32s.

In fact, there is no 32-bit only solution for this issue. The 32-bit WinExec() does not return an instance handle as the 16-bit WinExec() does. In addition, you cannot use GetExitCodeProcess() to find the exit status of 16-bit Windows-based applications in order to loop on their status. It is a limitation that GetExitCodeProcess() returns zero for 16-bit Windows-based applications on both Windows NT and Win32s.

The solution is to create a thunk to the 16-bit side and from the 16-bit side, solve the problem as you would normally solve it from a Windows-based application. Namely, start the application with WinExec() and use one of the Toolhelp APIs in a test loop to determine when the application is terminated. Alternatively, you can use EnumWindows() to determine when the application is terminated. The sample code below uses the Toolhelp APIs.

↑ Back to the top

More information

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};

   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,
                     &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 DWORD (APIENTRY *PUT32CBPROC) (LPVOID lpBuff, DWORD dwUserDefined);

UT32PROC      pfnUTProc = 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,

      if( !pUTUnRegister )
         return(FALSE);        // Error- Win32s, but can't find

      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

      (*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
#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;

*                                                                    *
*  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                 *

   glpfnUT16CallBack = lpfnUT16CallBack;
   return(1);   // Return Success
} // UTInit()

* Function: DWORD FAR PASCAL UTProc(LPVOID, DWORD)                   *
*                                                                    *
*  Purpose: Dispatch routine called by 32-bit UT DLL                 *

   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)
          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()

↑ Back to the top


For more information on the limitation of the C run-time (CRT) function, _spawnl() with the _P_WAIT parameter under Win32s, please refer to the following Knowledge Base article:
125213 PRB: Spawn with _P_WAIT Returns Immediately on Win32s

↑ Back to the top


Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer offers support. Therefore, this article is offered "as is" and will no longer be updated.

↑ Back to the top

Keywords: kbcode, KB125212

↑ Back to the top

Article Info
Article ID : 125212
Revision : 3
Created on : 3/7/2005
Published on : 3/7/2005
Exists online : False
Views : 83