1

更新

我为原始问题(我在下面移动)做了一个测试项目。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)。

4

1 回答 1

0

看起来您的参数类型不匹配。从 c# 获取句柄通常会在 IntPtr 中为您提供窗口句柄。这将是实际的句柄,而不是指向句柄的指针。从您的代码看来,您需要一个指针来处理。我可以通过 long* hWnd 和 VTS_PI4 来判断。

如果 COM 调用确实需要一个 INT_PTR(指向句柄的指针),则必须存储传入的变量并获取它的地址以传递。如果它直接使用窗口句柄,那么您需要将 VTS_PI4/VTS_PI8 修改为 VTS_I4/VTS_I8。

于 2013-03-26T20:56:09.590 回答