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

[方法2] value=1122334455667788, hwnd=1234605616436508552

Method2ターゲットが x86 の場合、への呼び出しは例外をスローします。

TestOleDispatcher.exe の 0x76A2B727 での初回例外: Microsoft C++ 例外: メモリ位置 0x003FE3C4 での EEException。

この例外のハンドラがあれば、プログラムは安全に続行できます。

と の両方を試してみましlong long__int64が、エラーは明らかに同じです。

どういうわけかVTS_I8、x86 でパラメーターを正しくマーシャリングできないようです。

元の質問

で COM オブジェクトを表す .NET クラスのメソッドを呼び出すレガシー コードに問題がありますCOleDispatchDriver::InvokeHelper。パラメータの 1 つは、ウィンドウのハンドルです。

以前の .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 ビットにすぎないため、データが失われます。

IntPtrそこで、代わりに使用する .NET コードを変更し始めました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 ビットにする必要があります。何が悪いのかわからない。

InvokeHelper32 ビット バージョンと 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 に答える