error LNK2001: unresolved external symbol WinMainCRTStartup - NOT your usual problem

Daniel Sedory 161 Reputation points
2020-11-20T10:24:52.067+00:00

Microsoft Visual Studio Community 2019 (Version 16.8.1; now 16.8.2 or latest: 16.8.5).

LATEST UPDATE (March 1, 2021): The simple fact is: Visual Studio Community 2019 does not have a choice for building MASM projects. The closest choice is C++ and even though you can assemble and link a .ASM file that has no errors from the "x64 Native Tools Command Prompt" without any problems, when attempting to do so from the IDE after choosing any type of C++ project (Empty, Desktop; whatever), if there is no .CPP source file listed, you will get the error mentioned below! HOWEVER, you do not need that .CPP file to be part of the overall code! I just ran my example file below with no other source files except one titled EMPTY.CPP with nothing in it! (The IDE simply needs to see a .CPP file listed.)

I've also made a correction to the example program: 64-bit Assembly requires you to 1) Always have a minimum of 32 bytes "Shadow Space" on the Stack, 2) Additional space must be reserved if more than 4 parameters are passed to any Function you CALL and 3) Space must also be reserved for the Return Address used during the CALL.

[ Please NOTE: Viorel-1's answer below, although being VERY HELPFUL in at least being able to use the VS2019 Debug screens, etc. when including his .cpp file, does not answer this question:
Why won't VS2019 allow this .asm file to work by itself? (Especially since it has no issue when doing the assembly, linking and creation of an executable at the "x64 Native Tools Command Prompt".) ] Well, the answer as described above, turns out to be: BECAUSE there is no "MASM Project" choice in the IDE.

This concerns code which links and creates an executable just fine when entering the following command at the "x64 Native Tools Command Prompt":

ml64.exe asmHelloWorld.asm /link /subsystem:windows /entry:main

HOWEVER, when attempting to Build or Debug the code in Visual Studio 2019 (be sure to use x64; this is not 32-bit code!), I always get this (no matter what I've tried from reading answers here):

Build started...
1>------ Build started: Project: asmHelloWorld, Configuration: Debug x64 ------
1>LINK : error LNK2001: unresolved external symbol WinMainCRTStartup
1>C:\Users\user\source\repos\asmHelloWorld\x64\Debug\asmHelloWorld.exe : fatal error LNK1120: 1 unresolved externals
1>Done building project "asmHelloWorld.vcxproj" -- FAILED.

==========
Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

BEFORE you attempt to answer this question, PLEASE have VS2019 set up for C++ and use the "x64 Native Tools Command Prompt" and the file provided below, so you can be assured that it works!

Then try it in VS2019; as a Windows subsystem under C++ (which includes MASM), and provide an answer that actually works for you.

NOTE: I made sure to have "main" as the Entry Point in: Properties -> Linker -> Advanced, and have tried a number of variations already; such as naming the proc either "WinMainCRTStartup" or "_WinMainCRTStartup" with appropriate "end <name>" instead of "end main", and other such things... which is why I'm finally asking for help here. And yes, under Linker -> System, I have "Windows (/SUBSYSTEM:WINDOWS)". [I had also set Project -> "Build Customizations..." to: "masm(.targets, .props)", but failed to mention that the first time I posted this.]

Here's the full "asmHelloWorld.asm" file contents:

; Finally -> A Real Windows 64-bit ASSEMBLY Program!  
; (Notes are Copyright (c) 2020 by Daniel B. Sedory)  
; (You can freely use the code itself, but should  
;  acknowledge any use of my comments and notes.)  
;  
; Note: Based upon a sample by Chris Lomont; see:  
;       https://software.intel.com/en-us/articles/introduction-to-x64-assembly  
;  
; Note: Although I've included two "includelib  <LIB>" statements  
;       here for kernel32.lib and User32.lib;  these would not be  
;       necessary when using the two "/defaultlib:"  command-line  
;       arguments (see "INSTRUCTIONS ..." much further below)!  
  
includelib kernel32.lib  
includelib User32.lib  
  
; These two External Functions are found  
; in the kernel32 and User32 Libraries:  
  
EXTERN MessageBoxA: proc  
EXTERN ExitProcess: proc  
  
.data  
  
winCapt db 'Simple "Hello World" Program - in x64 Assembly code!', 0  
winText db 'Only 64-bit Assembly _source code_ was used to make ...', 0dh,0ah  
        db 'this "Hello World!" (Windows 10 compatible) executable!', 0dh,0ah,0dh,0ah  
        db '(Edited, Assembled and Linked by Daniel B. Sedory using', 0dh,0ah  
        db ' only: Notepad.exe, ml64.exe, link.exe, mspdbcore.dll,', 0dh,0ah  
        db " tbbmalloc.dll, kernel32.lib and User32.lib.)", 0  
  
.code  
  
; Note: The Windows Linker in VS2019 needs to know the name of the main process!  
; We avoid having to set that when doing command-line assembly & linking  
; with the additional parameter:  /entry:main  
; (So set the "Entry" in the PROPERTIES -> Linker -> Advanced area!)  
  
main proc  
  
  sub rsp, 28h  ; Reserve required "Shadow Space" on Stack (32 bytes), plus 8 bytes  
                ; more for the Call's Return ADDRESS to keep Stack 16-byte aligned by  
                ; 'subtracting' 0x28 = 40 bytes from the 64-bit Stack Pointer (RSP).  
  
; Set up the parameters for calling the "MessageBoxA" Function (see:  
; https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-messageboxa  
; for more information on all the available parameters you could use):   
  
  mov rcx, 0 ; hWnd = HWND_DESKTOP  
  
  lea rdx, winText ; The main window text; often referred to as: LPCTSTR lpText  
  lea r8,  winCapt ;   The window caption; often referred to as: LPCTSTR lpCapt  
  
  mov r9d, 40h ; uType = MB_OK; "d" means only lower 32-bits (double-word)  
               ; are required for this parameter, so "r9d" instead of "r9"  
               ; (Note: I made this 40h instead of 0 to add "Info" icon!)  
  
  call MessageBoxA ; Call Windows API Function "MessageBoxA" (in User32.Lib).  
  
  mov ecx, eax ; EAX contains the Exit Code from MessageBox Function which  
               ; we pass to the ExitProcess (from Kernel32.lib).   
  
  call ExitProcess ; Call Windows API Function "ExitProcess"; no values returned.  
  
main endp  
      
end ; This "END Directive" is required at end of file  
    ;     when Assembling & Linking at Command Prompt.  
  
  
; INSTRUCTIONS FOR ASSEMBLING & LINKING:  
;  
; Either place the following 6 files in the same folder as our .asm  
;  
; (Or: Download and install a version of  Visual Studio 2019,  then  
;  start typing "x64" into the Windows 10 Search Box until you see:  
;  
;  _x64 Native Tools Command Prompt_ and select that;  which places  
;  all the necessary tools in your pathway.)  
;  
; Directory of your "WinHelloWorld" folder/directory, must contain  
; the following files _if_ they are _not_ in the PATH:  
;  
; From:  
; C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\...  
; Tools\MSVC\14.28.29333\bin\Hostx64\x64\  
;  
;   11/16/2020  03:06 PM           601,520 ml64.exe  
;   11/16/2020  03:06 PM         2,194,864 link.exe  
;   11/16/2020  03:06 PM           740,736 mspdbcore.dll  
;   11/16/2020  03:06 PM           260,488 tbbmalloc.dll  
;  
; C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\um\x64\  
;  
;   10/23/2018  12:09 AM           299,116 kernel32.Lib  
;   10/23/2018  12:10 AM           162,112 User32.Lib  
;  
;  
; Then to both assemble and link the code above at the  
; same time, just execute this at the command prompt:  
;  
; ml64.exe asmHelloWorld.asm /link /subsystem:windows /entry:main  
;  
;  
; Output SHOULD appear as follows (if any errors, make sure  
; all the necessary files are in your pathway!):  
;  
;  
; Microsoft (R) Macro Assembler (x64) Version 14.28.29333.0  
; Copyright (C) Microsoft Corporation.  All rights reserved.  
;  
; Assembling: asmHelloWorld.asm  
; Microsoft (R) Incremental Linker Version 14.28.29333.0  
; Copyright (C) Microsoft Corporation.  All rights reserved.  
;  
; /OUT:asmHelloWorld.exe  
; asmHelloWorld.obj  
; /subsystem:windows  
; /defaultlib:kernel32.lib  
; /defaultlib:User32.lib  
; /entry:main  
;  
; NOTE: The first time you execute the command line provided  
;       above, you will find 3 new files in the directory:  
;  
;   11/20/2020  02:34 PM          2,560 asmHelloWorld.exe  
;   11/20/2020  02:34 PM          1,054 asmHelloWorld.obj  
;   11/20/2020  02:34 PM            254 mllink$.lnk  
;  
;  
Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
4,616 questions
0 comments No comments
{count} votes

Accepted answer
  1. Daniel Sedory 161 Reputation points
    2020-11-20T21:41:52.467+00:00

    Thank you Viorel-1 for your answer, but it did not allow the .asm file to run by itself!

    NOTE: As I wrote in my UPDATE to the question, the answer is simply that VS2019 Community has no choice for a MASM Project by itself, so you MUST include an empty .CPP file (or the one you provided us with for calling an assembly program; but no other files are necessary). [2021/03/01]

    I should have mentioned that I'd already set the Build to “masm(.targets, .props)” as you indicated.
    Also, it didn't matter whether I left the ‘add rsp, 28h’ instruction in or not, however I did remove it from my final code, since it appears that the Windows code takes care of alignment afterwards without it; so not necessary (Chris Lomont didn't have it in his sample code either).

    As implied above, the code ran fine; provided I included your modified .cpp file.
    HOWEVER, without it, VS2019 spits out the same exact LINK error: "LNK2001: unresolved external symbol wWinMainCRTStartup"; no matter what I use for the "proc" name! I did just as you suggested; using: "wWinMain proc" and "wWinMain endp"; same error whether I used that, or any other name, as the Entry Point in: Properties -> Linker -> Advanced, OR if I left that blank!

    All I can guess at this time is: For some strange reason, VS2019 will not allow an Assembly program by itself to use WINDOW display code without a .cpp file... IN SPITE OF THE FACT THAT MANY OTHER solutions/projects [beginning with "Windows (/SUBSYSTEM:WINDOWS)" and C++ Desktop, etc.] RUN JUST FINE without any .cpp files (see further below)...

    NOT TO MENTION AS I POINTED OUT IN MY FIRST POST: The code assembles, links and completes WITH NO ERRORS when doing so from the "x64 Native Tools Command Prompt"!!

    As an example of the very simple kind of .asm code that runs OK in VS2019 by itself, see:
    setup-an-assembly-project-on-visual-studio-2019

    And to be sure I didn't miss anything that was set up there, I copied my code into that .asm file, and still got the same LINK error under that project setup.

    So what is the final answer here? WHY does my code work fine by itself at the "x64 Native Tools Command Prompt," but not in VS2019 IDE (even with various modifications; such as changing the proc name to "wWinMain")?

    Although I'm still searching for answers elsewhere, if you read through all of the following link, it appears that Microsoft's Visual Studio 2019 does NOT address the use of any Assembly code without having a .CPP file associated with it, like the one Viorel-1 provided in his answer!
    masm-for-x64-ml64-exe


1 additional answer

Sort by: Most helpful
  1. Viorel 112.4K Reputation points
    2020-11-20T13:10:11.007+00:00

    It seems to work in this case:

    • Create a new project in Visual Studio, selecting “Windows Desktop Application”. This will set the required options and also add sample code.
    • Select the project in Solution Explorer, go to Project menu, “Build Customisations” and select “masm(.targets, .props)”.
    • Add the existing .asm file to project.
    • Comment the premature ‘add rsp, 28h’ instruction, because there is code to execute after this line.
    • Go to the generated .cpp file, remove the sample code and only keep this:

    .

    #include "framework.h"
    
    extern "C" void __cdecl main( );
    
    int APIENTRY wWinMain( _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPWSTR    lpCmdLine,
        _In_ int       nCmdShow )
    {
     main( );
    }
    
    • Select the “Debug”, “x64” configuration and try executing the code.

    If it works, you can then remove the .cpp code and rename your main procedure to wWinMain.

    If you want to restart from an empty solution, you can get the required compiler and linker option from the current project.

    0 comments No comments