3

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  
1
  • 2
    Runtime.initialize and the scope exit runtime.terminate should both be outside the try, put them at the very top of the function, then try your code. What's happening now is the new Window throws an exception (like David said, you'll need to register a window class first instead of passing null), then at the end of try, the runtime terminates. So inside catch, the stuff isn't set up and the toString call can't do its work. So move those two Runtime lines above try and you should get a nice exception message box. Commented Dec 16, 2013 at 18:01

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I now simply get an access violation in the dissasembly of "leave".
5

CreateWindowExW takes 16-bit strings rather than 8 bit strings. I'm not sure how to achieve that in D. I assume that char is 8 bit as it is in C++ on Windows? You could use CreateWindowExA I suppose, but it would be better to pass 16-bit UTF-16 text.

It must be an error to pass null for the lpClassName parameter. A window does need a window class.

3 Comments

Yeah, the type should be wchar instead of char. If you put a w prefix at the end of a string literal, e.g. "myclassname"w, it will work as a wchar*. Converting non-literals to wchar is done with std.utf.toUTFz!wstring(your_data);
@AdamD.Ruppe Thanks! I don't know any of this stuff in the context of D!
I just tried it and apparently wstrings don't implicitly convert to pointers, so either do the toUTFz onthem too, or use the ptr property, "myclassname"w.ptr. (I think this might be a compiler oversight, since utf-8 string literals do implicitly convert to char*. D strings aren't necessarily zero terminated so the implicit conversion is bad in general, but since literals are a special case it is ok there, all string literals get a zero terminator for easy C interoperability.) EDIT: also apparently it needs to be toUTFz!(wchar*) rather than wstring. Weird, I thought wstring worked...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.