C++ 将类成员函数转为回调函数
本帖最后由 枫叶棋语 于 2026-1-23 23:03 编辑苦于C++ 不像C#那么方便使用回调函数,迫于无奈是用thunk 技术实现将类成员函数转变为回调函数类型。
若是大家有更好的方法,也分享一下。
#pragma once
#include <windows.h>
#include <memory>
#include <cassert>
class MapleThunk {
public:
// 构造函数:初始化跳板代码
MapleThunk() {
static constexpr BYTE kThunkCode[] = {
0x48, 0x83, 0xEC, 0x28, // sub rsp, 28h
0x4C, 0x89, 0x4C, 0x24, 0x20, // mov , r9
0x4D, 0x8B, 0xC8, // mov r9, r8
0x4C, 0x8B, 0xC2, // mov r8, rdx
0x48, 0x8B, 0xD1, // mov rdx, rcx
0x48, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mov rcx,
0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mov rax,
0xFF, 0xD0, // call rax
0x48, 0x83, 0xC4, 0x28, // add rsp, 28h
0xC3 // ret
};
m_codeSize = sizeof(kThunkCode);
AllocateExecutableMemory();
// 复制原始指令模板
memcpy(m_pThunkCode, kThunkCode, m_codeSize);
// 定位关键偏移量
LocateKeyOffsets();
}
~MapleThunk() {
if (m_pThunkCode) {
VirtualFree(m_pThunkCode, 0, MEM_RELEASE);
}
}
template<typename T, typename Method>
void* SetThunkTarget(T* instance, Method method) {
static_assert(std::is_member_function_pointer_v<Method>,
"Method must be a member function pointer");
*m_pThisPtr = reinterpret_cast<ULONG_PTR>(instance);
// 使用 union 转换成员函数指针
union {
Method func;
ULONG_PTR addr;
} converter;
converter.func = method;
*m_pTargetPtr = converter.addr;
FlushInstructionCache(GetCurrentProcess(), m_pThunkCode, m_codeSize);
return m_pThunkCode;
}
// 禁用复制/移动
MapleThunk(const MapleThunk&) = delete;
MapleThunk& operator=(const MapleThunk&) = delete;
private:
BYTE* m_pThunkCode = nullptr;
size_t m_codeSize = 0;
ULONG_PTR* m_pThisPtr = nullptr; // this指针位置
ULONG_PTR* m_pTargetPtr = nullptr; // 目标函数位置
void AllocateExecutableMemory() {
m_pThunkCode = static_cast<BYTE*>(
VirtualAlloc(nullptr, m_codeSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE));
if (!m_pThunkCode) {
throw std::bad_alloc();
}
}
void LocateKeyOffsets() {
// 搜索特征码定位关键位置
for (size_t i = 0; i < m_codeSize - 9; ++i) {
if (*reinterpret_cast<WORD*>(m_pThunkCode + i) == 0xB948) { // mov rcx,
m_pThisPtr = reinterpret_cast<ULONG_PTR*>(m_pThunkCode + i + 2);
}
else if (*reinterpret_cast<WORD*>(m_pThunkCode + i) == 0xB848) { // mov rax,
m_pTargetPtr = reinterpret_cast<ULONG_PTR*>(m_pThunkCode + i + 2);
}
}
assert(m_pThisPtr && m_pTargetPtr && "Failed to locate key offsets");
}
};
使用方法:比如将python的函数注册到CAD 命令
//py函数包装器
class PyCallWrapper {
private:
MapleThunk m_thunk; //将成员函数转变为命令回调函数
py::function m_pyfun;
public:
PyCallWrapper(py::function pyfunc) :m_pyfun(pyfunc) {}
//回调钩子,执行python函数
void invoke() {
m_pyfun();
}
//转变为void(*)() 类型,才能进行命令注册
AcRxFunctionPtr GetCallback() {
return reinterpret_cast<AcRxFunctionPtr>(m_thunk.SetThunkTarget(this, &PyCallWrapper::invoke));
}
};
static const wchar_t* GroupName = L"PyArxCmd";
void addCmd(py::function pyfun, int flags) {
auto wrapper = new PyCallWrapper(pyfun);//这里不能使用智能指针,也不可以用栈变量,只能使用new ,没有为什么
auto callback = wrapper->GetCallback();
std::wstring name = py::str(pyfun.attr("__name__")).cast<std::wstring>();
const wchar_t* fn = name.c_str();
auto es = acedRegCmds->addCommand(GroupName, fn, fn, flags, callback);
}
void removeCmd(const std::wstring& funname) {
acedRegCmds->removeCmd(GroupName, funname.c_str());
}
void clearCmds() {
acedRegCmds->removeGroup(GroupName);
}
//绑定输出到python 模块
void bind_PyCommandManager(py::module_* m) {
m->def("addCmd", &addCmd, py::arg("func"),
py::arg("flags") = ACRX_CMD_TRANSPARENT,
"Register Python function as ARX command")
.def("removeCmd", &removeCmd, py::arg("name"), "Unregister ARX command")
.def("clear", &clearCmds, "Remove all registered commands");
}
牛哇,居然还要区分可执行内存和资源内存. 你有种再说一遍 发表于 2026-1-23 23:26
牛哇,居然还要区分可执行内存和资源内存.
这边卡了我好多天,确实上难度了 看不懂,给大佬顶一下
页:
[1]