整理了 C# 与 COM 交互的数据类型对应关系,以及数组封送处理示例
1. 数据封送处理#
参考文档:用 COM 互操作对数据进行封送处理
1.1 数据类型映射#
下表显示在 COM 中使用的数据类型及其相应的 .NET Framework 内置值类型或类。此表中没有显式标识的任何类型都将被转换为 Int32 系统类型。
| COM 值类型 | COM 引用类型 | 系统类型 |
|---|---|---|
| bool | bool * | System.Int32 |
| char、small | char *、small * | System.SByte |
| short | short * | System.Int16 |
| long、int | long *、int * | System.Int32 |
| Hyper | hyper * | System.Int64 |
| unsigned char、byte | unsigned char *、byte * | System.Byte |
| wchar_t、unsigned short | wchar_t *、unsigned short * | System.UInt16 |
| unsigned long、unsigned 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.Int16 或 System.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);