"Apientry _Twinmain" and "Winapi Winmain" Difference

APIENTRY _tWinMain and WINAPI WinMain difference

_tWinMain is just a #define shortcut in tchar.h to the appropriate version of WinMain.

If _UNICODE is defined, then _tWinMain expands to wWinMain. Otherwise, _tWinMain is the same as WinMain.

The relevant macro looks something like this (there's actually a lot of other code interspersed):

#ifdef  _UNICODE
#define _tWinMain wWinMain
#else
#define _tWinMain WinMain
#endif

WINMAIN and main() in C++ (Extended)

About the functions.

The C and C++ standards require any program (for a “hosted” C or C++ implementation) to have a function called main, which serves as the program's startup function. The main function is called after zero-initialization of non-local static variables, and possibly but not necessarily (!, C++11 §3.6.2/4) this call happens after dynamic initialization of such variables. It can have one of the following signatures:

int main()
int main( int argc, char* argv[] )

plus possible implementation-defined signatures (C++11 §3.6.1/2) except that the result type must be int.

As the only such function in C++ main has a default result value, namely 0. If main returns then after the ordinary function return exit is called with the main result value as argument. The standard defines three values that guaranteed can be used: 0 (indicates success), EXIT_SUCCESS (also indicates success, and is typically defined as 0), and EXIT_FAILURE (indicates failure), where the two named constants are defined by the <stdlib.h> header which also declares the exit function.

The main arguments are intended to represent the command line arguments for the command used to start the process. argc (argument count) is the number of items in the argv (argument values) array. In addition to those items argv[argc] is guaranteed to be 0. If argc > 0 – which is not guaranteed! – then argv[0] is guaranteed to either be a pointer to an empty string, or a pointer to the “name used to invoke the program”. This name may include a path, and it may be the name of the executable.

Using the main arguments to obtain the command line arguments works fine in *nix, because C and C++ originated with *nix. However, the de facto Windows standard for the encoding of the main arguments is Windows ANSI, which does not support general Windows filenames (such as, for a Norwegian Windows installation, filenames with Greek or Cyrillic characters). Therefore Microsoft chose to extend the C and C++ languages with a Windows-specific startup function called wmain, which has wide character based arguments encoded as UTF-16, which can represent any filename.

The wmain function can have one of these signatures, corresponding to the standard signatures for main:

int wmain()
int wmain( int argc, wchar_t* argv[] )

plus a few more that are not especially useful.

I.e., wmain is a direct wide character based replacement for main.

The WinMain char based function was introduced with Windows, in the early 1980's:

int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
);

where CALLBACK, HINSTANCE and LPSTR are defined by the <windows.h> header (LPSTR is just char*).

Arguments:

  • the hInstance argument value is the base address of the memory image of the executable, it's primarily used to load resources from the executable, and it can alternatively be obtained from the GetModuleHandle API function,

  • the hPrevInstance argument is always 0,

  • the lpCmdLine argument can alternatively be obtained from the GetCommandLine API function, plus a bit of weird logic to skip the program name part of the command line, and

  • the nCmdShow argument value can alternatively be obtained from the GetStartupInfo API function, but with modern Windows the first creation of a top level window does that automatically so it's not of any practical use.

Thus, the WinMain function has the same drawbacks as standard main, plus some (in particular the verbosity and being non-standard), and no advantages of its own, so it's really inexplicable except possibly as a vendor lock-in thing. However, with the Microsoft tool chain it makes the linker default to the GUI subsystem, which some see as an advantage. But with e.g. the GNU toolchain it does not have such an effect so this effect cannot be relied on.

The wWinMain wchar_t based function is a wide character variant of WinMain, in the same way as wmain is a wide character variant of standard main:

int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PWSTR lpCmdLine,
int nCmdShow
);

where WINAPI is the same as CALLBACK, and PWSTR is simply wchar_t*.

There is no good reason to use any of the non-standard functions except the least known and least supported of them, namely wmain, and then just for convenience: that this avoids using the GetCommandLine and CommandLineToArgvW API functions to pick up UTF-16 encoded arguments.

