1. Introduction.

1.1 In part
4
, I have started to discuss how to interop marshal a managed array that is
contained within a structure.

1.2 I have given a specific working example for
marshaling such a container structure to unmanaged code “one way”
(i.e. as an “in” parameter).

1.3 Here in part 5, I shall demonstrate how to marshal
such a container structure from an unmanaged function to managed code as an
“out” (return) parameter.

1.4 Just like the case in part 4, the example codes that
will be presented here are practically not much more complicated than
direct marshaling of a SAFEARRAY from unmanaged code to managed code, to be
eventually transformed into a managed array.

1.5 I have endeavoured to cover this aspect of
marshaling in order to pave the way for a later installment in which an array of
an array of structures are to be marshaled.

1.6 It is also an opportunity for me to demonstrate the
use of several IRecordInfo methods which pertain to UDTs. Through these
IRecordInfo methods I hope to show strong consistency and robustness of both the
IRecordInfo interface as well as the SAFEARRAY.

2. The TestStructure, ArrayContainer Structures and
the CSConsoleApp.tlb Type
Library.

2.1 We shall continue to use the TestStructure and the
ArrayContainer structures that we have previously defined in part 1 and part 4
respectively.

2.2 We shall also be using the CSConsoleApp.tlb type
library that we have updated in part 4.

3. Unmanaged API that Returns an ArrayContainer
Structure as an “Out” Parameter.

3.1 In this section, I shall present a new unmanaged
function to be exported from the UnmanagedDll.dll that
we have been using since part 1. This function takes a pointer to
an ArrayContainer structure as input parameter.

3.2 The intension of this function is to fill the
underlying ArrayContainer structure with values for its fields. This
ArrayContainer structure is in this way returned to the calling
function.

3.3 Full source codes of this unmanaged function is
listed below :

extern "C" __declspec(dllexport) void __stdcall GetArrayContainer
(
/*[out]*/ ArrayContainer* pArrayContainerReceiver
)
{
std::vector<TestStructure> vecTestStructure;
TestStructure test_structure; test_structure.m_integer = 0;
test_structure.m_double = 0;
test_structure.m_string = ::SysAllocString(L"Hello World");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 1;
test_structure.m_double = 1.0;
test_structure.m_string = ::SysAllocString(L"Hello World");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 2;
test_structure.m_double = 2.0;
test_structure.m_string = ::SysAllocString(L"Hello World");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 3;
test_structure.m_double = 3.0;
test_structure.m_string = ::SysAllocString(L"Hello World");
vecTestStructure.push_back(test_structure); HRESULT hrRet;
IRecordInfoPtr spIRecordInfoTestStructure = NULL; hrRet = GetIRecordType
(
TEXT("CSConsoleApp.tlb"),
__uuidof(TestStructure),
&spIRecordInfoTestStructure
); // Define a receiver of the SAFEARRAY.
SAFEARRAY* pSafeArrayOfTestStructure = NULL; CreateSafeArrayEx<TestStructure, VT_RECORD>
(
(TestStructure*)&(vecTestStructure[0]),
vecTestStructure.size(),
(PVOID)spIRecordInfoTestStructure,
pSafeArrayOfTestStructure
); // At this point, "pSafeArrayOfTestStructures" contains
// copies of TestStructure structs from "vecTestStructure". IRecordInfoPtr spIRecordInfoArrayContainer = NULL; hrRet = GetIRecordType
(
TEXT("CSConsoleApp.tlb"),
__uuidof(ArrayContainer),
&spIRecordInfoArrayContainer
); spIRecordInfoArrayContainer -> RecordClear((PVOID)pArrayContainerReceiver); // Define a VARIANT that will contain "pSafeArrayOfTestStructures".
VARIANT varFieldValue; VariantInit(&varFieldValue);
V_VT(&varFieldValue) = (VT_ARRAY | VT_RECORD);
V_ARRAY(&varFieldValue) = pSafeArrayOfTestStructure; // Set the "array_of_test_structures" field
// of the "pArrayContainerReceiver" structure.
spIRecordInfoArrayContainer -> PutFieldNoCopy
(
INVOKE_PROPERTYPUT,
pArrayContainerReceiver,
L"array_of_test_structures",
&varFieldValue
); std::vector<TestStructure>::iterator theIterator; // The members of structures which are reference types
// (e.g. m_string which is BSTR) must be freed here.
// This is because SAFEARRAYs use copy-semantics.
// That is, they store an entire copy of the structures
// that we insert into it.
//
// Therefore each TestStructure structure inside the
// SAFEARRAY will contain a BSTR copy in its m_string
// member.
for
(
theIterator = vecTestStructure.begin();
theIterator != vecTestStructure.end();
theIterator++
)
{
TestStructure& test_structure = *theIterator; ::SysFreeString(test_structure.m_string);
} // Because we have used IRecordInfo::PutFieldNoCopy()
// to insert "pSafeArrayOfTestStructure" as a field
// value into "ArrayContainer", we must not destroy
// "pSafeArrayOfTestStructure".
//
// If we had used IRecordInfo::PutField() then we
// must call SafeArrayDestroy() on "pSafeArrayOfTestStructure"
// or use VariantClear() on the VARIANT that was
// used in the IRecordInfo::PutField() call.
// Otherwise there will be a memory leakage.
//
//::SafeArrayDestroy(pSafeArrayOfTestStructure);
//pSafeArrayOfTestStructure = NULL;
//VariantClear(&varFieldValue);
}

The following is a summary of the workings of
this function :

  • An STL vector “vecTestStructure” is
    defined.
  • The vector is used to insert 4 copies of TestStructure
    structs each of which has different values for its “m_integer” and “m_double”
    fields. The “m_string” fields are set to a standard value.
  • The GetIRecordType() helper function (first introduced
    in part 2) is used to obtain a pointer to the IRecordInfo interface which is
    associated with the TestStructure UDT.
  • The CreateSafeArrayEx<>() helper function is then
    used to copy each of the TestStructure UDTs from “vecTestStructure” to a
    SAFEARRAY.
  • Instead of directly using the SAFEARRAY field
    of the input “pArrayContainerReceiver” ArrayContainer structure to hold the
    SAFEARRAY created inside CreateSafeArrayEx<>(), I have used a separate
    SAFEARRAY “pSafeArrayOfTestStructure” to point to it.
  • What I intend to do is to use the IRecordInfo::PutFieldNoCopy()
    method
    to insert the SAFEARRAY directly into the
    “array_of_test_structures” field of “pArrayContainerReceiver”.
  • To do this, we need to use a VARIANT to contain
    “pSafeArrayOfTestStructure”.
  • Before the function completes and returns to the caller,
    we must clear the TestStructure structs contained inside
    “vecTestStructure”.
  • This is necessary because the SAFEARRAY that was
    eventually inserted into “pArrayContainerReceiver” holds a complete copy of
    each of the UDTs contained inside “vecTestStructure”.
  • Hence the UDTs inside “vecTestStructure” are no longer
    needed anywhere and if we do not clear them, memory leakage will
    result.
  • However, note well that because we have used
    IRecordInfo::PutFieldNoCopy() to insert “pSafeArrayOfTestStructure” as a field
    value into “pArrayContainerReceiver”, we must not destroy
    “pSafeArrayOfTestStructure”.
  • As the documentation of IRecordInfo::PutFieldNoCopy() indicated,
    the ownership of “pSafeArrayOfTestStructure” was transferred to the receiving
    “pArrayContainerReceiver”.
  • If we had used IRecordInfo::PutField() then we must call
    SafeArrayDestroy() on “pSafeArrayOfTestStructure” or use VariantClear() on the
    VARIANT that was used in the IRecordInfo::PutField() call. Otherwise there will
    be a memory leak.

4. Example Call to
GetArrayContainer().

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

[DllImport("UnmanagedDll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void GetArrayContainer([Out] out ArrayContainer ArrayContainerReceiver);

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

  • When the interop marshaler internally makes a call to
    the GetArrayContainer() function, it will create the unmanaged version of the
    ArrayContainer structure and then pass a pointer to it as parameter to the
    GetArrayContainer() function.
  • The unmanaged version of the
    ArrayContainer structure is the one which is created by the Visual C++
    compiler when it processed the “CSConsoleApp.tlb”
    type library via an #import statement in a C++ project :
struct __declspec(uuid("42d386a1-aae1-445e-a755-00aa7b2c1753"))
ArrayContainer
{
SAFEARRAY * array_of_test_structures;
};
  • pointer to
    this unmanaged ArrayContainer structure is passed instead of a complete
    structure itself due to the use of the OutAttribute as well as the out
    keyword.
  • This is so that the underlying structure can be modified
    by the GetArrayContainer() function.

4.2 The following is a sample C# function that
calls GetArrayContainer() :

static void DoTest_GetArrayContainer()
{
ArrayContainer ArrayContainerReceiver; GetArrayContainer(out ArrayContainerReceiver); for (int i = 0; i < ArrayContainerReceiver.array_of_test_structures.Length; i++)
{
Console.WriteLine("ArrayContainerReceiver.array_of_test_structures[{0}].m_integer : [{1}]",
i, ArrayContainerReceiver.array_of_test_structures[i].m_integer);
Console.WriteLine("ArrayContainerReceiver.array_of_test_structures[{0}].m_double : [{1}]",
i, ArrayContainerReceiver.array_of_test_structures[i].m_double);
Console.WriteLine("ArrayContainerReceiver.array_of_test_structures[{0}].m_string : [{1:S}]",
i, ArrayContainerReceiver.array_of_test_structures[i].m_string);
}
}

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

  • An ArrayContainer structure “ArrayContainerReceiver” is
    defined but is not instantiated.
  • This is acceptable because it is used as an “out”
    parameter to a call to GetArrayContainer().
  • Under the covers, when the GetArrayContainer() returns,
    a new ArrayContainer structure will be created by the interop marshaler
    and the unmanaged ArrayContainer structure the pointer to which was
    passed to the function would be filled with actual field
    values.
  • These field values will be used to set the corresponding
    ones in the newly created managed ArrayContainer structure.
  • The new field values of the managed ArrayContainer
    structure will be displayed via a loop.

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

ArrayContainerReceiver.array_of_test_structures[0].m_integer : [0]
ArrayContainerReceiver.array_of_test_structures[0].m_double : [0]
ArrayContainerReceiver.array_of_test_structures[0].m_string : [Hello World]
ArrayContainerReceiver.array_of_test_structures[1].m_integer : [1]
ArrayContainerReceiver.array_of_test_structures[1].m_double : [1]
ArrayContainerReceiver.array_of_test_structures[1].m_string : [Hello World]
ArrayContainerReceiver.array_of_test_structures[2].m_integer : [2]
ArrayContainerReceiver.array_of_test_structures[2].m_double : [2]
ArrayContainerReceiver.array_of_test_structures[2].m_string : [Hello World]
ArrayContainerReceiver.array_of_test_structures[3].m_integer : [3]
ArrayContainerReceiver.array_of_test_structures[3].m_double : [3]
ArrayContainerReceiver.array_of_test_structures[3].m_string : [Hello World]

5. In Conclusion.

5.1 Here in part 5, we have looked at how managed
structures may be derived from unmanaged code by way of a SAFEARRAY contained in
an outer wrapping structure.

5.2 I have made a good
attempt at demonstrating the use of the
IRecordInfo::PutFieldNoCopy() method to insert a field value into a
UDT.

5.3 Part of my aim was to demontrate a solid
example of memory ownership.

5.4 I certainly hope that the reader has benefitted from
this demonstration.

[转]Marshaling a SAFEARRAY of Managed Structures by P/Invoke Part 5.的更多相关文章

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

    1. Introduction. 1.1 I have previously written about exchanging SAFEARRAYs of managed structures wit ...

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

    1. Introduction. 1.1 Starting from part 4 I have started to discuss how to interop marshal a managed ...

  3. [转]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 th ...

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

    1. Introduction. 1.1 In part 1 of this series of articles, I demonstrated how to transfer managed ar ...

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

    1. Introduction. 1.1 In part 1 of this series of articles, I explained how managed arrays may be tra ...

  6. [转]Passing Managed Structures With Strings To Unmanaged Code Part 2

    1. Introduction. 1.1 In part 1 of this series of blogs we studied how to pass a managed structure (w ...

  7. [转]Passing Managed Structures With Strings To Unmanaged Code Part 1

    1. Introduction. 1.1 Managed structures that contain strings are a common sight. The trouble is that ...

  8. [转]Passing Managed Structures With Strings To Unmanaged Code Part 3

    1. Introduction. 1.1 In part 1 of this series of blogs we studied how to pass a managed structure (w ...

  9. Passing JavaScript Objects to Managed Code

    Silverlight If the target managed property or input parameter is strongly typed (that is, not typed ...

随机推荐

  1. 分布式开发框架Pebble使用说明

    简介 Pebble定位为一个好用.可靠.高性能.易扩展的分布式开发框架,支持多种使用方式: 使用完整的pebble框架 独立使用各pebble子模块 在其他框架中嵌入pebble框架. Pebble具 ...

  2. 多线程使用信号量sem_init,sem_wait,sem_post

    信号量的主要函数有: int sem_init(sem_t *sem,int pshared,unsigned int value); int sem_wait(sem_t *sem); int se ...

  3. C Primer Plus学习笔记(十一)- 存储类别、链接和内存管理

    存储类别 从硬件方面来看,被储存的每个值都占用一定的物理内存,C 语言把这样的一块内存称为对象(object) 对象可以储存一个或多个值.一个对象可能并未储存实际的值,但是它在储存适当的值时一定具有相 ...

  4. ubuntu双网卡准备配置

    近日有个需求,交换机有两台,做了堆叠,服务器双网卡,每个分别连到一台交换机上.这样就需要将服务器的网卡做成主备模式,以增加安全性,使得当其中一个交换机不通的时候网卡能够自动切换. 整体配置不难,网上也 ...

  5. c# 程序调用cmd执行命令如SVN.exe

    c# 程序调用cmd执行命令如SVN.exe string str = Console.ReadLine(); System.Diagnostics.Process p = new System.Di ...

  6. Python 的mock模拟测试介绍

    如何不靠耐心测试 可能我们正在写一个社交软件并且想测试一下"发布到Facebook的功能",但是我们不希望每次运行测试集的时候都发布到Facebook上. Python的unitt ...

  7. 升级到Win10 周年更新版

    尝试过强制刷更新,但是没用,最近微软才跟我的机器推送周年更新,于是更新. 花费了些时间更新,之前网上有的那些诗句,亲眼看看还是蛮有意思的. 但是更新完了后,explorer 一直出错,有闪退(闪屏)一 ...

  8. spring中二个重要点

    spring核心主要两部分: (1)aop: 面向切面编程,扩展功能不是修改源代码实现 (2)ioc: 控制反转

  9. xcode找回欢迎界面

    [xcode找回欢迎界面] 有一次嫌xcode界面烦,点击了以下checkbox取消了欢迎界面: 随后发现没了欢迎界面反而更不方便,一心想找回欢迎界面,Preference里找了半天没找到开关,原来开 ...

  10. Android4.0+锁屏程序开发——按键屏蔽篇

    开发锁屏程序的时候我们要面临的重要问题无疑是如何屏蔽三个按键,Back,Home,Menu  看似简单的功能,实现起来却并不是那么容易. [屏蔽Back按键] 相对来说,屏蔽Back键是比较简单的,只 ...