Есть ли способ создать элегантную оконную функцию члена класса? - PullRequest
4 голосов
/ 27 декабря 2011

Window-процедура в 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);
    }
}

(без регистрации \ создания \ msg-loop ...)

Кажется, что события () не запускаются ... я что-то упустил?

Ответы [ 3 ]

3 голосов
/ 28 декабря 2011

Здесь я сделал это для вас (основываясь на ответе БКС):

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

Протестировано на 32-битной Windows и 64-битной Linux.

1 голос
/ 28 декабря 2011

Как насчет сохранения this в самом окне, с SetWindowLong ?

1 голос
/ 28 декабря 2011

Одним из очень непереносимых решений было бы динамическое создание функции, которая упаковывает вызов. Я бы сделал это, написав функцию, которая выглядит следующим образом:

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

Затем вы можете скомпилировать эту функцию как неоптимизированную PIC , декомпилировать ее и найти последовательность байтов, которая может быть преобразована в то, что вам нужно. Конечным результатом будет тип (вероятно, структура) с метудом, возвращающим указатель функции, который при построении заполняет внутренний массив void байтами, найденными на предыдущем шаге, и помещает соответствующий объект в соответствующие места.

A немного более продвинутое решение заполнило бы делегат как указателем объекта, так и указателем на метод, так что оба могут быть предоставлены конструктору. Еще более продвинутое решение будет шаблонизировать тип и использовать знания соглашений о вызовах C и D для динамической генерации кода пересылки аргументов.

...