C# 与 COM 交互

整理了 C# 与 COM 交互的数据类型对应关系,以及数组封送处理示例

1. 数据封送处理#

参考文档:用 COM 互操作对数据进行封送处理

1.1 数据类型映射#

下表显示在 COM 中使用的数据类型及其相应的 .NET Framework 内置值类型或类。此表中没有显式标识的任何类型都将被转换为 Int32 系统类型。

COM 值类型 COM 引用类型 系统类型
bool bool * System.Int32
charsmall char *、small * System.SByte
short short * System.Int16
longint long *、int * System.Int32
Hyper hyper * System.Int64
unsigned charbyte unsigned char *、byte * System.Byte
wchar_tunsigned short wchar_t *、unsigned short * System.UInt16
unsigned longunsigned int unsigned long *、unsigned int * System.UInt32
unsigned hyper unsigned hyper * System.UInt64
float float * System.Single
double double * System.Double
VARIANT_BOOL VARIANT_BOOL * System.Boolean
void * void ** System.IntPtr
HRESULT HRESULT * System.Int16System.IntPtr
SCODE SCODE * System.Int32
BSTR BSTR * System.String
LPSTR[string, …] char * LPSTR * System.String
LPWSTR[string, …] wchar_t * LPWSTR * System.String
VARIANT VARIANT * System.Object
DECIMAL DECIMAL * System.Decimal
DATE DATE * System.DateTime
GUID GUID * System.Guid
CURRENCY CURRENCY * System.Decimal
IUnknown * IUnknown ** System.Object
IDispatch * IDispatch ** System.Object
SAFEARRAY(type) SAFEARRAY(type) * type[]

1.2 元素类型映射#

下表列出了转换为对应的元素类型的 COM 值和引用类型。 例如, COM coclass 自动映射到同名的托管类。

COM 值类型 COM 引用类型 元素类型
Typedef BaseTypeMyType ByRef BaseType BaseType
MyStruct ByRef VALUETYPE<MyStruct> valuetype<MyStruct>
MyEnum ByRef VALUETYPE<MyEnum> valuetype<MyEnum>
MyInterface* ByRef CLASS <MyInterface> Class <MyInterface>
MyCoClass ByRef CLASS <_Class> Class<_Class>

2. 互操作代码示例#

2.1 在 COM 中修改 C# 传入的数组#

C++ 中接口定义:

interface IATLSimpleObject : IDispatch
{
	HRESULT TestMethod([in] SAFEARRAY(USHORT) array1, [in, out] SAFEARRAY(USHORT) array2, [out, retval] int* result);
};
STDMETHOD(TestMethod)(SAFEARRAY* array1, SAFEARRAY* array2, int* result);

对应的 C# 接口:

int TestMethod(
    [In][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI2)]
    Array array1,
    [In][Out][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI2)] 
    Array array2
    );

测试代码:

var s1 = new ushort[] {1,2,3,4,5,6,7,8,9,0};
Array array1 = s1;
var s2 = new ushort[5];
Array array2 = s2;

proxy.TestMethod(array1, array2);

// s2、array2数据均被修改
// 正常应该数据不会改变?

如果将 .idl 文件中的定义改为 SAFEARRAY(USHORT)*,同时修改 .h 文件,将 SAFEARRAY* 改为 SAFEARRAY**,则生成的接口会添加 ref 关键字,即 ref Array arrayx。 此时调用方法后,array2 数据被修改,s2 数据没有改变。

2.2 SAFEARRAY 基本操作#

参考:在COM中使用数组参数

2.2.1 获取数组维度#

// 数组下界
LONG p1LBound;
SafeArrayGetLBound(array1, 1 /*维度,从1开始*/, &p1LBound);
// 数组上界
LONG p1UBound;
SafeArrayGetUBound(array1, 1, &p1UBound);
// 数组长度,注意 +1
int p1len = p1UBound - p1LBound + 1;

2.2.2 访问数组#

USHORT* p1 = nullptr;
HRESULT h1 = SafeArrayAccessData(array1, (void**)&p1);

if (SUCCEEDED(h1))
{
    // 访问或修改 p1 指向的数据
}

// Access与Unaccess要成对出现
SafeArrayUnaccessData(array1);

2.2.3 创建数组#

SAFEARRAYBOUND bound;
bound.cElements = 5;
bound.lLbound = 0;

// 创建了一个一维的、索引从0开始的、包含5个元素的数组
SAFEARRAY* array1 = SafeArrayCreate(VT_UI2, 1, &bound);

❤️ 如果这篇文章对你有帮助,欢迎赞助支持我继续维护 ❤️

☕ Support me ⚡ 爱发电赞助