Replacing Winmain() with Main() Function in Win32 Programs

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.

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]
> _

Why is my Windows program trying to call main() instead of WinMain()?

You have a project of subsystem Console instead of Windows. Change it from your project properties, and it will work. That's in Linker -> System -> SubSystem.

How do I set WinMain as entry point?

You need to change the subsystem to Windows.

Sample Image

WinMain vs. main (C++)

Talking about the Microsoft toolchain, conventionally, Win32 graphical applications have always started with WinMain, while main is used for console applications.

The difference between the two kinds of applications actually boils down (mostly) to a single setting in the executable, and is not in capability - a GUI application can create a console, and a console application can create a window - but in the behavior of the loader: for an exe marked as GUI the loader won't allocate any console, while a console exe will attach to the parent's console or create a new one if there isn't one.

For the entrypoint name, it is all just a matter of linker/CRT defaults: the "real" entry point of the executable is just an offset into the final executable, that points to a function that takes no parameters. It's the CRT that does its stuff, determines the parameters and then calls "your" entrypoint, so, in line of principle, they both could work exactly the same way.

The point is, the name/signature of the default entrypoint that the CRT looks for depends from the type of application you are building; if the compiler and linker are set to build a console application, they will look for a main (or wmain or _tmain, depending on Unicode settings), for a GUI application they use WinMain, and DllMain for a dll.


So:

  • use WinMain if you are building a GUI (=no console created for it at startup) application;
  • main for a console application;
  • DllMain for a dll.

Again, this all isn't written in stone (and there are ways to start a GUI application from a standard main), but "when in Rome, do as the Romans do" - i.e. it's usually best to follow the uses of the platform to avoid confusing other developers and going through untested/unsupported compiler settings just to change the signature of the entrypoint.

How can I write a Windows application without using WinMain?

You have an incorrect understanding. The difference between main and WinMain, apart from some differet initialization code, is the parameters passed to it.

main looks like this:

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

While WinMain looks like this:

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

Something has to setup those parameters and make the call, and that's the startup code. When you compile and link a program, one of the linker parameters is the entry point, and that will be, depending on a console or GUI app, a different bit of startup code.

You can certainly write your own startup code, just go into your visual c++ source directory and you can find the startup code, it's called crt0.c and it's in the VC\crt\src directory.

Pass WinMain (or wWinMain) arguments to normal main

One way would be to use CommandLineToArgvW() to parse the result of GetCommandLineW() into an argv-style array of UTF-16 encoded strings, then use WideCharToMultiByte() to convert them to ANSI strings so you can then pass them to main() (assuming you can't use wmain() instead).

For example:

int w_argc = 0;
LPWSTR* w_argv = CommandLineToArgvW(GetCommandLineW(), &w_argc);
if (w_argv)
{
char** my_argv = new char*[w_argc];
int my_argc = 0;

for (int i = 0; i < w_argc; ++i)
{
int w_len = lstrlenW(w_argv[i]);
int len = WideCharToMultiByte(CP_ACP, 0, w_argv[i], w_len, NULL, 0, NULL, NULL);
my_argv[my_argc] = new char[len+1];
WideCharToMultiByte(CP_ACP, 0, wargv[i], w_len, my_argv[my_argc], len+1, NULL, NULL);
++my_argc;
}

main(my_argc, my_argv);

for (int i = 0; i < my_argc; ++i)
delete[] my_argv[i];
delete[] my_argv;

LocalFree(w_argv);
}

Alternatively:

int w_argc = 0;
LPWSTR* w_argv = CommandLineToArgvW(GetCommandLineW(), &w_argc);
if (w_argv)
{
std vector<std::string> my_argv_buf;
my_argv.reserve(w_argc);

for (int i = 0; i < w_argc; ++i)
{
int w_len = lstrlenW(w_argv[i]);
int len = WideCharToMultiByte(CP_ACP, 0, w_argv[i], w_len, NULL, 0, NULL, NULL);
std::string s;
s.resize(len);
WideCharToMultiByte(CP_ACP, 0, wargv[i], w_len, &s[0], len, NULL, NULL);
my_argv_buf.push_back(s);
}

std vector<char*> my_argv;
my_argv.reserve(my_argv_buf.size());
for (std vector<std::string>::iterator i = my_argv_buf.begin(); i != my_argv_buf.end(); ++i)
my_argv.push_back(i->c_str());

main(my_argv.size(), &my_argv[0]);

LocalFree(w_argv);
}

Joining two programs one with main() one with WinMain()

If your program has WinMain(), it is presumably a Windows program, while the other is a command-line app. Depending on what the original apps actually do, this may make it difficult to merge them. E.g. if the other app uses standard input/output extensively, it is challenging to transform it into a Windows app. However, if it is simply a bunch of utility methods without side effects, you may simply add the code to your project and start using it.

You have to set up your project to generate a Windows executable, the call to your WinMain() function will then be arranged automatically by the linker.



Related Topics



Leave a reply



Submit