更新
我为原始问题(我在下面移动)做了一个测试项目。C#代码:
namespace TestManagedCom
{
[ComVisible(true)]
public class DummyObject
{
public void Method1(int value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method1] value={0:X}, hwnd={1}", value, hwnd));
}
public void Method2(long value)
{
IntPtr hwnd = new IntPtr(value);
MessageBox.Show(string.Format("[Method2] value={0:X}, hwnd={1}", value, hwnd));
}
}
}
C++ 代码:
class CDispatchWrapper : public COleDispatchDriver
{
public:
CDispatchWrapper(){}
CDispatchWrapper(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
CDispatchWrapper(const CDispatchWrapper& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}
void CallMethod(DISPID dwDispID, int value)
{
static BYTE parms[] = VTS_I4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
}
void CallMethod(DISPID dwDispID, long long value)
{
static BYTE parms[] = VTS_I8;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, value);
};
};
template <typename T>
void Execute(const CString& progId, const CString& methodName, T value)
{
LPDISPATCH lpEventComponent = NULL;
_com_ptr_t<_com_IIID<IDispatch, &IID_IDispatch> > pCreateComp;
HRESULT hr = pCreateComp.CreateInstance(progId);
if(SUCCEEDED(hr) && pCreateComp != NULL)
{
hr = pCreateComp.QueryInterface(IID_IDispatch, (void**)&lpEventComponent);
if(SUCCEEDED(hr))
{
USES_CONVERSION;
DISPID dwFunctionID = 0;
OLECHAR FAR *szFunc = T2OLE(const_cast<LPTSTR>(methodName.GetString()));
hr = lpEventComponent->GetIDsOfNames(IID_NULL, &szFunc, 1, LOCALE_SYSTEM_DEFAULT, &dwFunctionID);
if(SUCCEEDED(hr) && dwFunctionID != -1)
{
lpEventComponent->AddRef(); // released by the dispatch driver
CDispatchWrapper wrapper(lpEventComponent);
wrapper.CallMethod(dwFunctionID, value);
}
}
}
}
Execute<int>(_T("TestManagedCom.DummyObject"), _T("Method1"), 0x11223344);
Execute<long long>(_T("TestManagedCom.DummyObject"), _T("Method2"), 0x1122334455667788LL);
当目标是 x64 时效果很好。它打印:
[方法1] value=11223344, hwnd=287454020
【方法二】value=1122334455667788,hwnd=1234605616436508552
Method2
当目标是 x86 时,调用会引发异常。
TestOleDispatcher.exe 中 0x76A2B727 处的第一次机会异常:Microsoft C++ 异常:内存位置 0x003FE3C4 处的 EEException。
如果有这个异常的处理程序,程序可以安全地继续。
我都试过了long long
,__int64
而且错误显然是一样的。
似乎它无法VTS_I8
在 x86 上正确编组参数。
原来的问题
我在一些遗留代码调用 .NET 类中的一个方法时遇到问题,该类表示一个带有 .NET 的 COM 对象COleDispatchDriver::InvokeHelper
。其中一个参数是窗口的句柄。
.NET 代码过去看起来像这样(简化):
[ComVisible(true)]
public class Sample
{
public void Method1(int hwndParent)
{
}
}
和 C++ 代码
class CSendEventWrapper : public COleDispatchDriver
{
public:
void CallMethod(DISPID dwDispID, long* hwnd)
{
static BYTE parms[] = VTS_PI4;
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
};
HWND hWnd = ...;
long lval = (long)hWnd;
o.CallMethod(dispId, &lval); // snippet for calling the method
当 C++ 应用程序只有 32 位时,这工作正常。但是在 64 位版本上,这是不正确的,因为HWND
是 64 位并且long
只是 32 位,所以您会丢失数据。
所以我开始改变 .NET 代码来使用IntPtr
而不是int
(因为它本来应该放在首位)。
[ComVisible(true)]
public class Sample
{
public void Method1(IntPtr hwndParent)
{
}
}
但现在的问题是我如何用InvokeHelper
. 我试着做这样的事情:
void CallMethod(DISPID dwDispID, INT_PTR hwnd)
{
#ifdef _WIN64
static BYTE parms[] = VTS_PI8;
#else
static BYTE parms[] = VTS_PI4;
#endif
InvokeHelper(dwDispID, DISPATCH_METHOD, VT_EMPTY, NULL, parms, hwnd);
}
HWND hWnd = ...;
INT_PTR lval = (INT_PTR)hWnd; // 32- or 64-bit depending on the platform
o.CallMethod(dispId, &lval); // snippet for calling the method
但是,现在这会导致异常,指出参数格式不正确。IntPtr
应该是 32 位还是 64 位,具体取决于进程是 32 位还是 64 位。我不确定出了什么问题。
InvokeHelper
对于确定如何正确通过32 位和 64 位版本的 HWND 的任何帮助,我们将不胜感激。(不,我不能代替使用COleDispatchDriver
)。