1. Introduction.

1.1 Starting from part
4
I have started to discuss how to interop marshal a managed array of
TestStructure structs that is contained within a structure.

1.2 We have seen in part 4 an example for marshaling
such a container structure to unmanaged code “one way” (i.e. as an “in”
parameter).

1.3 In part
5
we learned how to marshal such a container structure from an
unmanaged function to managed code as an “out” (return) parameter.

1.4 Here in part 6, we shall look into how to marshal
such a container structure bi-directionally, i.e. as both an “in” and an
“out” (or reference) parameter.

1.5 Just as I did in part 5, I shall take this
opportunity 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 a Reference 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 modify the
underlying ArrayContainer structure with new values for its fields. This
ArrayContainer structure is in this way returned to the calling function with
modified field data.

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

extern "C" __declspec(dllexport) void __stdcall ModifyArrayContainer
(
/*[in, out]*/ ArrayContainer* pArrayContainerToModify
)
{
IRecordInfoPtr spIRecordInfoArrayContainer = NULL;
HRESULT hrRet; hrRet = GetIRecordType
(
TEXT("CSConsoleApp.tlb"),
__uuidof(ArrayContainer),
&spIRecordInfoArrayContainer
); // We do something drastic : we completely clear the contents of
// "pArrayContainerToModify". This will release object references
// and other values of the UDT without deallocating the UDT.
//
// As a result, the "array_of_test_structures" SAFEARRAY contained
// inside "pArrayContainerToModify" will be destroyed.
spIRecordInfoArrayContainer -> RecordClear((PVOID)pArrayContainerToModify); // We now create an STL vector of TestStructure structs.
std::vector<TestStructure> vecTestStructure;
TestStructure test_structure; test_structure.m_integer = 100;
test_structure.m_double = 100.0;
test_structure.m_string = ::SysAllocString(L"New string");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 101;
test_structure.m_double = 101.0;
test_structure.m_string = ::SysAllocString(L"New string");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 102;
test_structure.m_double = 102.0;
test_structure.m_string = ::SysAllocString(L"New string");
vecTestStructure.push_back(test_structure); test_structure.m_integer = 103;
test_structure.m_double = 103.0;
test_structure.m_string = ::SysAllocString(L"New string");
vecTestStructure.push_back(test_structure); IRecordInfoPtr spIRecordInfoTestStructure = NULL; hrRet = GetIRecordType
(
TEXT("CSConsoleApp.tlb"),
__uuidof(TestStructure),
&spIRecordInfoTestStructure
); // Define a receiver of the SAFEARRAY that we want
// to now create using CreateSafeArrayEx<>().
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". // Define a VARIANT that will contain "pSafeArrayOfTestStructure".
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.
//
// As an alternative to PutFieldNoCopy() as
// used in GetArrayContainer(), we now use
// the PutField() method which will make a
// complete copy of the data contained
// inside "varFieldValue" to set as the
// field value of "array_of_test_structures".
//
// Hence later on, we must either destroy
// "pSafeArrayOfTestStructure" by a call to
// SafeArrayDestroy() or by calling VariantClear().
spIRecordInfoArrayContainer -> PutField
(
INVOKE_PROPERTYPUT,
pArrayContainerToModify,
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; spIRecordInfoTestStructure -> RecordClear((PVOID)&test_structure);
} // Because we have used IRecordInfo::PutField()
// to insert "pSafeArrayOfTestStructure" as a field
// value into "ArrayContainer", we must now destroy
// "pSafeArrayOfTestStructure" either by calling
// SafeArrayDestroy() or by calling VariantClear()
// but not both.
//
VariantClear(&varFieldValue);
}

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

  • The function begins by completely clearing the fields of
    “pArrayContainerToModify”.
  • This will result in the complete destruction of the
    “array_of_test_structures” SAFEARRAY field of
    “pArrayContainerToModify”.
  • Later on in the function, a new SAFEARRAY will be
    created and will replace the one that has just been
    destroyed.
  • This will present no problem to the corresponding
    “array_of_test_structures” field of the managed ArrayContainer structure that
    was passed to the ModifyArrayContainer() function in the managed client
    application. This is because a brand new managed array will be created and then
    initialized with new values from the new “array_of_test_structures” SAFEARRAY.
    But more on this when we get there later.
  • A STL vector of TestStructure structs (i.e.
    “vecTestStructure”) will then be instantiated and filled with
    values.
  • Then a new SAFEARRAY (i.e. “pSafeArrayOfTestStructure”)
    will be created using the CreateSafeArrayEx<>() helper function. The
    SAFEARRAY will be filled with TestStructure structs copied from the earlier
    “vecTestStructure” vector.
  • And then the IRecordInfo::PutField() method will be
    called on the IRecordInfo interface associated with the ArrayContainer UDT to
    insert the values of “pSafeArrayOfTestStructure” into the
    “array_of_test_structures” field of the input
    “pArrayContainerToModify”.
  • At this point, the whole purpose of the
    ModifyArrayContainer() function has been accomplished.
  • What remains is clean-up.
  • The TestStructure structs contained inside
    “vecTestStructure” must now each be destroyed. We loop through the elements of
    “vecTestStructure” and call on the IRecordInfo interface associated with
    the TestStructure UDT to clear each structure.
  • Note that we must not call IRecordInfo::RecordDestroy()
    because this will cause the memory area occuppied by each TestStructure struct
    to be freed.
  • This is not permitted because each TestStructure struct
    contained inside “vecTestStructure” remains owned by “vecTestStructure”. Only
    “vecTestStructure” can de-allocate each struct.
  • Finally, we call on VariantClear() to clear the
    “varFieldValue” VARIANT that was used in the call to
    IRecordInfo::PutField().
  • Alternatively, SafeArrayDestroy() can also be used to
    directly destroy “pSafeArrayOfTestStructure”.
  • However, do not call both VariantClear() and
    SafeArrayDestroy().

