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
:

[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[Guid("42D386A1-AAE1-445e-A755-00AA7B2C1753")]
public struct ArrayContainer
{
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)]
public TestStructure[] array_of_test_structures;
}

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 :

tlbexp CSConsoleApp.exe /out:CSConsoleApp.tlb

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

// Generated .IDL file (by the OLE/COM Object Viewer)
// [
uuid(4E765D3B-C1F5-4CDE-8095-1E9614E0AE3F),
version(1.0)
]
library CSConsoleApp
{
// TLib : // Forward declare all types defined in this typelib
typedef
[
uuid(42D386A1-AAE1-445E-A755-00AA7B2C1753), version(1.0) ,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSConsoleApp.ArrayContainer")
]
struct tagArrayContainer
{
SAFEARRAY(TestStructure) array_of_test_structures;
} ArrayContainer; typedef
[
uuid(1979BCD7-1062-44D8-B3FC-A2686C61E715), version(1.0) ,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSConsoleApp.TestStructure")
]
struct tagTestStructure
{
long m_integer;
double m_double;
BSTR m_string;
} TestStructure;
};

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 :

extern "C" __declspec(dllexport) void __stdcall SetArrayContainer
(
/*[in]*/ ArrayContainer array_container
)
{
std::vector<TestStructure> vecTestStructure; // Copy the TestStructure elements of the SAFEARRAY
// in "array_container.array_of_test_structures"
// into a vector of TestStructure structs.
CopySafeArrayToVector<TestStructure, VT_RECORD>
(
array_container.array_of_test_structures,
vecTestStructure
); // Display the field values of each TestStructure
// within the "vecTestStructure" vector.
std::for_each
(
vecTestStructure.begin(),
vecTestStructure.end(),
display_test_structure()
); // Obtain the IRccordInfo interface associated with
// the TestStructure UDT.
IRecordInfoPtr spIRecordInfoTestStructure = NULL; SafeArrayGetRecordInfo
(
array_container.array_of_test_structures,
&spIRecordInfoTestStructure
); // Before the end of this function, each of the
// TestStructure structs inside vecTestStructure
// must be cleared.
std::for_each
(
vecTestStructure.begin(),
vecTestStructure.end(),
clear_test_structure(spIRecordInfoTestStructure)
);
}

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 :

template <class T, VARTYPE v>
void CopySafeArrayToVector
(
/*[in]*/ SAFEARRAY*& pSafeArray,
/*[out]*/ std::vector<T>& vecReceiver
)
{
VARTYPE vt; SafeArrayGetVartype(pSafeArray, &vt); // We assert that the Variant Type is "v".
_ASSERT(vt == v); long LBound = 0;
long UBound = 0; SafeArrayGetLBound(pSafeArray, 1, &LBound);
SafeArrayGetUBound(pSafeArray, 1, &UBound); ULONG ulSafeArraySize = UBound - LBound + 1; for (ULONG ulIndex = 0; ulIndex < ulSafeArraySize; ulIndex++)
{
long lIndexVector[1];
T data;
HRESULT hrRetTemp; lIndexVector[0] = ulIndex; // Must initialize memory area of "data"
// before calling SafeArrayGetElement().
memset(&data, 0, sizeof(T)); hrRetTemp = SafeArrayGetElement
(
(SAFEARRAY*)pSafeArray,
(long*)lIndexVector,
(void*)(&data)
); if (SUCCEEDED(hrRetTemp))
{
vecReceiver.push_back(data);
}
}
}

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 :

struct display_test_structure :
public std::unary_function<TestStructure, void>
{
// Constructor
display_test_structure() :
m_iCounter(0)
{
} // Copy constructor.
display_test_structure(const display_test_structure& rhs) :
m_iCounter(rhs.m_iCounter)
{
} void operator () (TestStructure& test_structure)
{
printf ("TestStructure[%d].m_integer : [%d]\r\n",
m_iCounter, test_structure.m_integer);
printf ("TestStructure[%d].m_double : [%f]\r\n",
m_iCounter, test_structure.m_double);
printf ("TestStructure[%d].m_string : [%S]\r\n",
m_iCounter, test_structure.m_string);
m_iCounter++;
}; int m_iCounter;
};

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 :

