我试图测试我对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(¶ms, sizeof(params));
assert(SUCCEEDED(x->Invoke(dispid_Greeter_Greet, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms
, 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, ¶ms
, 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(¶ms, sizeof(params));
013F14ED push 10h
DISPPARAMS params;
ZeroMemory(¶ms, 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, ¶ms
, 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, ¶ms
, 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, ¶ms
, 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传递)?