垃圾收集器使用WinAPI函数时崩溃

Jer*_*oen 3 winapi garbage-collection memory-management d

在D中,每次启动应用程序时,我的垃圾收集器都会崩溃.

Windows模块:

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)");
    }
}
Run Code Online (Sandbox Code Playgroud)

主要模块:

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;
    }
    }
}
Run Code Online (Sandbox Code Playgroud)

这给了我位置0的访问冲突.当我查看反汇编时,它正在崩溃

0040986F  mov         ecx,dword ptr [eax]  
Run Code Online (Sandbox Code Playgroud)

该组件位于内部_gc_malloc.


编辑:这是新代码:

Windows模块:

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)");
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
Run Code Online (Sandbox Code Playgroud)

当我运行第二个代码时,我也会在随机(对我)地址上获得访问冲突.

消散(内部__d_createTrace):

0040C665  cmp         dword ptr [ecx+1Ch],0  
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

CreateWindowExW采用16位字符串而不是8位字符串.我不确定如何在D中实现这一点.我假设它char是8位,就像在Windows上的C++一样?您可以使用CreateWindowExA我想,但最好传递16位UTF-16文本.

它必须是一个错误传递nulllpClassName参数.窗口确实需要窗口类.

  • 是的,类型应该是`wchar`而不是`char`.如果你在字符串文字的末尾添加一个`w`前缀,例如``myclassname"w`,它将作为wchar*.使用`std.utf.toUTFz!wstring(your_data);将非文字转换为wchar. (2认同)
  • 我只是尝试了它,显然wstrings不会隐式转换为指针,所以要么也可以使用toUTFz,或者使用ptr属性,`"myclassname"w.ptr`.(我认为这可能是编译器的疏忽,因为utf-8字符串文字*do*隐式转换为char*.D字符串不一定是零终止,因此隐式转换通常是坏的,但由于文字是特殊情况它没关系,所有的字符串文字都得到一个零终止符,以便于C互操作.)编辑:显然它需要是`toUTFz!(wchar*)`而不是wstring.很奇怪,我觉得wstring有效...... (2认同)

Ada*_*ppe 5

David Heffernan的帖子有很好的信息,但不会解决这里的主要问题,即D运行时没有初始化.您的代码应该抛出异常,您创建窗口的参数是错误的,但它应该是访问冲突.

解决此问题的最简单方法是定义常规main函数而不是WinMain.WinMains在D中有效,但是如果你定义了自己的,它会跳过druntime初始化函数.(如果你感兴趣,运行时,src/druntime/src/rt/main2.d)定义一个C main函数,它执行设置任务,然后调用你的D main函数.反过来,C运行时由WinMain调用C main btw.

如果需要实例或命令行的参数,则可以使用Windows API函数GetModuleHandle(null)和GetCommandLineW().

或者,您可以在WinMain函数中自己初始化运行时:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

你应该做的另一件事是终止它并捕获异常.默认情况下,Windows上未被捕获的异常将触发系统调试器,因此并非一切都坏,但通常在D程序中,您期望更好的消息.所以做这样的事情:

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;
}
Run Code Online (Sandbox Code Playgroud)

所以我在那里初始化它,在函数返回时终止,并且还捕获异常并将其放在消息框中以便于阅读.我把自己的原型放到MessageBoxW中,就像你为CreateWindow做的那样.您还可以在这里获取更完整的win32绑定http://www.dsource.org/projects/bindings/browser/trunk/win32(我认为这是最新的链接.)

尽管如此,您还可以使用D main函数为您执行此类操作.WinMain提供更多控制但D不需要.