struct clear_test_structure :
public std::unary_function<TestStructure, void>
{
// Constructor.
clear_test_structure(IRecordInfoPtr& refIRecordInfoPtr) :
m_refIRecordInfoPtr(refIRecordInfoPtr)
{
} // Copy constructor.
clear_test_structure(const clear_test_structure& rhs) :
m_refIRecordInfoPtr(rhs.m_refIRecordInfoPtr)
{
} void operator () (TestStructure& test_structure)
{
m_refIRecordInfoPtr -> RecordClear((PVOID)&test_structure);
} IRecordInfoPtr& m_refIRecordInfoPtr;
};

4. Example C# Call to
SetArrayContainer().

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

[DllImport("UnmanagedDll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void SetArrayContainer([In] ArrayContainer array_container);

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 :
struct ArrayContainer
{
SAFEARRAY * array_of_test_structures;
};
  • 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() :

static void DoTest_SetArrayContainer()
{
ArrayContainer array_container = new ArrayContainer(); array_container.array_of_test_structures = new TestStructure[3]; for (int i = 0; i < array_container.array_of_test_structures.Length; i++)
{
array_container.array_of_test_structures[i].m_integer = i;
array_container.array_of_test_structures[i].m_double = (double)i;
array_container.array_of_test_structures[i].m_string = string.Format("Hello World [{0}]", i);
} SetArrayContainer(array_container);
}

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 :

TestStructure[0].m_integer : [0]
TestStructure[0].m_double : [0.000000]
TestStructure[0].m_string : [Hello World [0]]
TestStructure[1].m_integer : [1]
TestStructure[1].m_double : [1.000000]
TestStructure[1].m_string : [Hello World [1]]
TestStructure[2].m_integer : [2]
TestStructure[2].m_double : [2.000000]
TestStructure[2].m_string : [Hello World [2]]

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.

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

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

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

  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. Python函数-map()

    map()函数 map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回.如下: def ...

  2. spark远程调试

    基本流程1.远程运行spark,打开Spark master机器的JVM的jdwp,让其阻塞监听指定端口(8888),让其有终端向指定端口发送特定请求再执行:2.IntelliJ配置socket远程连 ...

  3. UIAlertController UIAlertView用法

    项目中很多地方会出现弹出框框,来做个判断 基本方法如下 UIAlertController *alertC = [UIAlertController alertControllerWithTitle: ...

  4. Oracle 闪回归档(Flashback Database)

    cmd --管理员身份打开 sqlplus / as sysdba --管理数据库 shu immediate; --独占方式开始 startup mount --修改日期模式 alter datab ...

  5. 如果有多个集合的迭代处理情况【使用MAP】

    在SQL开发过程中,动态构建In集合条件查询是比较常见的用法,在Mybatis中提供了foreach功能,该功能比较强大,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内.它也允许你指 ...

  6. 蓝桥杯 算法训练 ALGO-149 5-2求指数

     算法训练 5-2求指数   时间限制:1.0s   内存限制:256.0MB 问题描述 已知n和m,打印n^1,n^2,...,n^m.要求用静态变量实现.n^m表示n的m次方.已知n和m,打印n^ ...

  7. mongodb collection method

    https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/ db.coll_test.getIndexes()# ...

  8. stdin和STDIN_FILENO的区别

    STDIN_FILENO与stdin的区别: STDIN_FILENO: 1).数据类型:int 2).层次:系统级的API,是一个文件句柄,定义在<unistd.h>中. 3).相应的函 ...

  9. 开发环境入门 linux基础 (部分)awk 赋值变量 if

    awk 常用于处理格式非常明显的文件 awk -F: '{print $1}' /etc/passwd  含义:取冒号分隔符的第一段内容 $0 指取所有! NF 指有几段内容 $NF 取最后一段内容 ...

  10. iOS 给Main.storyboard 添加button 事件《转》

    XCODE中使用Main.Storyboard拉入控件并实现事件(Swift语言)   如何在XCODE中的Main.Storyboard内拉入控件并实现一个简单的效果呢?本人由于刚接触Swift语言 ...