To avoid the Microsoft linker acting up (the GNU toolchain's linker doesn't), just set the LINK environment variable to /entry:mainCRTStartup, or specify that option directly. This is the Microsoft runtime library entry point function that, after some initialization, calls the standard main function. The other startup functions have corresponding entry point functions named in the same systematic way.


Examples of using the standard main function.

Common source code:

    foo.cpp

#undef UNICODE
#define UNICODE
#include <windows.h>

int main()
{
MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}

In the examples below (first with the GNU toolchain and then with the Microsoft toolchain) this program is first built as a console subsystem program, and then as a GUI subsystem program. A console subsystem program, or in short just a console program, is one that requires a console window. This is the default subsystem for all Windows linkers I've used (admittedly not a great many), possibly for all Windows linkers period.

For a console program Windows creates a console window automatically if needed. Any Windows process, regardless of subsystem, can have an associated console window, and at most one. Also, the Windows command interpreter waits for a console program program to finish, so that the program's text presentation has finished.

Conversely, a GUI subsystem program is one that doesn't require a console window. The command interpreter does not wait for a GUI subsystem program, except in batch files. One way to avoid the completion wait, for both kinds of program, is to use the start command. One way to present console window text from a GUI subsystem program is to redirect its standard output stream. Another way is to explicitly create a console window from the program's code.

The program's subsystem is encoded in the executable's header. It's not shown by Windows Explorer (except that in Windows 9x one could “quick view” an executable, which presented just about the same information as Microsoft's dumpbin tool now does). There is no corresponding C++ concept.

main with the GNU toolchain.


