我希望使用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);
}
在导入COM组件的类型库后,这是相应的C#签名:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
如果我没记错的话,我需要传入null值给pvarPrecursorInfos参数,这样COM互操作才能将其作为预期的VT_EMPTY变体进行编组。但当我这样做时,我会遇到SafeArrayTypeMismatchException异常——这并不令人惊讶,因为在示例中结果的处理方式已经很明显了。因此,我尝试使用自定义编组器。由于无法更改组件本身,所以我试图通过以下方式引入它:
[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#。)