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

使用WinAPI函数时垃圾收集器崩溃,winapi,memory-management,garbage-collection,d,Winapi,Memory Management,Garbage Collection,D,在D中,每次启动应用程序时,我的垃圾收集器都会崩溃 Windows模块: pragma(lib, "user32.lib"); import std.string; extern(Windows) { void* CreateWindowExW(uint extendedStyle , const char* classname, const char* title,

在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)");
    }
}
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)");
    }
}
主要模块:

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;
    }
    }
}
这使我在位置0中遇到访问冲突。当我看到disassembly时,它正在崩溃

0040986F  mov         ecx,dword ptr [eax]  
此组件位于
\u gc\u malloc
内部


编辑:以下是新代码:

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)");
    }
}
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)");
    }
}
温曼:

    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;
    }
当我运行第二个代码时,我也会在一个随机的(给我的)地址上遇到访问冲突

Dissasembly(在
\uu d\u createTrace
内):


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


lpClassName
参数传递
null
必须是错误的。窗口确实需要一个窗口类。

David Heffernan的文章提供了很好的信息,但无法解决这里的主要问题,即D运行时没有初始化。您的代码应该抛出一个异常,创建窗口的参数是错误的,但不应该是访问冲突

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

如果需要实例或命令行的参数,可以使用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;
    }
}
您应该做的另一件事是终止它并捕获异常。默认情况下,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;
}
所以我在那里初始化了它,在函数返回时终止,并且捕获了异常并将其放在消息框中以便于阅读。我把MessageBoxW的原型放在了我自己身上,就像你为CreateWindow做的一样。您还可以在这里获取更完整的win32绑定(我认为这是最新的链接)


不过,您也可以使用一个D main函数来完成这类工作。WinMain提供了更多的控制,但D不需要它。

是的,类型应该是
wchar
,而不是
char
。如果将
w
前缀放在字符串文字的末尾,例如
“myclassname”w
,它将用作wchar*。使用
std.utf.toUTFz!将非文本转换为wchar!wstring(您的_数据)@AdamD.Ruppe谢谢!我不知道任何关于D的东西!我只是尝试了一下,显然wstring不会隐式地转换为指针,所以也可以对它们执行toUTFz,或者使用ptr属性,
“myclassname”w.ptr
。(我认为这可能是编译器的疏忽,因为utf-8字符串文字隐式转换为char*.D字符串不一定以零结尾,因此隐式转换通常是不好的,但由于文字是一种特殊情况,所以在这里没有问题,所有字符串文字都有一个零结尾,便于C的互操作性。)编辑:显然它也需要
toUTFz!(wchar*)
而不是wstring。奇怪的是,我认为wstring工作了……我现在只是在“leave.Runtime.initialize”和“scope exit Runtime.terminate”的dissasembly中得到了一个访问冲突,它们都应该在try之外,将它们放在函数的最顶端,然后尝试您的代码。现在的情况是,新窗口抛出一个异常(如David所说,您需要首先注册一个窗口类,而不是传递null),然后在try结束时,运行时终止。因此,在catch内部,东西没有设置,toString调用也无法完成它的工作。因此,将这两行运行时代码移到try上面,您应该会得到一个很好的异常消息框。