Thread overview
BetterC Name Mangling Linker Errors
Jul 27, 2022
MyNameHere
Jul 27, 2022
Dennis
Jul 27, 2022
MyNameHere
July 27, 2022

I have included the source to a simple 64-bit Windows program.
It compiles and runs fine with dmd -m64 -L="/Subsystem:Windows" -L="/Entry:Main" Main.d. But compiling with -betterC using the following throws up a linker error, dmd -m64 -betterC -L="/Subsystem:Windows" -L="/Entry:Main" Main.d:

Main.obj : error LNK2019: unresolved external symbol _D4core3sys7windows7winuser11WNDCLASSEXA6__initZ referenced in function Main

And I'm not sure why.

import core.sys.windows.winuser;
import core.sys.windows.winbase;

pragma(lib, "User32.lib");
pragma(lib, "Kernel32.lib");

extern(Windows)
void Main(void* Instance)
{
    WNDCLASSEXA WindowClass;
    with (WindowClass)
    {
        cbSize        = WindowClass.sizeof;
        lpfnWndProc   = &WindowProc;
        hInstance     = Instance;
        hCursor       = LoadCursor(null, IDC_ARROW);
        lpszClassName = "Window";
    }
    RegisterClassExA(&WindowClass);
    void *WindowHandle = CreateWindowExA(0,
                                         "Window",
                                         "Window",
                                         WS_OVERLAPPEDWINDOW,
                                         CW_USEDEFAULT,
                                         CW_USEDEFAULT,
                                         CW_USEDEFAULT,
                                         CW_USEDEFAULT,
                                         null,
                                         null,
                                         Instance,
                                         null);
    ShowWindow(WindowHandle, SW_MAXIMIZE);

    MSG Message;
    while (GetMessage(&Message, null, 0, 0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
}

extern(Windows)
long WindowProc(void* WindowHandle, uint Message, ulong WParam, long LParam) nothrow @system
{
    if (Message == WM_DESTROY) ExitProcess(0);

    return DefWindowProcA(WindowHandle, Message, WParam, LParam);
}
July 27, 2022

On Wednesday, 27 July 2022 at 12:26:59 UTC, MyNameHere wrote:

>
void Main(void* Instance)
{
    WNDCLASSEXA WindowClass;

This is equivalent to WNDCLASSEXA WindowClass = WNDCLASSEXA.init;

If the struct's fields all initialize to 0, the compiler would simply set the variable's bytes to 0, but the definition in druntime gives fields with non-zero default value:

struct WNDCLASSEXA {
    UINT      cbSize = WNDCLASSEXA.sizeof; // <-- non zero init
    UINT      style;
    WNDPROC   lpfnWndProc;
    int       cbClsExtra;
    int       cbWndExtra;
    HINSTANCE hInstance;
    HICON     hIcon;
    HCURSOR   hCursor;
    HBRUSH    hbrBackground;
    LPCSTR    lpszMenuName;
    LPCSTR    lpszClassName;
    HICON     hIconSm;
}

Because of this, the compiler defines an 'init symbol' in druntime that gets copied into your variable to initialize it. Because druntime isn't linked when using BetterC, the linker fails to find the init symbol.

I think removing the default initialization will fix it:

WNDCLASSEXA WindowClass = void;
July 27, 2022

Thank you, that seems to have been the source of the error.