这种接口指针是通过虚表指针成员来获得函数地址,没法通过接口类型在编译时刻获取具体的偏移量。
如果编译为 C 代码,就好办多了,可以先通过虚表结构体轻松获得函数基于虚表指针的偏移量。
在运行时刻,先通过接口地址得到虚表指针的地址,再通过虚表的得到接口函数的地址。
首先,最好是使用微软提供的宏将接口声明改成 C/C++ 通用的声明语句,放在公共头文件。如:
#undef INTERFACE
#define INTERFACE ISample
DECLARE_INTERFACE_(ISample, IUnknown)
{
BEGIN_INTERFACE
// *** IUnknown methods ***
STDMETHOD (QueryInterface)(THIS_ REFIID riid, void **ppv) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
// *** ISample methods ***
STDMETHOD (MethodWithSingleParam)(THIS_ LONG nArg) PURE;
STDMETHOD (MethodWithoutParam)(THIS) PURE;
END_INTERFACE
};
这里补充一下,如果是微软的头文件就没这个问题,如果是其他的建议修改一下。
将基于虚表地址的静态偏移量放在另外一个 c 文件中,通过 extern 导出偏移量给 C++ 调用。
size_t nMethodOffset0 = offsetof(ISampleVtbl, QueryInterface);
size_t nMethodOffset1 = offsetof(ISampleVtbl, AddRef);
size_t nMethodOffset2 = offsetof(ISampleVtbl, Release);
size_t nMethodOffset3 = offsetof(ISampleVtbl, MethodWithSingleParam);
size_t nMethodOffset4 = offsetof(ISampleVtbl, MethodWithoutParam);
#if defined(__cplusplus)
extern "C" {
#endif
LPBYTE GetSampleVtblAddress(LPCVOID pv)
{
return (LPBYTE)*(ISampleVtbl **)((LPBYTE)pv + offsetof(ISample, ISampleVtbl));
}
#if defined(__cplusplus)
}
#endif
或者直接用 C 语法来写这部分功能更省事。
C++ 调用的时候,可以通过接口指针得到虚表地址,再通过虚表地址得到函数地址:
ISample *ps;
//
// 通过 union 将指针 ps 转换成标准的指针 pv,略去。
//
LPBYTE pVtbl = GetSampleVtblAddress(pv);
LPVOID pOffset0 = (LPVOID)(lpVtbl + nMethodOffset0);
LPVOID pOffset1 = (LPVOID)(lpVtbl + nMethodOffset1);
LPVOID pOffset2 = (LPVOID)(lpVtbl + nMethodOffset2);
LPVOID pOffset3 = (LPVOID)(lpVtbl + nMethodOffset3);
LPVOID pOffset4 = (LPVOID)(lpVtbl + nMethodOffset4);
仅供参考思路,代码未实测。
最后于 2021-2-20 16:25
被Xenophon编辑
,原因: