Write a Function to Read in an Array of Strings C++
ane. Introduction.
1.ane I've previously written a web log entry expounding on ways to return a C-style cord from C++ unmanaged code to C#.
1.2 Besides those that return a single string, APIs that return an array of strings are besides never short in demand.
ane.3 In this 2-role series of blogs I shall be discussing ways to return an array of C-manner strings from a C++ API to C#.
one.4 This first part presents a very low-level technique which involves manual marshaling that tin be rather convoluted. The aim is to show how memory is transferred from unmanaged code to managed code.
one.5 The second part presents a simpler and recommended method that uses automated marshaling served by the interop marshaler.
1.6 The techniques presented in the offset office serves as a knowledge base for understanding the low-level activities that are performed nether the covers by the interop marshaler which is used in the 2nd role.
two. Returning an Array of Strings via a Pointer to an Array of Char Pointers.
2.one An API that returns an array of strings must provide 2 of import entities :
-
A pointer to an array of strings.
-
An interger indicating how many strings there are in the array.
2.2 Furthermore, considering the array of strings is returned from the API to the client application, the protocol is such that the API will classify the retentivity for the array and its string contents and the client (which is said to ain this returned array) is responsible for freeing this memory.
2.3 Equally mentioned in the introduction, the low-level technique presented in this office does not use the services of the interop marshaler. The marshaling of strings from unmanaged memory to managed retentivity is done completely manually. This being the example, the manner memory is allocated on the C++ side is crucial in ensuring smoothen string data transformation (from unmanaged string buffers to C# strings) and eventual memory freeing.
2.iv The delaration of the C++ role should be something like the post-obit :
__declspec(dllexport) void GenerateStringsAndStoreInStringBuffer ( /*[out]*/ char*** ppStringBufferReceiver, /*[out]*/ int* piStringsCountReceiver );
Note well that the showtime parameter "ppStringBufferReceiver" which is to receive the array of C-style string pointers, is really a pointer to a pointer to a arrow of char. There are iii '*'s.
two.5 This is because the C++ function must perform the post-obit :
- Allocate memory to hold an array of char pointers.
- Then for each char pointer (char*) in the array, retentiveness must be allocated to hold a graphic symbol buffer and each character pointer must bespeak to this buffer.
2.6 The post-obit is a sample implementation of GenerateStringsAndStoreInStringBuffer() :
__declspec(dllexport) void __stdcall GenerateStringsAndStoreInStringBuffer ( /*[out]*/ char*** ppStringBufferReceiver, /*[out]*/ int* piStringsCountReceiver ) { char* MyStrings[] = { "A", "AB", "ABC", "ABCD", "ABCDE", "ABCDEF", "ABCDEFG", "ABCDEFGH", "ABCDEFGHI", "ABCDEFGHIJ" }; *piStringsCountReceiver = 10; size_t stSizeOfArray = sizeof(char*) * ten; *ppStringBufferReceiver = (char**)::CoTaskMemAlloc(stSizeOfArray); memset (*ppStringBufferReceiver, 0, stSizeOfArray); for (int i = 0; i < 10; i++) { (*ppStringBufferReceiver)[i] = (char*)::CoTaskMemAlloc(strlen(MyStrings[i]) + 1); strcpy((*ppStringBufferReceiver)[i], MyStrings[i]); } return; }
This API volition return a pointer to an array of char*. Each char* points to a copy of a string from the array :
char* MyStrings[] = { "A", "AB", "ABC", "ABCD", "ABCDE", "ABCDEF", "ABCDEFG", "ABCDEFGH", "ABCDEFGHI", "ABCDEFGHIJ" };
2.vii When GenerateStringsAndStoreInStringBuffer() completes, the memory layout of the C-style cord array pointed to by ppStringBufferReceiver is equally follows :
2.8 This API uses a rather convoluted manner of retentivity allocation. Nevertheless, information technology must be adhered to. I will explicate why in the next section where the C# client code volition be listed and discussed.
3. The C# Customer Code.
3.1 Allow's wait at how the GenerateStringsAndStoreInStringBuffer() function is to be alleged in C# :
[DllImport("TestDLL01.dll", CallingConvention = CallingConvention.StdCall)] public static extern void GenerateStringsAndStoreInStringBuffer ( out IntPtr UnmanagedStringArray, out int iStringsCountReceiver );
The parameter that will receive the array of unmanaged strings is declared equally an IntPtr.
iii.2 A sample call to this role is listed below :
int iStringsCountReceiver = 0; IntPtr UnmanagedStringArray = IntPtr.Naught; GenerateStringsAndStoreInStringBuffer(out UnmanagedStringArray, out iStringsCountReceiver);
When the GenerateStringsAndStoreInStringBuffer() office completes, UnmanagedStringArray volition internally point to unmanaged memory which is actually an array of char pointers.
Note well : UnmanagedStringArray does not betoken to any actual cord. It points to an array of char pointers. It is these char pointers that each bespeak to a C-style string.
iii.three Next comes the tough part : we must transform the data that UnmanagedStringArray points to and transform information technology into an array of C# strings. The post-obit must be done :
-
For each char pointer in "UnmanagedStringArray", we use the C-style string that it points to and create a C# cord object from it. Nosotros know how many char pointers there are within "UnmanagedStringArray" – the iStringsCountReceiver variable receives this number – then we can do this inside a for loop.
-
Afterwards the C# string creations are washed, the private unamanged char buffers (for each cord) must be freed. This must be done via Align.FreeCoTaskMem(). The employ of Marshal.FreeCoTaskMem() is important because it matches how memory was allocated within GenerateStringsAndStoreInStringBuffer() which is by CoTaskMemAlloc().
-
Then, after all char pointers have been freed, "UnmanagedStringArray" itself must be freed. Call back that "UnmanagedStringArray" is also an array (of unmanaged char pointers). Once again this is done by Align.FreeCoTaskMem().
3.iv The tasks described in signal 3.iii above can be encapsulated inside a C# office as listed below :
// This method transforms an array of unmanaged character pointers (pointed to by pUnmanagedStringArray) // into an assortment of managed strings. // // This method also destroys each unmanaged character pointers and volition also destroy the assortment itself. static void MarshalUnmananagedStrArray2ManagedStrArray ( IntPtr pUnmanagedStringArray, int StringCount, out cord[] ManagedStringArray ) { IntPtr[] pIntPtrArray = new IntPtr[StringCount]; ManagedStringArray = new string[StringCount]; Align.Copy(pUnmanagedStringArray, pIntPtrArray, 0, StringCount); for (int i = 0; i < StringCount; i++) { ManagedStringArray[i] = Marshal.PtrToStringAnsi(pIntPtrArray[i]); Align.FreeCoTaskMem(pIntPtrArray[i]); } Marshal.FreeCoTaskMem(pUnmanagedStringArray); }
Substantially we perform a contrary of the actions performed in the GenerateStringsAndStoreInStringBuffer() API.
3.5 I hope that the reader volition sympathise why nosotros must perform such elaborate memory allocation and deallocation. It is because the C# side does not know how many strings will exist allocated on the C++ side and how long each string will be.
three.6 Every bit such, the GenerateStringsAndStoreInStringBuffer() function must return an integer telling how many unmanaged strings have been allocated. Furthermore, each unmanaged string must exist separately allocated and so that on the C# side, each can exist separately freed.
3.7 The following C# lawmaking demonstrates a complete phone call to GenerateStringsAndStoreInStringBuffer() and a subsequent call to MarshalUnmananagedStrArray2ManagedStrArray() to create managed strings from the returned unmanaged character pointers :
static void Call_ByIntPtr() { int iStringsCountReceiver = 0; IntPtr UnmanagedStringArray = IntPtr.Zippo; GenerateStringsAndStoreInStringBuffer ( out UnmanagedStringArray, out iStringsCountReceiver ); string[] ManagedStringArray = zilch; MarshalUnmananagedStrArray2ManagedStrArray ( UnmanagedStringArray, iStringsCountReceiver, out ManagedStringArray ); for (int i = 0; i < ManagedStringArray.Length; i++) { Console.WriteLine("{0:S}", ManagedStringArray[i]); } }
3.8 When the Call_ByIntPtr() is run, the following console output will exist displayed :
A AB ABC ABCD ABCDE ABCDEF ABCDEFG ABCDEFGH ABCDEFGHI ABCDEFGHIJ
three.9 After MarshalUnmananagedStrArray2ManagedStrArray() is called, ManagedStringArray will comprise an array of C# strings each containing a re-create of a corresponding unmanaged string. Each unmanaged string, and the unmanaged array pointed to past UnmanagedStringArray will be destroyed.
4. In Conclusion.
4.1 This part i has presented a rigorous low-level string array allotment technique.
iv.two In part 2, a simpler and largely automatically supported marshaling technique will be presented. In part ii, I shall frequently refer to the low-level marshaling operations of part i for comparison purposes.
jamisonwouturairim.blogspot.com
Source: https://limbioliong.wordpress.com/2011/08/14/returning-an-array-of-strings-from-c-to-c-part-1/
0 Response to "Write a Function to Read in an Array of Strings C++"
Post a Comment