Reputation: 33813
I have read a lot about how to change the WinMain
entry point procedure, some say you can change the entry point from the linker and some others say you can put the WinMain
into DLL (dllMain
) and so on.
Honestly, I am confused. I believe that there are one or more ways to change the entry point procedure to a custom procedure because there are some examples like MFC don't have a direct WinMain
function and the Qt framework also has a custom entry point procedure it's similar to the console application main
function int main(int argc, char *argv[])
, so, there are ways as I expected.
I want a whatever way to replace/change the entry point procedure for GUI application on Windows from the traditional procedural WinMain
to int main(int argc, char *argv[])
like Qt or even any other custom function but it must be compatible with (MS, GCC, Clang) compilers.
///////////Windows main/////////////
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdParam, int cmdShow){
}
///////////Console main and Qt framework////////////
int main(int argc, char *argv[]) {
}
//////////MFC////////////
class CMyFrame : public CFrameWnd {
public:
CMyFrame() {}
};
class CExample : public CWinApp {
BOOL InitInstance() {}
};
CExample theApp;
How do I do that?
Upvotes: 1
Views: 1507
Reputation: 33706
entry point of exe can by any function with signature
ULONG CALLBACK ep(void* )
possible and use ULONG CALLBACK ep()
- despite on x86 will be wrong stack pointer (esp) after return, this not lead to error, because windows just call ExitThread
after entry return, if it return control at all - usually it call ExitProcess
instead return.
the name of this entry point of course not play any role at all - it can be any valid c/c++ name. entry point found/called not by name but by AddressOfEntryPoint
offset from IMAGE_OPTIONAL_HEADER
but when we build PE - we need tell linker name of this function, for it can set AddressOfEntryPoint, but this info (name of function) used only during build process (not used in runtime)
different linkers of course have different options for do this, link.exe have option /ENTRY
. this option is optional and by default, the starting address is a function name from the C run-time library.
if /ENTRY:MyEntry
explicitly stated - it used as is - MyEntry
will be used as entry point. if no /ENTRY
option set - used default:
if /SUBSYSTEM:CONSOLE
set - used mainCRTStartup
or if it not found wmainCRTStartup
if /SUBSYSTEM:WINDOWS
set - used WinMainCRTStartup
or if it not found wWinMainCRTStartup
but in most case c/c++ developers use CRT libraries. regardless of whether static or dynamic linking used with CRT - some lib code always statically linked with your exe and this code containing function which you used as entry point. for ms windows crt - this is mainCRTStartup
or wmainCRTStartup
(for console apps), WinMainCRTStartup
or wWinMainCRTStartup
for gui apps.
in all this 4 functions - called hardcoded function by name
mainCRTStartup
call main
wmainCRTStartup
call wmain
WinMainCRTStartup
call WinMain
wWinMainCRTStartup
call wWinMain
of course called function must be implemented somewhere in your code or in another lib code. for example if you use MFC - it implement wWinMain
by self and called your code in another way ( via calling virtual functions on object which you override - InitApplication
and InitInstance
)
if back to question how change name of your custom entry point - but for what ? you really not need change name. you need only understand how your entry point is called. if you understand this - you can do almost all.
assume we want use main
as "entry point". i take this in quotes because we really want have real entry point in CRT code and we want that CRT code call exactly main
function.
possible ? simply !
set /ENTRY: mainCRTStartup
linker option. so mainCRTStartup
will be real entry point and it call main
.
another question, i personally think that this is senseless trick, which nothing change and nothing give
also possible simply call main
from WinMain
typedef struct
{
int newmode;
} _startupinfo;
/*
* new mode flag -- when set, makes malloc() behave like new()
*/
EXTERN_C _CRTIMP int __cdecl _query_new_mode( );
EXTERN_C _CRTIMP int __cdecl _set_new_mode( _In_ int _NewMode);
EXTERN_C
_CRTIMP int __cdecl __getmainargs(__out int * _Argc,
__deref_out_ecount(*_Argc) char *** _Argv,
__deref_out_opt char *** _Env,
__in int _DoWildCard,
__in _startupinfo * _StartInfo);
int __cdecl main(__in int _Argc, __in_ecount_z(_Argc) char ** _Argv, ...);
int CALLBACK WinMain( _In_ HINSTANCE , _In_opt_ HINSTANCE , _In_ LPSTR , _In_ int )
{
int _Argc, r;
char ** _Argv;
char ** _Env;
_startupinfo _StartInfo { _query_new_mode( ) };
if (!(r = __getmainargs(&_Argc, &_Argv, &_Env, 0, &_StartInfo)))
{
r = main(_Argc, _Argv, _Env);
if (_Argv) free(_Argv);
}
return r;
}
Upvotes: 1
Reputation: 26066
it must be compatible with (MS, GCC, Clang) compilers
How you do that depends on your compiler. Most of them will have some flags to choose which "subsystem" (the Windows term) you are targeting and even customize the entry point manually.
Put another way, there is no standard way of doing so because this is outside the scope of the C++ standard.
Having said that, some compilers provide the means to emulate the flags of other compilers. For instance, Clang can imitate Microsoft's.
Upvotes: 0