{"id":110338,"date":"2024-10-04T07:00:00","date_gmt":"2024-10-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110338"},"modified":"2024-10-04T12:35:08","modified_gmt":"2024-10-04T19:35:08","slug":"20241004-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241004-00\/?p=110338","title":{"rendered":"How does the linker decide whether to call <CODE>WinMain<\/CODE> or <CODE>wWinMain<\/CODE>?"},"content":{"rendered":"<p>If you don&#8217;t specify the <tt>\/ENTRY<\/tt> option to the Visual C++ linker, it tries to guess. The details are <a title=\"\/ENTRY (Entry-point symbol)\" href=\"https:\/\/learn.microsoft.com\/cpp\/build\/reference\/entry-entry-point-symbol?view=msvc-170\"> spelled out in the documentation<\/a>, but I&#8217;ll recapture it here since it will lead to a troubleshooting session.<\/p>\n<p>First, if you specified the <tt>\/DLL<\/tt> flag, then the default entry point is <tt>_DllMainCRTStartup<\/tt>. That&#8217;s the easy case.<\/p>\n<p>If you did not specify the <tt>\/DLL<\/tt> flag, then the linker looks at your <tt>\/SUBSYSTEM<\/tt> flag. If you asked for <tt>\/SUBSYSTEM:<wbr \/>CONSOLE<\/tt>, then it looks for <code>wmain<\/code> and <code>main<\/code>. If you asked for <tt>\/SUBSYSTEM:<wbr \/>WINDOWS<\/tt>, then it looks for <code>wWinMain<\/code> and <code>WinMain<\/code>. If you didn&#8217;t specify a subsystem, then it looks for all four symbols, and whichever symbol it finds first determines what the implied <tt>\/ENTRY<\/tt> is:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>If linker finds<\/td>\n<td>Then entry point is<\/td>\n<\/tr>\n<tr>\n<td><tt>wmain<\/tt><\/td>\n<td><tt>wmainCRTStartup<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>main<\/tt><\/td>\n<td><tt>mainCRTStartup<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>wWinMain<\/tt><\/td>\n<td><tt>wWinMainCRTStartup<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>WinMain<\/tt><\/td>\n<td><tt>WinMainCRTStartup<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The search for these magic symbols is made over the symbols present in the module after external references have been resolved (according to <a title=\"Understanding the classical model for linking, groundwork: The algorithm\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130107-00\/?p=5633\"> the classical model for linking<\/a>).\u00b9 If there is no match for anything, then it uses the ANSI entry point and hopes for the best.<\/p>\n<p>Okay, now you can try to solve this customer&#8217;s problem:<\/p>\n<blockquote class=\"q\"><p>We have several programs that follow the same overall structure. The idea is that we will put the <code>wWinMain<\/code> function in a common static library, and the <code>wWinMain<\/code> function will use global variables (defined differently in each program) to manage the program-specific business logic. But when we do that, we get a linker error saying &#8220;unresolved external symbol WinMain referenced by invoke_main.&#8221; It works if we use <code>WinMain<\/code>, but we want to support Unicode.<\/p><\/blockquote>\n<p>Moving the <code>wWinMain<\/code> function into a library means that the linker doesn&#8217;t see it when looking through your module&#8217;s symbols trying to find an entry point. It therefore assumes ANSI and then gets stuck when it can&#8217;t find <code>WinMain<\/code>.<\/p>\n<p>One way to solve this is to require clients of the library to define their own <code>wWinMain<\/code>, but have it just forward the call to your library.<\/p>\n<pre>int WINAPI wWinMain(HINSTANCE hinst,\r\n    HINSTANCE hinstPrev,\r\n    PWSTR pszCmdLine,\r\n    int nCmdShow)\r\n{\r\n    return Contoso::wWinMain(hinst, hinstPrev,\r\n            pszcmdLine, nCmdShow);\r\n}\r\n<\/pre>\n<p>Now that the <code>wWinMain<\/code> function is defined in the project object files, the linker will see it and use it as the entry point.<\/p>\n<p>Having a separate function into which clients forward their <code>wWinMain<\/code> also means that it&#8217;s possible for a program to use multiple libraries.<\/p>\n<pre>int WINAPI wWinMain(HINSTANCE hinst,\r\n    HINSTANCE hinstPrev,\r\n    PWSTR pszCmdLine,\r\n    int nCmdShow)\r\n{\r\n    if (GetConfiguration(\"useContoso\")) {\r\n        return Contoso::wWinMain(hinst, hinstPrev,\r\n                pszcmdLine, nCmdShow);\r\n    } else {\r\n        return Fabrikam::wWinMain(hinst, hinstPrev,\r\n                pszcmdLine, nCmdShow);\r\n    }\r\n}\r\n<\/pre>\n<p>If you don&#8217;t really care about peaceful coexistence with other libraries, another solution would be to have a function that everybody must call, but which doesn&#8217;t do anything,\u00b2 and put that function in the same .obj file as your <code>wWinMain<\/code>. The classical model of linking will try to resolve that function, and it will pull in the .obj file from the library, and that .obj file carries <code>wWinMain<\/code> along for the ride.<\/p>\n<p>Another possibility is to just put <code>WinMain<\/code> in your library, since that&#8217;s the name that the linker will use for a <code>\/SUBSYSTEM:WINDOWS<\/code> module if it can&#8217;t find <code>wWinMain<\/code>. Your <code>WinMain<\/code> can ignore its ANSI command line and call <code>Get\u00adCommand\u00adLineW()<\/code> to get the Unicode command line.<\/p>\n<p>But probably the easiest solution if you don&#8217;t care about peaceful coexistence with other libraries is to add a<\/p>\n<pre>#pragma comment(linker, \"\/include:wWinMain\")\r\n<\/pre>\n<p>This forces the linker to resolve <code>wWinMain<\/code>, and it will find it in your library and add its object file to your project, at which point the &#8220;Gosh, what entry point should I use?&#8221; will see that <code>wWinMain<\/code> is present and use it.<\/p>\n<p>\u00b9 Naturally, this is done prior to dead code removal: Without an entry point, all you have is dead code!<\/p>\n<p>\u00b2 Basically, you&#8217;re using the <a title=\"If InitCommonControls doesn't do anything, why do you have to call it?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050718-16\/?p=34913\"> InitCommonControls trick<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you don&#8217;t tell it, it will try to figure it out.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110338","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>If you don&#8217;t tell it, it will try to figure it out.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110338"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110338\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110338"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110338"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}