Я сделал тестовый проект для исходного вопроса (который я переместил ниже).
Код 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. Это печатает:
[Method1] значение = 11223344, hwnd = 287454020 [Method2] значение = 1122334455667788, hwnd = 1234605616436508552
Призыв к Method2
выбрасывает исключение, когда целью является x86.
Исключение первого шанса в 0x76A2B727 в TestOleDispatcher.exe:
Исключение Microsoft C ++: EEException в ячейке памяти 0x003FE3C4.Если есть обработчик для этого исключения, программа может быть безопасно
продолжение.
Я пытался с обоими long long
а также __int64
и ошибка, очевидно, то же самое.
Кажется, что как-то не может правильно маршал VTS_I8
params на x86.
У меня есть проблемы в каком-то устаревшем коде, вызывающем метод в классе .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-разрядным. Я не уверен, что не так.
Любая помощь для выяснения, как правильно передать HWND с InvokeHelper
как для 32-битных, так и для 64-битных версий. (И нет, я не могу заменить использование COleDispatchDriver
).
Похоже, у вас несоответствие типов параметров. Получение дескриптора из c # обычно дает вам дескриптор окна в IntPtr. Это будет фактический дескриптор, а не указатель на дескриптор. Из вашего кода похоже, что вы ожидаете указатель для обработки. Я могу сказать по длинным * hWnd и VTS_PI4.
Если вызов COM действительно хочет INT_PTR (указатель на дескриптор), вам нужно будет сохранить переданную переменную и принять адрес для передачи. Если он принимает дескриптор окна напрямую, вам нужно изменить VTS_PI4 / VTS_PI8 на VTS_I4 / VTS_I8.
Других решений пока нет …