[D:\dev\test]
> g++ foo.cpp
[D:\dev\test]
> objdump -x a.exe | find /i "subsys"
MajorSubsystemVersion 4
MinorSubsystemVersion 0
Subsystem 00000003 (Windows CUI)
[544](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[612](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000003 __subsystem__
[636](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> g++ foo.cpp -mwindows
[D:\dev\test]
> objdump -x a.exe | find /i "subsys"
MajorSubsystemVersion 4
MinorSubsystemVersion 0
Subsystem 00000002 (Windows GUI)
[544](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[612](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000002 __subsystem__
[636](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> _

main with Microsoft's toolchain:


[D:\dev\test]
> set LINK=/entry:mainCRTStartup
[D:\dev\test]
> cl foo.cpp user32.lib
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
6.00 subsystem version
3 subsystem (Windows CUI)

[D:\dev\test]
> cl foo.cpp /link user32.lib /subsystem:windows
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
6.00 subsystem version
2 subsystem (Windows GUI)

[D:\dev\test]
> _

Examples of using Microsoft’s wmain function.

The following main code is common to both the GNU toolchain and Microsoft toolchain demonstrations:

    bar.cpp

#undef UNICODE
#define UNICODE
#include <windows.h>

#include <string> // std::wstring
#include <sstream> // std::wostringstream
using namespace std;

int wmain( int argc, wchar_t* argv[] )
{
wostringstream text;

text << argc - 1 << L" command line arguments:\n";
for( int i = 1; i < argc; ++i )
{
text << "\n[" << argv[i] << "]";
}

MessageBox( 0, text.str().c_str(), argv[0], MB_SETFOREGROUND );
}

wmain with the GNU toolchain.

The GNU toolchain doesn't support Microsoft's wmain function:


[D:\dev\test]
> g++ bar.cpp
d:/bin/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.1/../../../libmingw32.a(main.o):main.c:(.text.startup+0xa3): undefined reference to `WinMain
@16'
collect2.exe: error: ld returned 1 exit status

[D:\dev\test]
> _

The link error message here, about WinMain, is because the GNU toolchain does support that function (presumably because so much ancient code uses it), and searches for it as a last resort after failing to find a standard main.

However, it's trivial to add a module with a standard main that calls the wmain:

    wmain_support.cpp

extern int wmain( int, wchar_t** );

#undef UNICODE
#define UNICODE
#include <windows.h> // GetCommandLine, CommandLineToArgvW, LocalFree

#include <stdlib.h> // EXIT_FAILURE

int main()
{
struct Args
{
int n;
wchar_t** p;

~Args() { if( p != 0 ) { ::LocalFree( p ); } }
Args(): p( ::CommandLineToArgvW( ::GetCommandLine(), &n ) ) {}
};

Args args;

if( args.p == 0 )
{
return EXIT_FAILURE;
}
return wmain( args.n, args.p );
}

Now,


[D:\dev\test]
> g++ bar.cpp wmain_support.cpp
[D:\dev\test]
> objdump -x a.exe | find /i "subsystem"
MajorSubsystemVersion 4
MinorSubsystemVersion 0
Subsystem 00000003 (Windows CUI)
[13134](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[13576](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000003 __subsystem__
[13689](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> g++ bar.cpp wmain_support.cpp -mwindows
[D:\dev\test]
> objdump -x a.exe | find /i "subsystem"
MajorSubsystemVersion 4
MinorSubsystemVersion 0
Subsystem 00000002 (Windows GUI)
[13134](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[13576](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000002 __subsystem__
[13689](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> _

wmain with Microsoft’s toolchain.

With Microsoft's toolchain the linker automatically infers the wmainCRTStartup entry point if no entry point is specified and a wmain function is present (it's unclear what happens if a standard main is also present, I haven't checked that in recent years):


[D:\dev\test]
> set link=/entry:mainCRTStartup
[D:\dev\test]
> cl bar.cpp user32.lib
bar.cpp
LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
bar.exe : fatal error LNK1120: 1 unresolved externals

[D:\dev\test]
> set link=
[D:\dev\test]
> cl bar.cpp user32.lib
bar.cpp

[D:\dev\test]
> _

With a non-standard startup function such as wmain it is, however, probably best to specify the entry point explicitly, so as to be very clear about the intention:


[D:\dev\test]
> cl bar.cpp /link user32.lib /entry:wmainCRTStartup
bar.cpp

[D:\dev\test]
> dumpbin /headers bar.exe | find /i "subsystem"
6.00 subsystem version
3 subsystem (Windows CUI)

[D:\dev\test]
> cl bar.cpp /link user32.lib /entry:wmainCRTStartup /subsystem:windows
bar.cpp

[D:\dev\test]
> dumpbin /headers bar.exe | find /i "subsystem"
6.00 subsystem version
2 subsystem (Windows GUI)

[D:\dev\test]
> _

What is the correct way to define a UNICODE independent WinMain function?

In <tchar.h>, the macro _tWinMain expands to WinMain or wWinMain depending on project settings. This isn't enough, though; you need to declare the third argument (lpCmdLine) with the charset-agnostic LPTSTR too:

int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nShowCmd
)

If Unicode is enabled on the project, this becomes LPWSTR, giving the signature:

int APIENTRY wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
)

If Unicode is not enabled, you get the signature:

int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)

Replacing WinMain() with main() function in Win32 programs

You can use standard main in a "windows" app (that is, a GUI subsystem Windows application) even with the Microsoft tools, if you add the following to the Microsoft linker options:

/subsystem:windows /ENTRY:mainCRTStartup

Note that this is not necessary for the GNU toolchain.

Still for the Microsoft tools you can alternatively add this to your main file:

#ifdef _MSC_VER
# pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

James McNellis tells you how to get the hInstance.

_tWinMain in static lib LNK2019

Your problem is that the function in the lib is called _tWinMain.

Just call it WinMain and you are good to go.

WINAPI / APIENTRY is not defined

You'll have to include windows.h to get the required definitions, that's just the way Windows development works.

Difference between WinMain and wWinMain

On Windows XP, if an application entry is WinMain, does Windows convert the command line from Unicode to Ansi and pass to the application?

Yes.

If the command line parameter must be in Unicode (for example, Unicode file name, conversion will cause some characters missing), does that mean that I must use wWinMain as the entry function?

Yes, you should, if you want to correctly handle Unicode arguments to your program.

The documentation to WinMain() on MSDN also agrees.

You can, however, also use GetCommandLineW to retrieve the command line specifically in Unicode.



Related Topics



Leave a reply



Submit