Есть ли способ создать элегантную оконную функцию члена класса?
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 ответа
Здесь я сделал это для вас (основываясь на ответе БКС):
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.
Одним из очень непереносимых решений было бы динамическое создание функции, которая упаковывает вызов. Я бы сделал это, написав функцию, которая выглядит следующим образом:
extern(C) RetType TestFn(Arg arg /* and any others */) {
Class c = cast(Class)(0xDEAD_BEEF);
return c.Method(arg);
}
Затем вы можете скомпилировать эту функцию как неоптимизированную PIC, декомпилировать ее и найти последовательность байтов, которая может быть преобразована в то, что вам нужно. Конечным результатом будет тип (вероятно, структура) с метудом, возвращающим указатель на функцию, который при создании заполняет внутренний void
массив с байтами, которые вы нашли на предыдущем шаге и помещает рассматриваемый объект в соответствующие места.
Немного более продвинутое решение заполнило бы делегат и указателем объекта и методом, таким образом, оба могут быть предоставлены конструктору. Еще более продвинутое решение будет шаблонизировать тип и использовать знания соглашений о вызовах C и D для динамической генерации кода пересылки аргументов.