これは数回述べられていることは知っていますが、私の状況は少し異なります。動的な呼び出しのメンバーメソッドC++
私はいくつかのクラスをエクスポートするサードパーティ製のDLLを持っています。残念ながら、ヘッダファイルは利用できません。 エクスポートされた関数を呼び出すことはまだ可能です。しかし、私は正しい 'this'ポインタ(RCXレジスタに渡される)を渡すことはできません。
最初にdumpbin/exportsを使用して関数名を抽出します(名前はサードパーティのライブラリと関数名が機密として変更されています)。今
4873 1308 0018B380 [email protected]@[email protected]@QEBAJXZ = [email protected]@[email protected]@QEBAJXZ (public: long __cdecl ThirdPartyNamespace::ThirdPartyClass::GetId(void)const)
、APIは私がThirdPartyNamespaceへのポインタを受け取り、私のコールバックを登録することができます:: ThirdPartyClassは(ThirdPartyClassの唯一の前方宣言があります)。私はThirdPartyNamespace :: ThirdPartyClass ::のgetId()を呼び出ししようとしていますどのようにここで
:私はステップ場合
long (ThirdPartyNamespace::ThirdPartyClass::*_pFnGetId)() const;
HMODULE hModule = GetModuleHandle("ThirdPartyDLL.dll");
*(FARPROC*)&_pFnGetId= GetProcAddress(hModule, "[email protected]@[email protected]@QEBAJXZ");
long id = (ptr->*_pFnGetId)();
すべてが正常に見えるが(つまりは - 私はThirdPartyClass :: getIDメソッドの内部で実際に取得します。しかし。このポインタは良くないptrが良いですが、デバッガで、私は手動でPTRにRCX変更した場合 - それは正常に動作します。しかし、コンパイラは、何らかの理由でptrを渡すことはありませんここで解体です。実行する前に
long id = (ptr->*_pFnGetId)();
000000005C882362 movsxd rax,dword ptr [rdi+30h]
000000005C882366 test eax,eax
000000005C882368 jne MyClass::MyCallback+223h (05C882373h)
000000005C88236A movsxd rcx,dword ptr [rdi+28h]
000000005C88236E add rcx,rsi
000000005C882371 jmp MyClass::MyCallback+240h (05C882390h)
000000005C882373 movsxd r8,dword ptr [rdi+2Ch]
000000005C882377 mov rcx,rax
000000005C88237A mov rax,qword ptr [r8+rsi]
000000005C88237E movsxd rdx,dword ptr [rax+rcx]
000000005C882382 movsxd rcx,dword ptr [rdi+28h]
000000005C882386 lea rax,[r8+rdx]
000000005C88238A add rcx,rax
000000005C88238D add rcx,rsi
000000005C882390 call qword ptr [rdi+20h]
000000005C882393 mov ebp,eax
。これらのコマンドは、rsiはThirdPartyCのオブジェクトへのポインタを含んでいますラス(すなわち、 ptr)、それをrcxに直接渡すのではなく、いくつかの算術演算が実行され、その結果、このポインタは完全に間違ったものになります。
それは非仮想関数ThirdPartyClassを呼び出す終わると、コンパイラはそれをやっている理由を私は理解していないいくつかの痕跡::のgetId():私の見解で
000000005C88237A mov rax,qword ptr [r8+rsi]
R8 0000000000000000
RSI 000000004C691AA0 // good pointer to ThirdPartyClass object
RAX 0000000008E87728 // this gets pointer to virtual functions table of ThirdPartyClass
000000005C88237E movsxd rdx,dword ptr [rax+rcx]
RAX 0000000008E87728
RCX FFFFFFFFFFFFFFFF
RDX FFFFFFFFC0F3C600
000000005C882382 movsxd rcx,dword ptr [rdi+28h]
RCX 0000000000000000
RDI 000000005C9BE690
000000005C882386 lea rax,[r8+rdx]
RAX FFFFFFFFC0F3C600
RDX FFFFFFFFC0F3C600
R8 0000000000000000
000000005C88238A add rcx,rax
RAX FFFFFFFFC0F3C600
RCX FFFFFFFFC0F3C600
000000005C88238D add rcx,rsi
RCX 000000000D5CE0A0
RSI 000000004C691AA0
000000005C882390 call qword ptr [rdi+20h]
、それはのように単純でなければなりません
long id = (ptr->*_pFnGetId)();
mov rcx,rsi
call qword ptr [rdi+20h]
mov ebp,eax
私はコールQWORD ptrに[RDI + 20H]前RSIに等しいRCX設定した場合、そして、それは私の期待値を返します。
私は何か完全に間違っていますか? ありがとうございます。
良いキャッチです。 MSVCは、クラスに応じてメンバー関数に異なるサイズのポインタを使用します.64ビットプラットフォームでは8〜24バイトです。 http://www.agner.org/optimize/calling_conventions.pdf –
リンクをありがとう! 実際には完全な説明とリンクしています(2015年以降も実際のリンクです)。 http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible これは本当に心配です。 私のケースではThirdPartyクラスで単一の継承を持つことができて幸いです。 – Jurys