Я хочу вызвать метод в COM-компоненте из C #, используя COM-взаимодействие. Это подпись методов:
long GetPrecursorInfoFromScanNum(long nScanNumber,
LPVARIANT pvarPrecursorInfos,
LPLONG pnArraySize)
и это пример кода (который я проверил, действительно работает), чтобы вызвать его в C ++:
struct PrecursorInfo
{
double dIsolationMass;
double dMonoIsoMass;
long nChargeState;
long nScanNumber;
};
void CTestOCXDlg::OnOpenParentScansOcx()
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = 0;
m_Rawfile.GetPrecursorInfoFromScanNum(m_nScanNumber,
&vPrecursorInfos,
&nPrecursorInfos);
// Access the safearray buffer
BYTE* pData;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pData);
for (int i=0; i < nPrecursorInfos; ++i)
{
// Copy the scan information from the safearray buffer
PrecursorInfo info;
memcpy(&info,
pData + i * sizeof(MS_PrecursorInfo),
sizeof(PrecursorInfo));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
}
А вот соответствующая сигнатура C # после импорта библиотеки типов компонента COM:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
Если я не ошибаюсь, мне нужно передать значение null для pvarPrecursorInfos, чтобы COM-взаимодействие использовало маршал как ожидаемый вариант VT_EMPTY. Когда я это делаю, я получаю исключение SafeArrayTypeMismatchException - не удивительно, если посмотреть, как ожидается, что результат будет обработан в образце. Поэтому я пытался использовать пользовательский маршалер. Поскольку a не может изменить сам компонент, я попытался представить его следующим образом:
[Guid("06F53853-E43C-4F30-9E5F-D1B3668F0C3C")]
[TypeLibType(4160)]
[ComImport]
public interface IInterfaceNew : IInterfaceOrig
{
[DispId(130)]
int GetPrecursorInfoFromScanNum(int nScanNumber, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaler))] ref object pvarPrecursorInfos, ref int pnArraySize);
}
Атрибуты TypeLibType и DispID такие же, как в исходной версии. Это работает до тех пор, пока вызывается метод MyMarshaller.GetInstance (), но я не получаю обратный вызов в MyMarshaller.NativeToManaged. Вместо этого сообщается о нарушении доступа. Так это надежный подход? Если да - как я могу заставить это работать? Если нет: есть ли альтернативы?
(Просто сноска: теоретически я мог бы попытаться использовать управляемый C ++ для собственного вызова компонента. Однако в нем есть много других методов, которые прекрасно работают с COM-взаимодействием, поэтому я бы очень хотел придерживаться C # если есть способ.)