vtables - 为什么这样做(COM下C和C ++的二进制兼容性)?

时间:2018-02-12 23:29:37

标签: com ole

我试图测试我对COM的理解,并意识到当我尝试用纯C接口调用C ++对象(用vtable和this指针作为第一个参数)时,它没有按预期工作,因为我的编译器使用了这个调用在ecx寄存器中传递方法的“this”参数,而不是在堆栈上传递(所以当C ++试图通过重新解释的接口调用我的方法时,它不知道“这个”是什么),所以C不能调用C ++方法在x86中没有内联汇编来通过ecx传递/接受第一个参数。

我制作了一个非常简单的COM组件来测试这种二进制兼容性。我创建了一个简单的Visual Basic类,我想用IDispatch C ++抽象类指针和一个IDispatch纯C类指针(带有一个成员lpVtbl)调用;

<System.Runtime.InteropServices.ComVisible(True)>
Public Class Greeter
    Public Sub Greet()
        MsgBox("Hey!")
    End Sub
End Class

代码如下

stdafx.h中:

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <assert.h>

的main.cpp

#include "stdafx.h"

typedef struct {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            __RPC__in struct c_IDispatch * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [annotation][iid_is][out] */ 
            _COM_Outptr_  void **ppvObject);

        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            __RPC__in struct c_IDispatch * This);

        ULONG ( STDMETHODCALLTYPE *Release )( 
            __RPC__in struct c_IDispatch * This);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( 
            __RPC__in struct c_IDispatch * This,
            /* [out] */ __RPC__out UINT *pctinfo);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( 
            __RPC__in struct c_IDispatch * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo);

        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( 
            __RPC__in struct c_IDispatch * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
            /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( 
            struct c_IDispatch * This,
            /* [annotation][in] */ 
            _In_  DISPID dispIdMember,
            /* [annotation][in] */ 
            _In_  REFIID riid,
            /* [annotation][in] */ 
            _In_  LCID lcid,
            /* [annotation][in] */ 
            _In_  WORD wFlags,
            /* [annotation][out][in] */ 
            _In_  DISPPARAMS *pDispParams,
            /* [annotation][out] */ 
            _Out_opt_  VARIANT *pVarResult,
            /* [annotation][out] */ 
            _Out_opt_  EXCEPINFO *pExcepInfo,
            /* [annotation][out] */ 
            _Out_opt_  UINT *puArgErr);

        END_INTERFACE    
} c_IDispatchVtbl;

typedef struct c_IDispatch {
    c_IDispatchVtbl *lpVtbl;
} c_IDispatch;

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);

    IDispatch *x = nullptr;
    CLSID c1;
    DISPID dispid_Greeter_Greet;
    LPOLESTR name = L"Greet";
    assert(SUCCEEDED(CLSIDFromProgID(L"Dmitry.Greeter", &c1)));
    CoCreateInstance(c1, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (LPVOID*)&x);

    assert(SUCCEEDED(x->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_SYSTEM_DEFAULT, &dispid_Greeter_Greet)));

    DISPPARAMS params;
    ZeroMemory(&params, sizeof(params));
    assert(SUCCEEDED(x->Invoke(dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params
        , NULL, NULL, NULL)));

    c_IDispatch *y = *(c_IDispatch **)&x;

    assert(SUCCEEDED(y->lpVtbl->Invoke(y, dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params
        , NULL, NULL, NULL)));
    return 0;
}
令我惊讶的是,即使IDispatch使用此调用,两个调用都会成功,而c_IDispatch则不会。

这是反汇编:

int _tmain(int argc, _TCHAR* argv[])
{
013F1412  add         dword ptr [ebx],esi  
013F1414  lds         ecx,fword ptr [ecx-0B7403BBh]  
    CoInitialize(NULL);
013F141A  push        0  
013F141C  call        dword ptr ds:[13FA360h]  
013F1422  cmp         esi,esp  
013F1424  call        __RTC_CheckEsp (013F115Eh)  

    IDispatch *x = nullptr;
013F1429  mov         dword ptr [x],0  
    CLSID c1;
    DISPID dispid_Greeter_Greet;
    LPOLESTR name = L"Greet";
013F1430  mov         dword ptr [name],13F5858h  
    assert(SUCCEEDED(CLSIDFromProgID(L"Dmitry.Greeter", &c1)));
013F1437  mov         esi,esp  
013F1439  lea         eax,[c1]  
013F143C  push        eax  
013F143D  push        13F5868h  
013F1442  call        dword ptr ds:[13FA364h]  
013F1448  cmp         esi,esp  
013F144A  call        __RTC_CheckEsp (013F115Eh)  
013F144F  test        eax,eax  
013F1451  jge         wmain+89h (013F1479h)  
013F1453  mov         ecx,dword ptr ds:[13F9000h]  
013F1459  ?? ?? 
013F145A  ?? ?? 
013F145B  ?? ?? 
013F145C  ?? ?? 
013F145D  ?? ?? 
013F145E  ?? ?? 
013F145F  ?? ?? 
013F1460  ?? ?? 
013F1461  ?? ?? 
013F1462  ?? ?? 
013F1463  ?? ?? 
013F1464  ?? ?? 
013F1465  ?? ?? 
013F1466  ?? ?? 
013F1467  ?? ?? 
013F1468  ?? ?? 
013F1469  ?? ?? 
013F146A  ?? ?? 
013F146B  ?? ?? 
013F146C  ?? ?? 
013F146D  ?? ?? 
013F146E  ?? ?? 
013F146F  ?? ?? 
013F1470  ?? ?? 
013F1471  ?? ?? 
013F1472  ?? ?? 
013F1473  ?? ?? 
013F1474  ?? ?? 
013F1475  ?? ?? 
013F1476  ?? ?? 
013F1477  ?? ?? 
013F1478  ?? ?? 
013F1479  ?? ?? 
013F147A  ?? ?? 
013F147B  ?? ?? 
    CoCreateInstance(c1, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (LPVOID*)&x);
013F147C  inc         ebp  
013F147D  ?? ?? 
013F147E  ?? ?? 
013F147F  ?? ?? 
013F1480  ?? ?? 
013F1481  ?? ?? 
013F1482  ?? ?? 
013F1483  ?? ?? 
013F1484  ?? ?? 
013F1485  ?? ?? 
013F1486  ?? ?? 
013F1487  ?? ?? 
013F1488  ?? ?? 
013F1489  ?? ?? 
013F148A  ?? ?? 
013F148B  ?? ?? 
013F148C  ?? ?? 
013F148D  ?? ?? 
013F148E  ?? ?? 
    CoCreateInstance(c1, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (LPVOID*)&x);
013F148F  mov         dword ptr ds:[F43B013Fh],eax  
013F1494  call        __RTC_CheckEsp (013F115Eh)  

    assert(SUCCEEDED(x->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_SYSTEM_DEFAULT, &dispid_Greeter_Greet)));
013F1499  mov         esi,esp  
013F149B  lea         eax,[dispid_Greeter_Greet]  
013F149E  push        eax  
013F149F  push        800h  
013F14A4  push        1  
013F14A6  lea         ecx,[name]  
013F14A9  push        ecx  
013F14AA  push        13F64B4h  
013F14AF  mov         edx,dword ptr [x]  
013F14B2  mov         eax,dword ptr [edx]  
013F14B4  mov         ecx,dword ptr [x]  
013F14B7  push        ecx  
013F14B8  mov         edx,dword ptr [eax+14h]  
013F14BB  call        edx  
013F14BD  cmp         esi,esp  
013F14BF  call        __RTC_CheckEsp (013F115Eh)  
013F14C4  test        eax,eax  
013F14C6  jge         wmain+0FDh (013F14EDh)  
013F14C8  mov         eax,dword ptr ds:[013F9000h]  
013F14CD  add         eax,0Ah  
013F14D0  mov         esi,esp  
013F14D2  push        eax  
013F14D3  push        13F5890h  
013F14D8  push        13F5A38h  
013F14DD  call        dword ptr ds:[13FA30Ch]  
013F14E3  add         esp,0Ch  
013F14E6  cmp         esi,esp  
013F14E8  call        __RTC_CheckEsp (013F115Eh)  

    DISPPARAMS params;
    ZeroMemory(&params, sizeof(params));
013F14ED  push        10h  

    DISPPARAMS params;
    ZeroMemory(&params, sizeof(params));
013F14EF  push        0  
013F14F1  lea         eax,[params]  
013F14F4  push        eax  
013F14F5  call        _memset (013F1087h)  
013F14FA  add         esp,0Ch  
    assert(SUCCEEDED(x->Invoke(dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params
        , NULL, NULL, NULL)));
