571 lines
18 KiB
C
571 lines
18 KiB
C
/***
|
|
*crtexe.c - Initialization for console EXE using CRT DLL
|
|
*
|
|
* Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* This is the actual startup routine for apps linking to the CRT DLL.
|
|
* It calls the user's main routine [w]main() or [w]WinMain after
|
|
* performing C Run-Time Library initialization.
|
|
*
|
|
* With ifdefs, this source file also provides the source code for:
|
|
* wcrtexe.c the startup routine for console apps with wide chars
|
|
* crtexew.c the startup routine for Windows apps
|
|
* wcrtexew.c the startup routine for Windows apps with wide chars
|
|
*
|
|
*Revision History:
|
|
* 08-12-91 GJF Module created.
|
|
* 01-05-92 GJF Substantially revised
|
|
* 01-17-92 GJF Restored Stevewo's scheme for unhandled exceptions.
|
|
* 01-29-92 GJF Added support for linked-in options (equivalents of
|
|
* binmode.obj, commode.obj and setargv.obj).
|
|
* 04-17-92 SKS Add call to _initterm() to do C++ constructors (I386)
|
|
* 08-01-92 SRW winxcpt.h replaced bu excpt.h which is included by
|
|
* oscalls.h
|
|
* 09-16-92 SKS Prepare for C8 C++ for MIPS by calling C++ constructors
|
|
* 04-02-93 SKS Change try/except to __try/__except
|
|
* 04-06-93 SKS Replace _CRTAPI* with _cdecl
|
|
* Change __GetMainArgs to __getmainargs
|
|
* Change fmode/commode implementation to reflect the
|
|
* the change to _declspec(dllimport) for imported data.
|
|
* 04-12-93 CFW Added xia..xiz initializers and initializer call.
|
|
* 04-26-93 GJF Made lpszCommandLine (unsigned char *) to deal with
|
|
* chars > 127 in the command line.
|
|
* 04-27-93 GJF Removed support for _RT_STACK, _RT_INTDIV,
|
|
* _RT_INVALDISP and _RT_NONCONT.
|
|
* 05-14-93 GJF Added support for quoted program names.
|
|
* 05-24-93 SKS Add support for special versions of _onexit/atexit
|
|
* which do one thing for EXE's and another for DLLs.
|
|
* 10-19-93 CFW MIPS support for _imp__xxx.
|
|
* 10-21-93 GJF Added support for NT's crtdll.dll.
|
|
* 11-08-93 GJF Guard the initialization code with the __try -
|
|
* __except statement (esp., C++ constructors for static
|
|
* objects).
|
|
* 11-09-93 GJF Replaced PF with _PVFV (defined in internal.h).
|
|
* 11-23-93 CFW Wide char enable.
|
|
* 12-07-93 CFW Change _TCHAR to _TSCHAR.
|
|
* 01-04-94 CFW Pass copy of environment to main.
|
|
* 01-11-94 GJF Call __GetMainArgs instead of __getmainargs for NT
|
|
* SDK build (same function, different name)
|
|
* 01-28-94 CFW Move environment copying to setenv.c.
|
|
* 03-04-94 SKS Remove __setargv from this module to avoid link-time
|
|
* problems compiling -MD and linking with setargv.obj.
|
|
* static _dowildcard becomes an extern. Add another
|
|
* parameter to _*getmainargs (_newmode).
|
|
* NOTE: These channges do NOT apply to the _NTSDK.
|
|
* 03-28-94 SKS Add call to _setdefaultprecision for X86 (not _NTSDK)
|
|
* 04-01-94 GJF Moved declaration of __[w]initenv to internal.h.
|
|
* 04-06-94 GJF _IMP___FMODE, _IMP___COMMODE now evaluate to
|
|
* dereferences of function returns for _M_IX86 (for
|
|
* compatability with Win32s version of msvcrt*.dll).
|
|
* 04-25-94 CFW wWinMain has Unicode args.
|
|
* 05-16-94 SKS Get StartupInfo to give correct nCmdShow parameter
|
|
* to (_w)WinMain() (was ALWAYS passing SW_SHOWDEFAULT).
|
|
* 08-04-94 GJF Added support for user-supplied _matherr routine
|
|
* 09-06-94 GJF Added code to set __app_type properly.
|
|
* 10-04-94 BWT Fix _NTSDK build
|
|
* 02-22-95 JWM Spliced in PMAC code.
|
|
* 05-24-95 CFW Official ANSI C++ new handler added.
|
|
* 07-24-95 CFW Change PMac onexit malloc to _malloc_crt.
|
|
* 06-27-96 GJF Replaced defined(_WIN32) wint !defined(_MAC). Cleaned
|
|
* up the format a bit.
|
|
* 08-01-96 RDK For PMac, set onexit table pointers to -1 to parallel
|
|
* x86 functionality.
|
|
* 04-28-99 PML Wrap __declspec(allocate()) in _CRTALLOC macro.
|
|
* 05-11-99 KBF Wrap RTC support in #ifdef.
|
|
* 05-17-99 PML Remove all Macintosh support.
|
|
* 11-13-99 PML Create 4 new COM+ specific entrypoints which return
|
|
* instead of calling exit().
|
|
* 11-16-99 PML ... and remove them - linker problems mean new
|
|
* entrypoints don't work. Instead, look directly at the
|
|
* COM Descriptor Image Directory entry in the optional
|
|
* header to see if we are a COM+ app.
|
|
* 02-16-00 GB Changed GetModuleHandle to GetModuleHandleA in
|
|
* check_complus_app.
|
|
* 08-04-00 PML check_complus_app -> check_managed_app (VS7#117746).
|
|
* 08-22-00 GB Changed GetModuleHandle to GetModuleHandleA in
|
|
* mainCRTStartup().
|
|
* 12-09-00 PML Tighten up check_managed_app tests (VS7#167293).
|
|
* 03-27-01 PML report _RT_SPACEARG on __[w]getmainargs error
|
|
* (VS7#231220).
|
|
* 04-30-01 BWT Don't do this for SYSCRT. Need to run against old msvcrt.dll's
|
|
* that don't return error.
|
|
* Remove _NTSDK.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CRTDLL
|
|
|
|
/*
|
|
* SPECIAL BUILD MACROS! Note that crtexe.c (and crtexew.c) is linked in with
|
|
* the client's code. It does not go into crtdll.dll! Therefore, it must be
|
|
* built under the _DLL switch (like user code) and CRTDLL must be undefined.
|
|
* The symbol SPECIAL_CRTEXE is turned on to suppress the normal CRT DLL
|
|
* definition of _fmode and _commode using __declspec(dllexport). Otherwise
|
|
* this module would not be able to refer to both the local and DLL versions
|
|
* of these two variables.
|
|
*/
|
|
|
|
#undef CRTDLL
|
|
#undef _DLL
|
|
#define _DLL
|
|
|
|
#define SPECIAL_CRTEXE
|
|
|
|
#include <cruntime.h>
|
|
#include <oscalls.h>
|
|
#include <internal.h>
|
|
#include <process.h>
|
|
#include <math.h>
|
|
#include <rterr.h>
|
|
#include <stdlib.h>
|
|
#include <tchar.h>
|
|
#include <rtcapi.h>
|
|
#include <sect_attribs.h>
|
|
|
|
|
|
/*
|
|
* wWinMain is not yet defined in winbase.h. When it is, this should be
|
|
* removed.
|
|
*/
|
|
|
|
int
|
|
WINAPI
|
|
wWinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPWSTR lpCmdLine,
|
|
int nShowCmd
|
|
);
|
|
|
|
#define SPACECHAR _T(' ')
|
|
#define DQUOTECHAR _T('\"')
|
|
|
|
#ifdef _M_IX86
|
|
|
|
/*
|
|
* The local copy of the Pentium FDIV adjustment flag
|
|
* and the address of the flag in MSVCRT*.DLL.
|
|
*/
|
|
|
|
extern int _adjust_fdiv;
|
|
|
|
extern int * _imp___adjust_fdiv;
|
|
|
|
#endif
|
|
|
|
|
|
/* default floating point precision - X86 only! */
|
|
|
|
#ifdef _M_IX86
|
|
extern void _setdefaultprecision();
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Declare function used to install a user-supplied _matherr routine.
|
|
*/
|
|
_CRTIMP void __setusermatherr( int (__cdecl *)(struct _exception *) );
|
|
|
|
|
|
/*
|
|
* Declare the names of the exports corresponding to _fmode and _commode
|
|
*/
|
|
#ifdef _M_IX86
|
|
|
|
#define _IMP___FMODE (__p__fmode())
|
|
#define _IMP___COMMODE (__p__commode())
|
|
|
|
#else /* ndef _M_IX86 */
|
|
|
|
/* assumed to be MIPS or Alpha */
|
|
|
|
#define _IMP___FMODE __imp__fmode
|
|
#define _IMP___COMMODE __imp__commode
|
|
|
|
#endif /* _M_IX86 */
|
|
|
|
#if !defined(_M_IX86)
|
|
extern int * _IMP___FMODE; /* exported from the CRT DLL */
|
|
extern int * _IMP___COMMODE; /* these names are implementation-specific */
|
|
#endif
|
|
|
|
extern int _fmode; /* must match the definition in <stdlib.h> */
|
|
extern int _commode; /* must match the definition in <internal.h> */
|
|
extern int _dowildcard; /* passed to __getmainargs() */
|
|
|
|
/*
|
|
* Declare/define communal that serves as indicator the default matherr
|
|
* routine is being used.
|
|
*/
|
|
int __defaultmatherr;
|
|
|
|
/*
|
|
* routine in DLL to do initialization (in this case, C++ constructors)
|
|
*/
|
|
extern void __cdecl _initterm(_PVFV *, _PVFV *);
|
|
|
|
/*
|
|
* routine to check if this is a managed application
|
|
*/
|
|
static int __cdecl check_managed_app(void);
|
|
|
|
/*
|
|
* pointers to initialization sections
|
|
*/
|
|
extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[];
|
|
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[]; /* C initializers */
|
|
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
|
|
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[]; /* C++ initializers */
|
|
|
|
/*
|
|
* Pointers to beginning and end of the table of function pointers manipulated
|
|
* by _onexit()/atexit(). The atexit/_onexit code is shared for both EXE's and
|
|
* DLL's but different behavior is required. These values are set to -1 to
|
|
* mark this module as an EXE.
|
|
*/
|
|
|
|
extern _PVFV *__onexitbegin;
|
|
extern _PVFV *__onexitend;
|
|
|
|
|
|
/***
|
|
*mainCRTStartup(void)
|
|
*wmainCRTStartup(void)
|
|
*WinMainCRTStartup(void)
|
|
*wWinMainCRTStartup(void)
|
|
*
|
|
*Purpose:
|
|
* These routines do the C runtime initialization, call the appropriate
|
|
* user entry function, and handle termination cleanup. For a managed
|
|
* app, they then return the exit code back to the calling routine, which
|
|
* is the managed startup code. For an unmanaged app, they call exit and
|
|
* never return.
|
|
*
|
|
* Function: User entry called:
|
|
* mainCRTStartup main
|
|
* wmainCRTStartup wmain
|
|
* WinMainCRTStartup WinMain
|
|
* wWinMainCRTStartup wWinMain
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
* Managed app: return value from main() et al, or the exception code if
|
|
* execution was terminated by the __except guarding the call
|
|
* to main().
|
|
* Unmanaged app: never return.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef _WINMAIN_
|
|
|
|
#ifdef WPRFLAG
|
|
int wWinMainCRTStartup(
|
|
#else
|
|
int WinMainCRTStartup(
|
|
#endif
|
|
|
|
#else /* ndef _WINMAIN_ */
|
|
|
|
#ifdef WPRFLAG
|
|
int wmainCRTStartup(
|
|
#else
|
|
int mainCRTStartup(
|
|
#endif
|
|
|
|
#endif /* _WINMAIN_ */
|
|
void
|
|
)
|
|
{
|
|
int argc; /* three standard arguments to main */
|
|
_TSCHAR **argv;
|
|
_TSCHAR **envp;
|
|
|
|
int argret;
|
|
int mainret;
|
|
int managedapp;
|
|
|
|
#ifdef _WINMAIN_
|
|
_TUCHAR *lpszCommandLine;
|
|
STARTUPINFO StartupInfo;
|
|
#endif
|
|
_startupinfo startinfo;
|
|
|
|
/*
|
|
* Determine if this is a managed application
|
|
*/
|
|
managedapp = check_managed_app();
|
|
|
|
/*
|
|
* Guard the initialization code and the call to user's main, or
|
|
* WinMain, function in a __try/__except statement.
|
|
*/
|
|
|
|
__try {
|
|
/*
|
|
* Set __app_type properly
|
|
*/
|
|
#ifdef _WINMAIN_
|
|
__set_app_type(_GUI_APP);
|
|
#else
|
|
__set_app_type(_CONSOLE_APP);
|
|
#endif
|
|
|
|
/*
|
|
* Mark this module as an EXE file so that atexit/_onexit
|
|
* will do the right thing when called, including for C++
|
|
* d-tors.
|
|
*/
|
|
__onexitbegin = __onexitend = (_PVFV *)(-1);
|
|
|
|
/*
|
|
* Propogate the _fmode and _commode variables to the DLL
|
|
*/
|
|
*_IMP___FMODE = _fmode;
|
|
*_IMP___COMMODE = _commode;
|
|
|
|
#ifdef _M_IX86
|
|
/*
|
|
* Set the local copy of the Pentium FDIV adjustment flag
|
|
*/
|
|
|
|
_adjust_fdiv = * _imp___adjust_fdiv;
|
|
#endif
|
|
|
|
/*
|
|
* Run the RTC initialization code for this DLL
|
|
*/
|
|
#ifdef _RTC
|
|
_RTC_Initialize();
|
|
#endif
|
|
|
|
/*
|
|
* Call _setargv(), which will trigger a call to __setargv() if
|
|
* SETARGV.OBJ is linked with the EXE. If SETARGV.OBJ is not
|
|
* linked with the EXE, a dummy _setargv() will be called.
|
|
*/
|
|
#ifdef WPRFLAG
|
|
_wsetargv();
|
|
#else
|
|
_setargv();
|
|
#endif
|
|
|
|
/*
|
|
* If the user has supplied a _matherr routine then set
|
|
* __pusermatherr to point to it.
|
|
*/
|
|
if ( !__defaultmatherr )
|
|
__setusermatherr(_matherr);
|
|
|
|
#ifdef _M_IX86
|
|
_setdefaultprecision();
|
|
#endif
|
|
|
|
/*
|
|
* Do runtime startup initializers.
|
|
*
|
|
* Note: the only possible entry we'll be executing here is for
|
|
* __lconv_init, pulled in from charmax.obj only if the EXE was
|
|
* compiled with -J. All other .CRT$XI* initializers are only
|
|
* run as part of the CRT itself, and so for the CRT DLL model
|
|
* are not found in the EXE. For that reason, we call _initterm,
|
|
* not _initterm_e, because __lconv_init will never return failure,
|
|
* and _initterm_e is not exported from the CRT DLL.
|
|
*
|
|
* Note further that, when using the CRT DLL, executing the
|
|
* .CRT$XI* initializers is only done for an EXE, not for a DLL
|
|
* using the CRT DLL. That is to make sure the -J setting for
|
|
* the EXE is not overriden by that of any DLL.
|
|
*/
|
|
_initterm( __xi_a, __xi_z );
|
|
|
|
#ifdef _RTC
|
|
atexit(_RTC_Terminate);
|
|
#endif
|
|
|
|
/*
|
|
* Get the arguments for the call to main. Note this must be
|
|
* done explicitly, rather than as part of the dll's
|
|
* initialization, to implement optional expansion of wild
|
|
* card chars in filename args
|
|
*/
|
|
|
|
startinfo.newmode = _newmode;
|
|
|
|
#ifdef ANSI_NEW_HANDLER
|
|
startinfo.newh = _defnewh;
|
|
#endif /* ANSI_NEW_HANDLER */
|
|
|
|
#ifdef WPRFLAG
|
|
argret = __wgetmainargs(&argc, &argv, &envp,
|
|
_dowildcard, &startinfo);
|
|
#else
|
|
argret = __getmainargs(&argc, &argv, &envp,
|
|
_dowildcard, &startinfo);
|
|
#endif
|
|
|
|
#ifndef _SYSCRT
|
|
if (argret < 0)
|
|
_amsg_exit(_RT_SPACEARG);
|
|
#endif
|
|
|
|
/*
|
|
* do C++ constructors (initializers) specific to this EXE
|
|
*/
|
|
_initterm( __xc_a, __xc_z );
|
|
|
|
#ifdef _WINMAIN_
|
|
/*
|
|
* Skip past program name (first token in command line).
|
|
* Check for and handle quoted program name.
|
|
*/
|
|
#ifdef WPRFLAG
|
|
/* OS may not support "W" flavors */
|
|
if (_wcmdln == NULL)
|
|
return 255;
|
|
lpszCommandLine = (wchar_t *)_wcmdln;
|
|
#else
|
|
lpszCommandLine = (unsigned char *)_acmdln;
|
|
#endif
|
|
|
|
if ( *lpszCommandLine == DQUOTECHAR ) {
|
|
/*
|
|
* Scan, and skip over, subsequent characters until
|
|
* another double-quote or a null is encountered.
|
|
*/
|
|
while ( *++lpszCommandLine && (*lpszCommandLine
|
|
!= DQUOTECHAR) );
|
|
/*
|
|
* If we stopped on a double-quote (usual case), skip
|
|
* over it.
|
|
*/
|
|
if ( *lpszCommandLine == DQUOTECHAR )
|
|
lpszCommandLine++;
|
|
}
|
|
else {
|
|
while (*lpszCommandLine > SPACECHAR)
|
|
lpszCommandLine++;
|
|
}
|
|
|
|
/*
|
|
* Skip past any white space preceeding the second token.
|
|
*/
|
|
while (*lpszCommandLine && (*lpszCommandLine <= SPACECHAR)) {
|
|
lpszCommandLine++;
|
|
}
|
|
|
|
StartupInfo.dwFlags = 0;
|
|
GetStartupInfo( &StartupInfo );
|
|
|
|
#ifdef WPRFLAG
|
|
mainret = wWinMain(
|
|
#else
|
|
mainret = WinMain(
|
|
#endif
|
|
GetModuleHandleA(NULL),
|
|
NULL,
|
|
lpszCommandLine,
|
|
StartupInfo.dwFlags & STARTF_USESHOWWINDOW
|
|
? StartupInfo.wShowWindow
|
|
: SW_SHOWDEFAULT
|
|
);
|
|
#else /* _WINMAIN_ */
|
|
|
|
#ifdef WPRFLAG
|
|
__winitenv = envp;
|
|
mainret = wmain(argc, argv, envp);
|
|
#else
|
|
__initenv = envp;
|
|
mainret = main(argc, argv, envp);
|
|
#endif
|
|
|
|
#endif /* _WINMAIN_ */
|
|
|
|
if ( !managedapp )
|
|
exit(mainret);
|
|
|
|
_cexit();
|
|
|
|
}
|
|
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
|
|
{
|
|
/*
|
|
* Should never reach here
|
|
*/
|
|
|
|
mainret = GetExceptionCode();
|
|
|
|
if ( !managedapp )
|
|
_exit(mainret);
|
|
|
|
_c_exit();
|
|
|
|
} /* end of try - except */
|
|
|
|
return mainret;
|
|
}
|
|
|
|
/***
|
|
*check_managed_app() - Check for a managed executable
|
|
*
|
|
*Purpose:
|
|
* Determine if the EXE the startup code is linked into is a managed app
|
|
* by looking for the COM Runtime Descriptor in the Image Data Directory
|
|
* of the PE or PE+ header.
|
|
*
|
|
*Entry:
|
|
* None
|
|
*
|
|
*Exit:
|
|
* 1 if managed app, 0 if not.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
static int __cdecl check_managed_app (
|
|
void
|
|
)
|
|
{
|
|
PIMAGE_DOS_HEADER pDOSHeader;
|
|
PIMAGE_NT_HEADERS pPEHeader;
|
|
PIMAGE_OPTIONAL_HEADER32 pNTHeader32;
|
|
PIMAGE_OPTIONAL_HEADER64 pNTHeader64;
|
|
|
|
pDOSHeader = (PIMAGE_DOS_HEADER)GetModuleHandleA(NULL);
|
|
if ( pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE )
|
|
return 0;
|
|
|
|
pPEHeader = (PIMAGE_NT_HEADERS)((char *)pDOSHeader +
|
|
pDOSHeader->e_lfanew);
|
|
if ( pPEHeader->Signature != IMAGE_NT_SIGNATURE )
|
|
return 0;
|
|
|
|
pNTHeader32 = (PIMAGE_OPTIONAL_HEADER32)&pPEHeader->OptionalHeader;
|
|
switch ( pNTHeader32->Magic ) {
|
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
/* PE header */
|
|
if ( pNTHeader32->NumberOfRvaAndSizes <=
|
|
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
|
|
return 0;
|
|
return !! pNTHeader32 ->
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] .
|
|
VirtualAddress;
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
/* PE+ header */
|
|
pNTHeader64 = (PIMAGE_OPTIONAL_HEADER64)pNTHeader32;
|
|
if ( pNTHeader64->NumberOfRvaAndSizes <=
|
|
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
|
|
return 0;
|
|
return !! pNTHeader64 ->
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] .
|
|
VirtualAddress;
|
|
}
|
|
|
|
/* Not PE or PE+, so not managed */
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CRTDLL */
|