4. Example Call to
ModifyArrayContainer().

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

[DllImport("UnmanagedDll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void ModifyArrayContainer
(
[In][Out] ref ArrayContainer ArrayContainerToModify
);
  • When the interop marshaler internally makes a call to
    the ModifyArrayContainer() function, it will create the unmanaged version of the
    ArrayContainer structure and then pass a pointer to it as parameter to the
    ModifyArrayContainer() function.
  • A 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 ModifyArrayContainer() function. The ModifyArrayContainer() function has
    the right to do so because the OutAttribute indicates that the parameter is to
    be returned from it.
  • Now due to the presence of the InAttribute, the existing
    contents of the managed ArrayContainer structure will be transformed
    into respective fields of the unmanaged version of the ArrayContainer
    structure which is then passed along to the ModifyArrayContainer()
    function.

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

static void DoTest_ModifyArrayContainer()
{
ArrayContainer ArrayContainerToModify = new ArrayContainer();
// Define and instantiate a managed array of 3
// TestStructure structs for the "array_of_test_structures"
// field.
ArrayContainerToModify.array_of_test_structures = new TestStructure[3]; // Assign simple values to the elements of the array.
for (int i = 0; i < ArrayContainerToModify.array_of_test_structures.Length; i++)
{
ArrayContainerToModify.array_of_test_structures[i].m_integer
= i;
ArrayContainerToModify.array_of_test_structures[i].m_double
= (double)i;
ArrayContainerToModify.array_of_test_structures[i].m_string
= string.Format("Hello World [{0}]", i);
} // Call on ModifyArrayContainer() to modify ArrayContainerToModify.
ModifyArrayContainer(ref ArrayContainerToModify); // Display the contents of the "array_of_test_structures" array
// inside ArrayContainerToModify.
for (int i = 0; i < ArrayContainerToModify.array_of_test_structures.Length; i++)
{
Console.WriteLine("ArrayContainerToModify.array_of_test_structures[{0}].m_integer : [{1}]",
i, ArrayContainerToModify.array_of_test_structures[i].m_integer);
Console.WriteLine("ArrayContainerToModify.array_of_test_structures[{0}].m_double : [{1}]",
i, ArrayContainerToModify.array_of_test_structures[i].m_double);
Console.WriteLine("ArrayContainerToModify.array_of_test_structures[{0}].m_string : [{1:S}]",
i, ArrayContainerToModify.array_of_test_structures[i].m_string);
}
}

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

  • An ArrayContainer structure “ArrayContainerToModify” is
    defined and instantiated.
  • The “array_of_test_structures” field of
    “ArrayContainerToModify” is instantiated to an array of 3 TestStructure
    structs.
  • Simple values are then assigned to the elements of the
    array.
  • The ModifyArrayContainer() function is then called with
    “ArrayContainerToModify” passed as a reference parameter.
  • Under the covers, when the ModifyArrayContainer() is
    called, an unmanaged counterpart of the “ArrayContainerToModify”
    structure is allocated by the interop marshaler and filled with
    actual field values based on those of the managed “ArrayContainerToModify”
    structure.
  • A pointer to this unmanaged “ArrayContainerToModify”
    structure is then passed to the ModifyArrayContainer() function.
  • When the ModifyArrayContainer() returns, the latest
    contents of the unmanaged “ArrayContainerToModify” structure is used to rebuild
    the managed “ArrayContainerToModify” structure.
  • The DoTest_ModifyArrayContainer() function then loops
    through the contents of the new “ArrayContainerToModify” structure and display
    all its new contents.

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

ArrayContainerToModify.array_of_test_structures[0].m_integer : [100]
ArrayContainerToModify.array_of_test_structures[0].m_double : [100]
ArrayContainerToModify.array_of_test_structures[0].m_string : [New string]
ArrayContainerToModify.array_of_test_structures[1].m_integer : [101]
ArrayContainerToModify.array_of_test_structures[1].m_double : [101]
ArrayContainerToModify.array_of_test_structures[1].m_string : [New string]
ArrayContainerToModify.array_of_test_structures[2].m_integer : [102]
ArrayContainerToModify.array_of_test_structures[2].m_double : [102]
ArrayContainerToModify.array_of_test_structures[2].m_string : [New string]
ArrayContainerToModify.array_of_test_structures[3].m_integer : [103]
ArrayContainerToModify.array_of_test_structures[3].m_double : [103]
ArrayContainerToModify.array_of_test_structures[3].m_string : [New string]

5. In Conclusion.

5.1 Here in part 6, we have looked at how managed
structures may be passed to and returned from unmanaged code bi-directionally by
way of a SAFEARRAY contained in an outer wrapping structure.

5.2 More good demonstrations of methods of the
IRecordInfo interface (RecordClear(), PutField()) was given. The
VariantClear() function was also used to completely clear a SAFEARRAY. I
certainly hope that the reader has benefitted from this.

5.3 In part 7, we shall shift gears again and up the
ante in complexity. We shall be explore marshaling a structure that contains an
array of ArrayContainer structures.

5.4 Noting that each ArrayContainer structue contains an
array of TestStructure structs, we are talking about marshaling a potentially
great number of TestStructure structs.

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

  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 5.

    1. Introduction. 1.1 In part 4, I have started to discuss how to interop marshal a managed array tha ...

  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. mysql事务之二:MySQL隔离级别演示

    登录mysql: mysql -u root -p123456 Mysql 版本号 mysql> select version(); +-------------------------+ | ...

  2. linux 定时脚本任务的创建

    参考资料https://my.oschina.net/xsh1208/blog/512810 定时脚本任务创建过程 1. 启动/终止 crontab 服务 一般使用这个命令/sbin/service ...

  3. 生成器+列表生成式,生成器可以节省内存,随时调取函数运行,以及实现多线程运行函数,__next__()和.send(参数)的区别,a,b=b,a+b其实是元祖的用法,出现异常状态用try...except StopIteration来处理

    列表生成式:是代码更简洁. 也可以是函数,比如func(i) 生成器:generator 列表生成式,是中括号,改成小括号,就是生成器: 如果你用列表生成式,生成一亿个数据:这里会卡好久,会生成一亿个 ...

  4. C#三层架构实例

    对于三层的概念查也查了,看也看了,下面是我找的一个关于三层的简单实例,真正看一下它是如何具体实现的. 我们先来一起看看 实体类-Model 实质:实体类就是在完成数据库与实体类对应的功能,一个类是一张 ...

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

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

  6. ceph 创建和删除osd

    ceph 创建和删除osd 1.概述         本次主要是使用ceph-deploy工具和使用ceph的相关命令实现在主机上指定磁盘创建和删除osd,本次以主机172.16.1.96(主机名ha ...

  7. angular结合echarts创建图表

    原理: 利用angularjs中的指令(directive)将echarts封装. 步骤: 1.封装函数: app.directive('line', function() { return { re ...

  8. leetcode888

    class Solution { public: int Binary_Search(vector<int> x, int N, int keyword) { , high = N - , ...

  9. day16-作业 后台管理

    二话不说开撸作业 作业要求: 后台管理平台 ,编辑表格:1. 非编辑模式:可对每行进行选择: 反选: 取消选择2. 编辑模式:进入编辑模式时如果行被选中,则被选中的行万变为可编辑状态,未选中的不改变退 ...

  10. 【原】Coursera—Andrew Ng机器学习—编程作业 Programming Exercise 3—多分类逻辑回归和神经网络

    作业说明 Exercise 3,Week 4,使用Octave实现图片中手写数字 0-9 的识别,采用两种方式(1)多分类逻辑回归(2)多分类神经网络.对比结果. (1)多分类逻辑回归:实现 lrCo ...