Universal Thunk Overview ======================== Win32s has the following design goals and requirements: o Offer a 32-bit programming model for Windows 3.1 Enhanced Mode o Use Win32 semantics for the windowing API o Win32s supports Win32 apps with binary compatibility (same exe format) o Win32s provides seemless migration from 16-bit Windows to 32-bit Windows NT Binary compatibility and the requirement to provide natural migration to Windows NT creates a portability problem for ISVs which have Windows apps that require device drivers. Win32s cannot support the Windows NT kernel device driver model on Windows 3.1. Therefore, the only way that Win32 apps running on Windows 3.1 can access devices is through existing drivers supported by Windows 3.1. However, Windows NT does not support mixing 16- and 32-bit code in the same process. This is not portable and therefore, not allowed. There are a number of IPC mechanisms such as DDEML that would allow data to be passed between Win32 and 16-bit Windows processes, but the bandwidth is not sufficiently high for some types of applications. Therefore, the Universal Thunk has been designed to overcome this problem. The Universal Thunk allows a Win32 application to call a routine in a 16-bit DLL. There is also support for a 16-bit routine to callback to a 32-bit function. The simple Win32s thunk used to implement this design also provide address translation services allowing access to flat memory from segmented code and vice versa. This design allows a Win32 application to isolate driver specific routines in a 16-bit DLL. The Win32 application remains portable across Windows 3.1 and Windows NT; on Windows NT the 16-bit DLL is replaced with a 32-bit DLL that communicates to devices via the Windows NT model. Universal Thunk Design ====================== First diagram illustrates Win32 Application using UT to access existing 16-bit service. Win32 application is binary compatible on Windows 3.1 and Windows NT. The Win32 DLL offers identical interfaces on Windows 3.1 and Windows NT, however unique Win32 (32-bit) DLLs are created for Windows 3.1 and Windows NT. This DLL isolates the different techniques required to access drivers and hardware on Windows 3.1 and Windows NT. Windows 3.1 ----------- +--------------+ | | | Win32 App | | | +--------------+ ^ | Std. Interface Existing Win3.1 v 16-bit DLL DLL/Driver +--------------+ +----------+ +----------+ (1) +----------+ | |------>| |------>| UT16Proc |------>| | | Win32 DLL | | Win32s UT|------>| UT16Init | (2) | | |fnUT32CallBack|<------| |<------| |<------| | +--------------+ +----------+ +----------+ +----------+ 32-bit 16-bit Interface Interface (1) Pass data from Win32 app to existing Win 3.1 DLL/driver or obtain data from existing Win 3.1 DLL/driver. Transaction driven by Win32 application. (2) Use callback function to transfer data from existing Win 3.1 DLL/driver to Win32 application. Transaction driven from 16-bit side. Windows NT ---------- Illustrates Win32 application relying on 32-bit Windows NT DLLs and drivers. +--------------+ | | | Win32 App | | | +--------------+ ^ | Std. Interface v +--------------+ | | | Win32 DLL | | | +--------------+ ^ | Driver I/O (via Win32 API or IOCTLs) v +--------------+ | | | Windows NT | | 32-bit driver| | Kernel Mode | | | +--------------+ ============================================================================ ================ Universal Thunk API ======================================= ============================================================================ typedef DWORD (* WINAPI UT32PROC)(LPVOID lpBuff, DWORD dwUserDefined, LPVOID *lpTranslationList ); Prototype of 32-bit thunk to 16-bit dll. Parameters ---------- lpBuff pointer to general purpose memory buffer. This 32-bit pointer is translated to 16:16 form and passed to the 16-bit procedure. The segmented address provides addresiblity for object of 32KB. This parameter is optional. dwUserDefined available for application use. lpTranslationList array of pointers to pointers that should be translated to segmented form. the list is terminated by a null pointer. No validity check is performed on the address except for null check. The translation list is used internally and not passed to the 16-bit procedure. This parameter is optional. See Also -------- UTRegister() ___________________________________________________________________________ DWORD FAR PASCAL UT16PROC(LPVOID lpBuff, DWORD dwUserDefined ); UT16PROC name is a placeholder for the application-defined function name. The actual name must be exported by including it in the EXPORTS statement in the DLL's module-definition file. Parameters ---------- lpBuff pointer to general purpose memory buffer that was passed by the 32-bit code. dwUserDefined available for application use. See Also -------- UTRegister() ___________________________________________________________________________ DWORD FAR PASCAL UT16INIT( UT16CBPROC pfnUT16CallBack, LPVOID lpBuff); UT16INIT name is a placeholder for the application-defined function name. The actual name must be exported by including it in the EXPORTS statement in the DLL's module-definition file. Parameters ---------- pfnUT16CallBack 16-bit thunk to 32-bit callback routine, as specified at registration. lpBuff pointer to general purpose memory buffer that was passed by the 32-bit code. See Also -------- UTRegister() ___________________________________________________________________________ DWORD WINAPI UT32CBPROC( LPVOID lpBuff, DWORD dwUserDefined ); UT32CBPROC name is a placeholder for the application-defined function name. It does not have to be exported. Parameters ---------- lpBuff pointer to general purpose memory buffer as passed to 16-bit callback thunk. dwUserDefined available for application use. Comments -------- Memory allocated by 16-bit code and passed to 32-bit must be first fixed in memory. See Also -------- UTRegister() ___________________________________________________________________________ typedef DWORD FAR PASCAL (*UT16CBPROC)( LPVOID lpBuff, DWORD dwUserDefined, LPVOID *lpTranslationList ); Prototype of 16-bit Universal Thunk Call Back procedure Parameters ---------- lpBuff Pointer to general purpose memory buffer. This segmented pointer is translated to flat address and passed to the 32-bit callback procedure. This parameter is optional. dwUserDefined available for application use. lpTranslationList array of pointers to pointers that should be translated to segmented form. the list is terminated by a null pointer. No validity check is performed on the address except for null check. The translation list is used internally and not passed to the 32-bit callback procedure. This parameter is optional. See Also -------- UTRegister() ___________________________________________________________________________ BOOL UTRegister(hModule, lpsz16BITDLL, lpszInitName, lpszProcName, ppfn32Thunk, pfnUT32CallBack, lpBuff ) HANDLE hModule; // 32-bit Dll module handle LPCTSTR lpsz16BITDLL; // Name of DLL (i.e. "MY16BIT.DLL") LPCTSTR lpszInitName; // Name of exported init function LPCTSTR lpszProcName; // Name of exported thunk function UT32PROC * ppfn32Thunk; // Output parameter (indirect 32-bit function pointer) FARPROC pfnUT32CallBack; // Address of 32-bit callback function LPVOID lpBuff; // Pointer to shared memory block This routine registers a universal thunk that can be used to access 16-bit code from a Win32 application running via Win32s on Windows 3.1. Only one UT can be created per Win32 dll. The thunk can be destroyed by calling UTUnRegister(). UTRegister() will automatically load the 16-bit DLL specified. It then calls the init routine and provide it a 16-bit callable routine. The system creates a 32-bit thunk that can be used to call the 16-bit procedure in the 16-bit dll. Parameters ---------- hDllInst Handle of 32-bit dll. The universal thunk provides a mechnism to "extend" a win32 dll into win3.1. The thunk is owned by the dll and every dll is limited to have only one thunk. lpsz16BITDLL Points to a null-terminated string that that names the library file to be loaded. If the string does not contain a path, Win32s searches for the library using the same search mechanism as LoadLibrary on Windows 3.1. lpszInitName Points to a null-terminated string containing the function name, or specifies the ordinal value of the initialization function. If it is an ordinal value, the value must be in the low word and the high word must be zero. This parameter can be NULL if no initialization or callback is required. lpszProcName Points to a null-terminated string containing the function name, or specifies the ordinal value of the 16-bit function. If it is an ordinal value, the value must be in the low word and the high word must be zero. ppfn32Thunk Return value is a 32-bit function pointer (thunk to 16-bit routine) if UTRegister() is successful. This function can be used to call the 16-bit routine indirectly. pfnUT32CallBack Address of the 32-bit callback routine. Win32S creates a 16-bit callable thunk to the 32-bit function and provid it to the initialization routine. The 32-bit routine does not need to be specified as an EXPORT function in the DEF file. No call back thunk is created if either this parameter or lpszInitName are NULL. lpBuff Pointer to globally allocated shared memory. Pointer is translated into 16:16 alias by Universal Thunk and is passed to the initialization routine. This parameter is optional and ignored if NULL. Returns ------- Function returns TRUE if DLL was loaded and UT16Init() routine was successfully called or FALSE if an error occurred. Use the GetLastError() function to obtain extended error information. Typical errors are: ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_ORDINAL, and ERROR_NOT_ENOUGH_MEMORY, Comments -------- Registering the Universal Thunk enables two capabilities for communicating between 32-bit and 16-bit routines. The first capability is to allow a Win32 application to call a 16-bit routine passing data using globally shared memory. This is a Win32 application initiated data transfer mechanism. The second capability is to register a callback routine by which 16-bit code can callback into a 32-bit routine in a Win32 DLL. Again, shared global memory is used to transfer data. The first capability is designed to allow Win32 applications to use existing Windows 3.1 device drivers. The design requires the Win32 application to poll the device driver just like a 16-bit Windows 3.1 application. The globally shared memory buffer can be used to transfer data from the device (read operation) or to the device (write operation). The 32-bit callback routine is designed to allow Win32 applications to obtain information from 16-bit service providers. This may be necessary until the service provider itself implements a Win32 DLL service. The 16-bit DLL must EXPORT the initialization routine and the sevice routine, and use the following function prototypes: VOID FAR PASCAL UT16Init( FARPROC pfnUT32CallBack, LPVOID lpBuff ); DWORD FAR PASCAL UT16Proc( LPVOID lpBuff, DWORD dwUserDefined ); UTRegister() will result in Win32s loading the specified DLL with normal Windows 3.1 DLL initialization occuring. Win32s will then call the initialization routine passing this function a 16:16 thunk to the 32-bit callback function. The initialization routine can return data in a global shared memory buffer. The initialization routine must return non zero value to indicate successful initialization. If it fails no thunk is created. UTRegister() returns a 32-bit thunk to the 16-bit UT16Proc(). This pointer can be used in Win32 code to call the 16-bit routine: dwUserDefinedReturn = (*pfnUT16Proc)( pSharedMemory, dwUserDefinedSend, lpTranslationList ); The Universal Thunk will translate the 32-bit linearaddress of the shared memory to a 16:16 segmented pointer, along with all the addresses listed by lpTranslationList before passing it to the 16-bit UT16Proc() routine. The 16-bit code can call Win32 code using the callback mechanism: dwUserDefinedReturn = (*pfnUT32CallBack)( pSharedMemory, dwUserDefinedSend, lpTranslationList ); The 32-bit callback routine should be defined as follows (but does not need to be EXPORTed in the DEF file): DWORD WINAPI UT32CBPROC( LPVOID lpBuff, DWORD dwUserDefined ); See Also -------- UTUnRegister(), LoadLibrary() (16-bit version), GetProcAddress (16-bit version) ___________________________________________________________________________ VOID UTUnRegister( hModule ) HANDLE hModule Requests Win32s to call FreeLibrary() for the 16-bit DLL loaded by UTRegister(). Also, destroys the dynamically created Universal Thunk. Returns ------- No return value. Comments -------- This call allows the single dynamically created Universal Thunk to be destroyed and the 16-bit DLL dereferenced. Win32s will clean-up these resources automatically when the Win32 dll is freed (normally or abnormally). See Also -------- UTRegister(), FreeLibrary() (16-bit version) ___________________________________________________________________________ Universal Thunk auxilary services ================================= The following are address translation services available for 16-bit code only. ___________________________________________________________________________ DWORD UTSelectorOffsetToLinear( lpByte ) LPVOID lpByte; Translate a segmented address to flat form; Returns ------- Equivalent flat address. Comments -------- The base of the flat selectors used by Win32S process is not zero. If the memory is allocated by 16-bit api it must be fixed before its address can be converted to flat form. See Also -------- UTLinearToSelectorOffset ___________________________________________________________________________ LPVOID UTLinearToSelectorOffset( lpByte ) DWORD lpByte; Translate a flat address to a segmented form. Returns ------- Equivalent segmented address. Comments -------- The returned address guarentees addressibility for objects up to 32K byte. See Also -------- UTSelectorOffsetToLinear Universal Thunk Implementation Notes ------------------------------------ The following information discusses implementation issues that impact application usage. 1) Only one Universal Thunk (UT) may exist for each Win32 dll. Additional calls to UTRegister() will fail until UTUnRegister() is called. 2) Win32s will destroy the UT and call FreeLibrary for the 16-bit DLL when: o UTUnRegister() is called o The Win32 dll is freed (normally or abnormally) 3) Memory allocated by 16-bit routines via GlobalAlloc should be fixed via GlobalFix. It must be translated to flat address before it can be used bu 32-bit code. Translated will be performed by the system if passed as lpBuff or by using the traslation list. It can also be translated explicitly via UTSelectorOffsetToLinear before it can be used by 32-bit code. 4) The 32-bit callback function should not be used by 16-bit interrupt handlers. The Win32 API does not support locking memory pages, therefore an interrupt service routine which calls back into 32-bit code cannot gurantee that the code is currently paged into memory. 5) Exception handling can be done in 32-bit code only. Exceptions in 16-bit code will be handled by the last 32-bit exception frame. ============================================================================ ======================= SAMPLE ============================================= ============================================================================ /*++ Copyright (c) 1985-92, Microsoft Corporation Module Name: db.h Abstract: Win32s sample code of Universal Thunk (UT) - this example demonstrates how existing 16bit code can provide services to 32bit application thru a thunking layer based on the UT api. This header file defines the interface to the data base services. The 32-bit interface provided by thunking is identical to the 16-bit interface. --*/ typedef struct { short int len; char *str; } DB_NAME; typedef struct { short int year; short int month; short int day; short int hour; short int minute; short int seconds; } DB_TIME; /* * following services are provided by DB.DLL which is a 16bit dll. */ int DbGetVersion(void); void DbSetTime(DB_TIME * pTime); short DbAddUser(DB_NAME * pName, int Permission, short int *pId); /*++ Copyright (c) 1985-92, Microsoft Corporation Module Name: db32.c Abstract: Win32s sample code of Universal Thunk (UT) - 32bit code that provides the same services as DB.DLL by means of thunking to 16bit dll. This is the main source file of DB32.DLL --*/ #include #include #include UT32PROC pUTProc=NULL; int cProcessesAttached = 0; BOOL DllInit(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) { if ( fdwReason == DLL_PROCESS_ATTACH ) { /* * Registration of UT need to be done only once for first * attaching process. */ if ( cProcessesAttached++ ) { return(TRUE); } return UTRegister( hInst, // DB32.DLL module handle "DB16.DLL", // name of 16bit thunk dll NULL, // no init routine "UTProc", // name of 16bit dispatch routine // exported from DB16.DLL &pUTProc, // Global variable to receive thunk address NULL, // no call back function NULL // no shared memroy ); } else if ( fdwReason == DLL_PROCESS_DETACH ) { if ( --cProcessesAttached == 0 ) { UTUnregister( hInst ); } } } /* * constants for dispatcher in 16bit side */ #define DB_SRV_GETVERSION 0 #define DB_SRV_SETTIME 1 #define DB_SRV_ADDUSER 2 int DbGetVersion(void) { /* * call the 16bit dispatch thru the 32bit thunk. no parameters * are passed. * returned value is the service result. */ return( (int) (* pfnUTProc)(NULL, DB_SRV_GETVERSION, NULL) ); } void DbSetTime(DB_TIME * pTime); { /* * call the 16bit dispatch thru the 32bit thunk. * pass one pointer to a buffer which will be translated * to 16:16 address before passed to 16bit side. * */ (* pfnUTProc)(pTime, DB_SRV_SETTIME, NULL); } int DbAddUser(DB_NAME * pName, int Permission, short int *pId) { DWORD Args[3]; // function has three arguments PVOID TransList[4]; // Three pointers needs translation: // pName // pName->str // pId // plus null to indicate end of list char *pSaveStr; int Ret; /* * put arguments in buffer */ Args[0] = pName; Args[1] = Permission; Args[2] = pId; /* * build translation list for all the flat pointers that need to * be translated to segmented form. */ TransList[0] = & Args[0]; TransList[1] = & pName->str; TransList[2] = & Args[2]; TransList[3] = 0; /* * save the original pointer in the NAME struct so it can be restored * after the call to the thunk. * This is required only if the caller of the service expects the * structure to be left unmodified. */ pSaveStr = pName->str; /* * call the 16bit dispatch thru the 32bit thunk. * pass arguments in buffer along with list of addresses * that need to be translated equivalent segmented format. * */ Ret = (int) (* pfnUTProc)(Args, DB_SRV_ADDUSER, TransList); /* * restore flat pointer */ pName->str = pSaveStr; return(Ret); } /*++ Copyright (c) 1985-92, Microsoft Corporation Module Name: db16.c Abstract: Win32s sample code of Universal Thunk (UT) - services dispatcher in 16bit side. This is the main source file of DB16.DLL. --*/ #include #include #include /* * constants for dispatcher in 16bit side */ #define DB_SRV_GETVERSION 0 #define DB_SRV_SETTIME 1 #define DB_SRV_ADDUSER 2 /* * 16bit dispatcher function. * exported by DB16.DLL */ DWORD FAR PASCAL UTProc(LPVOID lpBuf, DWORD dwFunc) { /* * call 16bit DB services based on the function Id. */ switch (dwFunc) { case DB_SRV_GETVERSION: return( (DWORD) DbGetVersion() ); case DB_SRV_SETTIME: DbSetTime((DB_TIME *) lpBuf); return; case DB_SRV_ADDUSER: return( (DWORD) DbAddUser( (DWORD *)lpBuf [0] , (DWORD *)lpBuf [1] , (DWORD *)lpBuf [2] ) ); } return( 0 ); } ++++++++++++++++++++++++++ E N D ++++++++++++++++++++++++++++++++++++++++++