Search code examples
winapimemory-managementgarbage-collectiond

Garbage Collector Crashing when using WinAPI Functions


In D, my garbage collector is crashing every time I launch my application.

Windows Module:

pragma(lib, "user32.lib");

import std.string;

extern(Windows) {
    void* CreateWindowExW(uint extendedStyle , 
                          const char* classname,
                          const char* title,
                          uint style,
                          int x, int y,
                          int width, int height,
                          void* parentHandle,
                          void* menuHandle,
                          void* moduleInstance, 
                          void* lParam);
}

class Window {
    private void* handle;
    private string title;

    this(string title, const int x, const int y, const int width, const int height) {
        this.title = title;
        handle = CreateWindowExW(0, null, toStringz(this.title), 0, x, y, width, height, null, null, null, null);

        if(handle == null)
            throw new Exception("Error while creating Window (WinAPI)");
    }
}

Main Module:

import std.stdio;

version(Windows) {
    import windows;
    extern (Windows) {
    int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
        import core.runtime;

        Runtime.initialize();
        scope(exit) Runtime.terminate();
        auto window = new Window("Hello", 0, 0, 0, 0);
        writeln("test");
        return 0;
    }
    }
}

This gives me an Access Violation in location 0. When I view the dissassembly, it's crashing in

0040986F  mov         ecx,dword ptr [eax]  

This assembly is located inside _gc_malloc.


EDIT: Here is the new code:

Windows Module:

pragma(lib, "user32.lib");

import std.utf;

extern(Windows) {
    void* CreateWindowExW(uint extendedStyle , 
                          const wchar* classname,
                          const wchar* title,
                          uint style,
                          int x, int y,
                          int width, int height,
                          void* parentHandle,
                          void* menuHandle,
                          void* moduleInstance, 
                          void* lParam);
}

class Window {
    private void* handle;
    private wstring title;

    this(wstring title, const int x, const int y, const int width, const int height) {
        this.title = title;
        handle = CreateWindowExW(0, null, toUTFz!(wchar*)(this.title), 0, x, y, width, height, null, null, null, null);

        if(handle == null)
            throw new Exception("Error while creating Window (WinAPI)");
    }
}

WinMain:

    int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
        import core.runtime;

        try {
            Runtime.initialize();
            scope(exit) Runtime.terminate();
            auto window = new Window("Hello", 0, 0, 0, 0);
            writeln("test");
        } catch(Exception ex) {
            writeln(ex.toString);
        }
        return 0;
    }

When I run this second code, I also get an Access Violation, on a random (to me) address.

Dissasembly (inside __d_createTrace):

0040C665  cmp         dword ptr [ecx+1Ch],0  

Solution

  • David Heffernan's post has good information, but won't fix the main problem here, which is that the D runtime is not initialized. You code should throw an exception, your arguments to create window are wrong, but it should not be an access violation.

    The easiest way to solve this is to define a regular main function instead of WinMain. WinMains are valid in D, but if you define your own, it skips the druntime initialization functions. (The runtime, src/druntime/src/rt/main2.d if you're interested) define a C main function which does setup tasks, then calls your D main function. The C main btw is, in turn, called from WinMain by the C runtime.

    If you need the arguments for the instance or command line, you can get with with the Windows API function GetModuleHandle(null) and GetCommandLineW().

    Alternatively, you can initialize the runtime yourself in your WinMain function:

    extern (Windows) {
       int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int   nCmdShow) {
        import core.runtime; // the Runtime functions are in here
        Runtime.initialize(); // initialize it!
            auto window = new Window("Hello", 0, 0, 0, 0);
            return 0;
        }
    }
    

    The other thing you should do is terminate it and catch exceptions. An uncaught exception on Windows by default will trigger the system debugger, so it isn't all bad, but typically in D programs, you expect a nicer message. So do something like this:

    int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
        import core.runtime;
        Runtime.initialize();
        scope(exit) Runtime.terminate();
        try
            auto window = new Window("Hello", 0, 0, 100, 100);
        catch(Throwable t)
            MessageBoxW(null, toUTFz!(wchar*)(t.toString()), null, 0);
        return 0;
    }
    

    So I initialized it there, terminated when the function returned, and also caught the exception and put it in a message box for easier reading. I put a prototype to MessageBoxW in myself, just like you did for CreateWindow. You can also fetch more complete win32 bindings here http://www.dsource.org/projects/bindings/browser/trunk/win32 (I think that's the up to date link anyway.)

    Again though, you can also just use a D main function which does this kind of thing for you. WinMain gives more control but isn't required by D.