アップデート
元の質問(以下に移動)のテストプロジェクトを作成しました。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 ビットにする必要があります。何が悪いのかわからない。
InvokeHelper
32 ビット バージョンと 64 ビット バージョンの両方でHWND を正しく渡す方法を理解するための助けをいただければ幸いです。(いいえ、の使用を置き換えることはできませんCOleDispatchDriver
)。