2016-04-04 16 views
1

これは数回述べられていることは知っていますが、私の状況は少し異なります。動的な呼び出しのメンバーメソッド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設定した場合、そして、それは私の期待値を返します。

私は何か完全に間違っていますか? ありがとうございます。

答えて

0

[OK]を、私は、既に同様のアプローチを使用して、わずかに異なる状況で働いていたように(入射することにより、解決策を見つけた。

溶液は偽クラスを定義し、ポインタがメンバメソッドを呼び出すことによって、コンパイラをだますことです

おそらく、それは問題ではありませんが、私はThirdPartyNamespace :: ThirdPartyClassに仮想関数があることを知っています。したがって、仮想関数を使って偽のクラスも宣言しています。 。

class FakeCall 
{ 
private: 
    FakeCall(){} 
    virtual ~FakeCall(){} 
}; 

代わりにPTRを呼び出し、一度小さなものを除いて、最初のコードのように残り、 - > * _ pFnGetId(ptrが不明、前方宣言されたクラスのThirdPartyNamespace :: ThirdPartyClassへのポインタである場合)、私は私が呼び出していますふりをしています私FakeCallクラスのメンバメソッド:

long sico = (fake->*_pFnGetSico)(); 
000000005A612096 mov   rcx,rax 
000000005A612099 call  qword ptr [r12+20h] 
000000005A61209E mov   esi,eax 

そして、それは完璧に動作します:

FakeCall * fake = (FakeCall*)ptr; 
    long sico = (fake->*_pFnGetId)(); 

分解が期待どおりに見えます!

いくつかの所見:

  1. メンバメソッドポインタ、私は通常の関数ポインタ以外の何物でも、最初は思っていないよう。
  2. 定義されていないクラス(つまり、名前の前方宣言のみ)のメンバメソッドを呼び出すと、Microsoftコンパイラ(少なくともVS2008)が狂ってしまいます。
+0

良いキャッチです。 MSVCは、クラスに応じてメンバー関数に異なるサイズのポインタを使用します.64ビットプラットフォームでは8〜24バイトです。 http://www.agner.org/optimize/calling_conventions.pdf –

+0

リンクをありがとう! 実際には完全な説明とリンクしています(2015年以降も実際のリンクです)。 http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible これは本当に心配です。 私のケースではThirdPartyクラスで単一の継承を持つことができて幸いです。 – Jurys

関連する問題