これについては何度か議論されていることは知っていますが、私の状況は少し異なります。
一部のクラスをエクスポートするサードパーティの dll があります。残念ながら、ヘッダー ファイルは使用できません。エクスポートされた関数を呼び出すことは引き続き可能です。しかし、正しい 'this' ポインター (RCX レジスターで渡される) を渡すことはできません。
まず、関数名を抽出するために dumpbin /exports を使用します (名前はサードパーティ ライブラリと関数名が機密であるため変更されます)。
4873 1308 0018B380 ?GetId@ThirdPartyClass@ThirdPartyNamespace@@QEBAJXZ = ??GetId@ThirdPartyClass@ThirdPartyNamespace@@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, "?GetId@ThirdPartyClass@ThirdPartyNamespace@@QEBAJXZ");
long id = (ptr->*_pFnGetId)();
すべて問題ないように見えます (つまり、ステップインすると - 実際に ThirdPartyClass::GetId メソッド内に入ります。しかし、このポインターは適切ではありません。ptr は適切ですが、デバッガーで手動で rcx を ptr に変更すると、正常に動作します。しかし、コンパイラは何らかの理由で 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 には ThirdPartyClass のオブジェクトへのポインター (つまり 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] を呼び出す前に rcx を rsi に設定すると、期待値が返されます。
私は何か完全に間違っていますか?前もって感謝します。