有没有办法创建一个优雅的类成员窗口函数?

发布于 2024-12-22 22:25:55 字数 783 浏览 2 评论 0原文

Win32 API 中的窗口过程必须是静态\全局函数,因为它不能采用类对象(this)参数。当然可以使用诸如 hWnd->object 字典之类的解决方法。

我想知道 D 是否有一种方法可以优雅地解决这个问题,例如为每个对象创建一个微小的成员函数副本(以调用对象的真实处理程序)或我可以分配给 WNDCLASS 的匿名函数。 lpfnWndProc (我知道有匿名函数,但我不能在它们上使用 extern(Windows) 属性)?

我可以做这样的事情吗:(


class Window {
    extern (Windows)
    LRESULT delegate (HWND hWnd, UINT msg, WPARAM w, LPARAM l) MyWinProcDelegate;

    this() {
        MyWinProcDelegate = &Events;
    }

    extern (Windows)
    LRESULT Events (HWND hWnd, UINT msg, WPARAM w, LPARAM l) {
        MessageBoxA(null , "Success!!!" , null ,0);
        return DefWindowProcA(hWnd, message, wParam, lParam);
    }
}

省略注册\创建\消息循环...)

Events() 似乎没有触发...我错过了什么吗?

The Window-Procedure in the Win32 API must be static \ global function since it cannot take a class-object (the this) parameter. One can of-course use workarounds like a hWnd->object dictionary and such.

