65.9K
CodeProject 正在变化。 阅读更多。
Home

集合互操作性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.18/5 (5投票s)

2005 年 5 月 24 日

2分钟阅读

viewsIcon

41694

downloadIcon

990

本文介绍了如何在原生代码和托管代码之间传递集合。

引言

在我的工作中,我使用一个 COM 对象作为托管服务器和具有 C DLL 接口的 HW 设备之间的接口。 上周,我遇到了需要将集合从 HW 设备传递到服务器的需求。 为此,我让 COM 从设备读取它并将其传递给服务器。 问题是我无法从 COM 将我的 UDT(用户定义类型)的集合返回到托管服务器。

代码

那么我们如何从原生代码返回 UDT 到托管代码呢?

在原生代码和托管代码之间实现互操作性的最简单方法是将非托管代码放入一个 COM 对象中。 所以剩下的就是将集合从 COM 返回到托管代码。

COM 有两种返回集合的方式

  1. 返回一个 C 风格的数组,如下所示
    HRESULT Foo(int Length, [length_is(Length)]  SUDTStruct  Col[]);
  2. 用集合返回一个 SAFEARRAY,如下所示

    如果你使用 IDL,语法是

    HRESULT Foo(SAFEARRAY(SUDTStruct) **Col)

    如果你使用嵌入式 IDL,语法是

    HRESULT Bar([out, satype(struct PTZPresetsInfo))] SAFEARRAY **Col)

第一种方法不适用于 .NET。 为其创建的互操作代理将如下所示

void Foo(int Length, ref  SUDTStruct  Col)

这意味着只会返回一个 struct 而不是整个集合,所以唯一的方法是使用安全数组。

为此,我们需要几个简单的步骤

  1. 定义您想在集合中传递的 UDT。
  2. 为 UDT 定义一个 GUID
             [export,
             uuid("3DA0FCDB-BAC1-4b13-8808-AB3E43A281BA")]
             struct SUDTStruct  
             {
                     ...
             };
  3. 定义一个 get 函数
             HRESULT Bar(([out, satype(struct PTZPresetsInfo)) SAFEARRAY **Col)
  4. 通过创建 SAFEARRAY 并返回它来实现 get 函数
             SAFEARRAY *pSafeArrayCol;
             unsigned int ndim =  1;
     
             USES_CONVERSION;
             SAFEARRAYBOUND  rgbounds;
             rgbounds.lLbound = 0;
             rgbounds.cElements = ColSize;
            
             IRecordInfo*                  pRecInfo = NULL;
     
             ITypeLib* pTypelib = NULL;
             HRESULT hr = LoadTypeLib(A2OLE(“UDT TYPE LIBRARY”),&pTypelib);
             if (FAILED(hr))
             {
                     return NULL;
             }
     
             ITypeInfo *pTypeInfo;
             hr = 
               pTypelib->GetTypeInfoOfGuid(__uuidof(CollectionType::value_type), 
                                                                    &pTypeInfo);
             if (FAILED(hr))
             {
                     return NULL;
             }
     
             hr = GetRecordInfoFromTypeInfo(pTypeInfo, &pRecInfo);
             if (FAILED(hr))
             {
                     return NULL;
             }
     
             pSafeArrayCol = SafeArrayCreateEx(VT_RECORD, 1, 
                                               &rgbounds, pRecInfo);
             pRecInfo->Release();
     
             CollectionType::value_type *pItem;
             hr = SafeArrayAccessData(pSafeArrayCol, 
                     reinterpret_cast<PVOID*>(&pItem));
  5. 初始化 pItem 指向的集合
             SafeArrayUnaccessData(pSafeArrayCol);

一些说明:如果想将 UDT 的集合传递给 COM,您只需要定义一个带有 GUID 的 UDT 并定义一个类似这样的函数

         HRESULT Bar(SAFEARRAY([in] SUDTStruct) *Col)

此函数将出现在 .NET 互操作中,如下所示

        void Bar(System.Array Col)

调用它时,将 SUDTStruct[] 作为集合传递

为了简化创建安全数组的过程,我创建了一个简单的模板函数,该函数接收一个 STL 集合,该集合的 UDT 作为其值类型,并从中创建一个具有相同数据的安全数组。 源代码附在本文中。

例如

std::vector<SUDTStruct> Col;
 
// init the collection with the data
SAFEARRAY *pSafeArrayCol = CreateUDTSafeArrayFromCol(Col);

CreateSafeArray 函数将创建一个安全数组并使用 STL 集合中的数据对其进行初始化。

随附的演示项目是一个 COM 对象和一个 .NET 程序集,用于从中发送和获取集合。

关注点

微软做了很多工作,使 COM 成为托管和非托管区域之间互操作的机制,但并非所有 COM 功能都通过为 COM 创建的 .NET 代理导出。

© . All rights reserved.