COM互操作:如何使用ICustomMashaler调用第三方组件
COM interop: how to use ICustomMarshaler to call 3rd party component
我想使用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组件的typelib后对应的C#签名:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
如果我没有弄错的话,我需要为pvarPrecursorInfo传递null,以使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()方法而言,这是有效的,但我在MyMarshalller.NativeToManaged中没有得到回调。相反,报告了访问冲突。那么,这是一种可靠的方法吗?如果是,我该如何让它发挥作用?如果没有:还有其他选择吗?
(只是一个脚注:理论上,我可以尝试使用托管C++来本机调用组件。然而,其中有很多其他方法可以很好地与COM互操作配合使用,所以如果有任何方法的话,我非常愿意坚持使用C#。)
由于有人提出要求,下面是我在托管C++中的解决方案。
array<PrecursorInfo^>^ MSFileReaderExt::GetPrecursorInfo(int scanNumber)
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = -1;
//call the COM component
long rc = pRawFile->GetPrecursorInfoFromScanNum(scanNumber, &vPrecursorInfos, &nPrecursorInfos);
//read the result
//vPrecursorInfos.parray points to a byte sequence
//that can be seen as array of MS_PrecursorInfo instances
//(MS_PrecursorInfo is a struct defined within the COM component)
MS_PrecursorInfo* pPrecursors;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pPrecursors);
//now transform into a .NET object
array<PrecursorInfo^>^ infos = gcnew array<PrecursorInfo^>(nPrecursorInfos);
MS_PrecursorInfo currentPrecursor;
for (int i=0; i < nPrecursorInfos; ++i)
{
currentPrecursor = pPrecursors[i];
infos[i] = safe_cast<PrecursorInfo^>(Marshal::PtrToStructure(IntPtr(¤tPrecursor), PrecursorInfo::typeid));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
return infos;
}
我查看了github代码mzLib,我认为它与本主题有关。代码看起来不错,它称之为
pin_ptr<const wchar_t> wch = PtrToStringChars(path);
我认为这可能会引起一些问题,最好使用
pin_ptr<const wchar_t> pathChar = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni(path).ToPointer());
然后,代码在编译时似乎工作得很好。但是,当作为dll导入时,它可能会出现问题。我通过添加一个构造函数来实现这一点,比如
public ref class ThermoDLLClass
{
public:
ThermoDLLClass();
PrecursorInfo GetPrecursorInfo(int scanNum, String^ path);
};
然后,它似乎可以适当地获得前兆信息和参数。
相关文章:
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 为什么使用 "this" 指针调用派生成员函数?
- 函数调用中参数的顺序重要吗
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在第三方函数调用之前同时运行线程
- P/调用第三方弹出窗口
- C#调用C 第三方DLL(无源)提出异常 - 不兼容PinVoke
- Unix C++第三方API调用
- 如何允许通过嵌入式 Python 解释器中调用的脚本导入第三方库
- 如何正确处理C++线程中永久挂起的第三方库调用
- COM互操作:如何使用ICustomMashaler调用第三方组件
- 全局重载 delete[] 未在第三方库中调用
- 调用第三方api,如amazon/ebay,使用c++获取产品详细信息
- 如何在第三方库调用的回调函数中传递/使对象/变量可访问
- 用于第三方库调用的c++ watchdog
- 为第三方库libA封装对malloc的调用.a,但不是为了libb
- 调用第三方可执行文件时调试分段错误