I wonder if D has a way to elegantly solve it, like create a tiny member function copy for each object (to call the object's real handler) or anonymous function that I can assign to WNDCLASS.lpfnWndProc (I know there are anonymous functions, but I cannot use the extern(Windows) property on them) ?

Can I do something like this :


class Window {
    extern (Windows)
    LRESULT delegate (HWND hWnd, UINT msg, WPARAM w, LPARAM l) MyWinProcDelegate;

    this() {
        MyWinProcDelegate = &Events;
    }

    extern (Windows)
    LRESULT Events (HWND hWnd, UINT msg, WPARAM w, LPARAM l) {
        MessageBoxA(null , "Success!!!" , null ,0);
        return DefWindowProcA(hWnd, message, wParam, lParam);
    }
}

(Omitting the registration\creation\msg-loop...)

The Events() doesn't seem to fire... am I missing something ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

緦唸λ蓇 2024-12-29 22:25:55

我在这里为您做了这个(基于 BCS 的答案):

version (Windows)
{
    import std.c.windows.windows;

    void makeExecutable(ubyte[] code)
    {
        DWORD old;
        VirtualProtect(code.ptr, code.length, PAGE_EXECUTE_READWRITE, &old);
    }
}
else
version (linux)
{
    import core.sys.posix.sys.mman;
    import core.sys.posix.unistd;

    static if (!is(typeof(&mprotect)))
        extern(C) int mprotect(void*, size_t, int);

    void makeExecutable(ubyte[] code)
    {
        auto pageSize = sysconf(_SC_PAGE_SIZE);
        auto address = ((cast(size_t)code.ptr) & ~(pageSize-1));
        int pageCount =
            (address/pageSize == (address+code.length)/pageSize) ? 1 : 2;
        mprotect(cast(void*)address, pageSize * pageCount,
            PROT_READ | PROT_WRITE | PROT_EXEC);
    }
}
else
    static assert(0, "TODO");

R function(A) delegate2function(R, A...)(R delegate(A) d)
{
    enum size_t TEMPLATE1 = cast(size_t)0x01234567_01234567;
    enum size_t TEMPLATE2 = cast(size_t)0x89ABCDEF_89ABCDEF;

    static R functionTemplate(A args)
    {
        R delegate(A) d;
        d.ptr     = cast(typeof(d.ptr    ))TEMPLATE1;
        d.funcptr = cast(typeof(d.funcptr))TEMPLATE2;
        return d(args);
    }

    static void functionTemplateEnd() {}

    static void replaceWord(ubyte[] a, size_t from, size_t to)
    {
        foreach (i; 0..a.length - size_t.sizeof + 1)
        {
            auto p = cast(size_t*)(a.ptr + i);
            if (*p == from)
            {
                *p = to;
                return;
            }
        }
        assert(0);
    }

    auto templateStart = cast(ubyte*)&functionTemplate;
    auto templateEnd   = cast(ubyte*)&functionTemplateEnd;
    auto templateBytes = templateStart[0 .. templateEnd - templateStart];

    // must allocate type with pointers, otherwise GC won't scan it
    auto functionWords = new void*[(templateBytes.length / (void*).sizeof) + 3];
    // store context in word-aligned boundary, so the GC can find it
    functionWords[0] = d.ptr;
    functionWords[1] = d.funcptr;
    functionWords = functionWords[2..$];

    auto functionBytes = (cast(ubyte[])functionWords)[0..templateBytes.length];
    functionBytes[] = templateBytes[];

    replaceWord(functionBytes, TEMPLATE1, cast(size_t)d.ptr    );
    replaceWord(functionBytes, TEMPLATE2, cast(size_t)d.funcptr);
    makeExecutable(functionBytes);

    return cast(typeof(return)) functionBytes.ptr;
}

void main()
{
    import std.stdio;

    auto context = 42;

    void del(string s)
    {
        writeln(s);
        writeln(context);
    }

    auto f = delegate2function(&del);
    f("I am a pretty function");
}

在 Windows 32 位和 Linux 64 位上测试。

Here I made this for you (based on BCS' answer):

version (Windows)
{
    import std.c.windows.windows;

    void makeExecutable(ubyte[] code)
    {
        DWORD old;
        VirtualProtect(code.ptr, code.length, PAGE_EXECUTE_READWRITE, &old);
    }
}
else
version (linux)
{
    import core.sys.posix.sys.mman;
    import core.sys.posix.unistd;

    static if (!is(typeof(&mprotect)))
        extern(C) int mprotect(void*, size_t, int);

    void makeExecutable(ubyte[] code)
    {
        auto pageSize = sysconf(_SC_PAGE_SIZE);
        auto address = ((cast(size_t)code.ptr) & ~(pageSize-1));
        int pageCount =
            (address/pageSize == (address+code.length)/pageSize) ? 1 : 2;
        mprotect(cast(void*)address, pageSize * pageCount,
            PROT_READ | PROT_WRITE | PROT_EXEC);
    }
}
else
    static assert(0, "TODO");

R function(A) delegate2function(R, A...)(R delegate(A) d)
{
    enum size_t TEMPLATE1 = cast(size_t)0x01234567_01234567;
    enum size_t TEMPLATE2 = cast(size_t)0x89ABCDEF_89ABCDEF;

    static R functionTemplate(A args)
    {
        R delegate(A) d;
        d.ptr     = cast(typeof(d.ptr    ))TEMPLATE1;
        d.funcptr = cast(typeof(d.funcptr))TEMPLATE2;
        return d(args);
    }

    static void functionTemplateEnd() {}

    static void replaceWord(ubyte[] a, size_t from, size_t to)
    {
        foreach (i; 0..a.length - size_t.sizeof + 1)
        {
            auto p = cast(size_t*)(a.ptr + i);
            if (*p == from)
            {
                *p = to;
                return;
            }
        }
        assert(0);
    }

    auto templateStart = cast(ubyte*)&functionTemplate;
    auto templateEnd   = cast(ubyte*)&functionTemplateEnd;
    auto templateBytes = templateStart[0 .. templateEnd - templateStart];

    // must allocate type with pointers, otherwise GC won't scan it
    auto functionWords = new void*[(templateBytes.length / (void*).sizeof) + 3];
    // store context in word-aligned boundary, so the GC can find it
    functionWords[0] = d.ptr;
    functionWords[1] = d.funcptr;
    functionWords = functionWords[2..$];

    auto functionBytes = (cast(ubyte[])functionWords)[0..templateBytes.length];
    functionBytes[] = templateBytes[];

    replaceWord(functionBytes, TEMPLATE1, cast(size_t)d.ptr    );
    replaceWord(functionBytes, TEMPLATE2, cast(size_t)d.funcptr);
    makeExecutable(functionBytes);

    return cast(typeof(return)) functionBytes.ptr;
}

void main()
{
    import std.stdio;

    auto context = 42;

    void del(string s)
    {
        writeln(s);
        writeln(context);
    }

    auto f = delegate2function(&del);
    f("I am a pretty function");
}

Tested on Windows 32-bit and Linux 64-bit.

缪败 2024-12-29 22:25:55

如何将 this 存储在窗口本身中,使用 SetWindowLong

How about storing this in the window itself, with SetWindowLong?

橘寄 2024-12-29 22:25:55

一种非常不可移植的解决方案是动态创建一个包装调用的函数。我会通过编写一个如下所示的函数来做到这一点:

extern(C) RetType TestFn(Arg arg /* and any others */) {
   Class c = cast(Class)(0xDEAD_BEEF);
   return c.Method(arg);
}

然后您可以将此函数编译为未优化的 PIC,反编译它,找到一个可以混搭成你需要的字节序列。最终结果将是一个类型(可能是一个结构体),它有一个返回函数指针的方法,并且在构造时,用您在上述步骤中找到的字节填充内部 void 数组,并戳出将有问题的对象放入适当的位置。

稍微更高级的解决方案将使用对象和方法指针填充委托,以便两者都可以提供给构造函数。更高级的解决方案将模板化类型并利用 C 和 D 调用约定的知识来动态生成参数转发代码。

One very un-portable solution would be to dynamically create a function that wraps the call. I would do this by writing a function that looks like this:

extern(C) RetType TestFn(Arg arg /* and any others */) {
   Class c = cast(Class)(0xDEAD_BEEF);
   return c.Method(arg);
}

You can then compile this function as un-optimized PIC, de-compile it, and find a byte sequence that can be mashed into what you need. The end result would be a type (likely a struct) that has a methoud returning a function pointer and that, when constructed, populates an internal void array with the bytes you found from the above step and pokes the object in question into the appropriate places.

A slightly more advanced solution would populate a delegate with both the object and the method pointer so both can be provided to the constructor. An even more advanced solution would template the type and take advantage of knowledge of the C and D calling conventions to dynamically generate the argument forwarding code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文