[转]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 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;
};
- 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 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.的更多相关文章
- [转]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 ...
- [转]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 ...
- [转]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 ...
- [转]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 ...
- [转]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 ...
- [转]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 ...
- [转]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 ...
- [转]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 ...
- Passing JavaScript Objects to Managed Code
Silverlight If the target managed property or input parameter is strongly typed (that is, not typed ...
随机推荐
- xml处理模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的 ...
- StringBuilder与StringBuffer的区别
相信大家看到过很多比较String和StringBuffer区别的文章,也明白这两者的区别,然而自从Java 5.0发布以后,我们的比较列表上将多出一个对象了,这就是StringBuilder类.St ...
- yield-from示例
#!/usr/bin/python3# -*- coding: utf-8 -*-# @Time : 2018/6/20 9:13# @File : yield_from11.py fro ...
- krpano之背景音乐
步骤: 1.添加音乐控制插件 <!-- START:音乐控制 --> <plugin name="soundinterface" url="%SWFPA ...
- MySQL MVCC原理
1 MVCC基本原理 MVCC:多版本并发控制(MVCC,Multiversion Currency Control).一般情况下,事务性储存引擎不是只使用表锁,行加锁的处理数据,而是结合了MVCC机 ...
- wordpress 基础文件
需要用到的PHP基础文件有: 404.php 404模板 rtl.css 如果网站的阅读方向是自右向左的,会被自动包含进来 comments.php 评论模板 single.php 文章模板.显 ...
- android 除法运算保留小数点
java保留两位小数问题: 方式一: 四舍五入 double f = 111231.5585; BigDecimal b = new BigDecimal(f); double f1 = b.setS ...
- Solr4.3---4.6删除数据的办法
Solr4.6的管理界面上,如果不配置数据导入的功能,将看不到清除数据的按钮.我表示很遗憾,正好我们线上没有配置数据导入的功能. 网上搜到的各种清理solr数据的HTTP请求,拿到我的solr4.6上 ...
- mask rcnn训练自己的数据集参考文章(推荐)
最近用Mask_RCNN训练模型,下面几篇文章提供了不少帮助,汇总出来,方便以后查找,并向几位博主老师表示感谢 https://blog.csdn.net/qq_29462849/article/de ...
- Visual Studio + C# + Xamarin = iOS/Android/Windows Apps
Visual Studio 跨平台開發實戰 (1) -- Hello Xamarin! 前言 應用程式發展的腳步,從來沒有停過.從早期的 Windows 應用程式, 到網路時代的 web 應用程式,再 ...