013F14FD  mov         esi,esp  
013F14FF  push        0  
013F1501  push        0  
013F1503  push        0  
013F1505  lea         eax,[params]  
013F1508  push        eax  
013F1509  push        1  
013F150B  push        800h  
013F1510  push        13F64B4h  
013F1515  mov         ecx,dword ptr [dispid_Greeter_Greet]  
013F1518  push        ecx  
013F1519  mov         edx,dword ptr [x]  
013F151C  mov         eax,dword ptr [edx]  
013F151E  mov         ecx,dword ptr [x]  
013F1521  push        ecx  
013F1522  mov         edx,dword ptr [eax+18h]  
013F1525  call        edx  
013F1527  cmp         esi,esp  
013F1529  call        __RTC_CheckEsp (013F115Eh)  
013F152E  test        eax,eax  
013F1530  jge         wmain+167h (013F1557h)  
013F1532  mov         eax,dword ptr ds:[013F9000h]  
013F1537  add         eax,0Fh  
013F153A  mov         esi,esp  
    assert(SUCCEEDED(x->Invoke(dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params
        , NULL, NULL, NULL)));
013F153C  push        eax  
013F153D  push        13F5890h  
013F1542  push        13F5B20h  
013F1547  call        dword ptr ds:[13FA30Ch]  
013F154D  add         esp,0Ch  
013F1550  cmp         esi,esp  
013F1552  call        __RTC_CheckEsp (013F115Eh)  

    c_IDispatch *y = *(c_IDispatch **)&x;
013F1557  mov         eax,dword ptr [x]  
013F155A  mov         dword ptr [y],eax  

    assert(SUCCEEDED(y->lpVtbl->Invoke(y, dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params
        , NULL, NULL, NULL)));
013F155D  mov         esi,esp  
013F155F  push        0  
013F1561  push        0  
013F1563  push        0  
013F1565  lea         eax,[params]  
013F1568  push        eax  
013F1569  push        1  
013F156B  push        800h  
013F1570  push        13F64B4h  
013F1575  mov         ecx,dword ptr [dispid_Greeter_Greet]  
013F1578  push        ecx  
013F1579  mov         edx,dword ptr [y]  
013F157C  push        edx  
013F157D  mov         eax,dword ptr [y]  
013F1580  mov         ecx,dword ptr [eax]  
013F1582  mov         edx,dword ptr [ecx+18h]  
013F1585  call        edx  
013F1587  cmp         esi,esp  
013F1589  call        __RTC_CheckEsp (013F115Eh)  
013F158E  test        eax,eax  
013F1590  jge         wmain+1C7h (013F15B7h)  
013F1592  mov         eax,dword ptr ds:[013F9000h]  
013F1597  add         eax,14h  
013F159A  mov         esi,esp  
013F159C  push        eax  
013F159D  push        13F5890h  
013F15A2  push        13F7B20h  
013F15A7  call        dword ptr ds:[13FA30Ch]  
013F15AD  add         esp,0Ch  
013F15B0  cmp         esi,esp  
013F15B2  call        __RTC_CheckEsp (013F115Eh)  
    return 0;
013F15B7  xor         eax,eax  
}
013F15B9  push        edx  
013F15BA  mov         ecx,ebp  
013F15BC  push        eax  
013F15BD  lea         edx,ds:[13F15E8h]  
013F15C3  call        @_RTC_CheckStackVars@8 (013F10A0h)  
013F15C8  pop         eax  
013F15C9  pop         edx  
013F15CA  pop         edi  
013F15CB  pop         esi  
013F15CC  pop         ebx  
013F15CD  mov         ecx,dword ptr [ebp-4]  
013F15D0  xor         ecx,ebp  
013F15D2  call        @__security_check_cookie@4 (013F101Eh)  
013F15D7  add         esp,124h  
013F15DD  cmp         ebp,esp  
013F15DF  call        __RTC_CheckEsp (013F115Eh)  
013F15E4  mov         esp,ebp  
013F15E6  pop         ebp  
013F15E7  ret  

有人可以解释为什么两个调用都成功,尽管有不同的调用约定(一个约定通过堆栈传递,第二个通过ecx传递)?

0 个答案:

没有答案