[转]Marshaling a SAFEARRAY of Managed Structures by P/Invoke Part 4.

1. Introduction.

1.1 In parts 1 through 3 of this series of articles, I have thoroughly discussed the techniques for exchanging arrays between managed and unmanaged code by way of SAFEARRAYs.

1.2 The knowledge that can be gained from the first 3 parts of this series is sufficient for general development purposes.

1.3 From part 4 onwards, I shall be covering cases where an outer structure is used to contain the array of structures.

1.4 The use of SAFEARRAYs for marshaling managed arrays is particularly useful when the array is meant to be variable in length.

1.5 I have previously written about marshaling managed structures that contain arrays (see the “Passing Managed Structures With Arrays To Unmanaged Code” series beginning from part 1).

1.6 However, the array contained in the example structure is an integer array whereas the arrays that are used in this series of articles will be arrays of structures, hence serving an alternate (and more complex) example.

2. A New Structure – ArrayContainer.

2.1 Along with using the same TestStructure struct which was first defined in part 1, we shall be using a new structure : ArrayContainer :

 
 

2.2 For the purpose of this article, I have defined this structure inside the source codes of the C# console client application CSConsoleApp which was first introduced in part 1.

2.3 After compiling the CSConsoleApp project into an output EXE assembly, we must call on the Type Library Exporter (TLBEXP.EXE) to produce a type library that will contain the COM-visible constructs defined in CSConsoleApp.exe :

2.4 After this is done, we can examine CSConsoleApp.tlb via OLEVIEW.EXE and observe the following definitions contained inside the type library :

 
 

We can see that the ArrayContainer structure contains a SAFEARRAY of TestStructure structs.

2.5 That the ArrayContainer structure is a wrapper for a SAFEARRAY of TestStructure does not add very much more complexity to its management unless ArrayContainer also contains other fields.

2.6 I have, however, defined it in the simple way it is so that when we reach later parts of this series of articles where we actually define a structure that contains an array of ArrayContainer structures, things do not get overly complicated and difficult to understand.

3. Unmanaged API that takes an ArrayContainer Structure as an “In” Parameter.

3.1 In this section, I shall present a new function to be exported from the UnmanagedDll.dll that we have been using since part 1. This function takes an ArrayContainer structure as input parameter and then displays the field values of each TestStructure struct contained inside the “array_of_test_structures” SAFEARRAY field.

3.2 Full source codes listed below :

 
 

The following is a synopsis of the function above :

  • It defines a vector of TestStructure structs “vecTestStructure”.
  • A helper function CopySafeArrayToVector<>() is used to copy the TestStructure elements of “array_container.array_of_test_structures” (i.e. the SAFEARRAY of TestStructure structs contained in the input ArrayContainer “array_container”) to the “vecTestStructure” vector.
  • It then use the for_each() algorithm function to loop through the elements of the “vecTestStructure” vector and display the field values of each TestStructure struct element.
  • It then calls on SafeArrayGetRecordInfo() to obtain a pointer to the IRecordInfo interface associated with the TestStructure UDT.
  • Another for_each() loop is called, this time to clear each TestStructure struct element contained inside “vecTestStructure”.
  • This last action is necessary because each TestStructure struct element contained inside “vecTestStructure” is a copy of its corresponding TestStructure contained inside “array_container.array_of_test_structures”.
  • While “array_container.array_of_test_structures” will eventually be destroyed by the interop marshaler (it owns the entire ArrayContainer structure passed to the SetArrayContainer() function, the TestStructure structs contained inside “vecTestStructure” must be cleared manually.

3.3 The CopySafeArrayToVector<>() helper function source codes are listed below :

 
 

The following is a summary of this helper function :

  • This function takes 2 template parameters “T” which specifies the type of data contained in the SAFEARRAY parameter. “T” is also the data type of the vector paraneter that receives a copy of the contents of the SAFEARRAY.
  • It first ues the SafeArrayGetVartype() function to obtain the variant type of the elements contained in the SAFEARRAY.
  • This is asserted to match the VARTYPE “v” which is specified by template parameter.
  • The SafeArrayGetLBound() and SafeArrayGetUBound() functions are then used to calculate the number of elements contained inside the SAFEARRAY.
  • The SAFEARRAY is then looped through and with each iteration, SafeArrayGetElement() is called to obtain a copy of an element.
  • The element is then pushed into the receiving vector.

3.4 The “display_test_structure” functor class which was used in the for_each() loop is listed below :

 
 

3.5 The “clear_test_structure” functor class which was used in a for_loop to clear the TestStructure struct elements of the “vecTestStructure” vector is listed below :

 
 

4. Example C# Call to SetArrayContainer().

4.1 The following is how the SetArrayContainer() function is declared in a client C# code :

 
 

The following are some important points pertaining to the code above :

  • The ArrayContainer parameter “array_container”, when passed to the unmanaged world, will be transformed into the following “unmanaged equivalent” format :
 
 
  • When the interop marshaler is about to call SetArrayContainer(), it first creates such an unmanaged structure and then translates the fields of the managed ArrayContainer structure to their equivalents in the unmanaged ArrayContainer structure.
  • For example, the “array_of_test_structures” field which is a managed array of TestStructure structs is transformed into an equivalent SAFEARRAY and the “array_of_test_structures” field of the unmanaged ArrayContainer struct will point to it.
  • The InAttribute indicates that the unmanaged ArrayContainer structure will be passed as a read-only parameter. It is not to be modified by the SetArrayContainer() function.

4.2 The following is a sample function that calls SetArrayContainer() :

 
 

The following is a synopsis of the function above :

  • It instantiates a ArrayContainer structure.
  • The “array_of_test_structures” member is instantiated to an array of 3 TestStructure structs.
  • The “array_of_test_structures” array is filled with values.
  • SetArrayContainer() is called.

4.3 At runtime, the above function will produce the following console output :

 
 

5. In Conclusion.

5.1 Here in part 4, we have shifted gears and looked at how managed structures may be marshaled to unmanaged code by way of a SAFEARRAY contained in an outer wrapping structure.

5.2 Although the concept is practically not much more complicated than direct marshaling of a managed array to unmanaged code via SAFEARRAYs, I have endeavoured to cover this in order to pave the way for a later installment in which an array of an array of structures are to be marshaled.

5.3 It is also an opportunity for me to introduce and expound on some useful helper functions for the benefit of the reader.

转载于:https://www.cnblogs.com/czytcn/p/7928079.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值