Marshal variable length array of structs from C to VB.NET
Introduction
This article presents an example of how to create a VB.NET program that retrieves data in the form of a variable length array of structs from a DLL file written in C. The challenge here is how to pass data between the safe world of managed code executed by the Common Language Runtime (CLR) and unmanaged code.
Background
The code which is developed using the .NET Framework is known as managed code. This code is directly executed by CLR with the help of managed code execution. Any code that is written in .NET Framework is managed code. Managed code uses CLR which in turns looks after your applications by managing memory, handling security, allowing cross-language debugging, and so on. Code which is developed outside the .NET Framework is known as unmanaged code. Applications that do not run under the control of the CLR are said to be unmanaged, and therefore don’t have the benefits of the CLR. I won’t get any deeper into the definitions of managed and unmanaged code since that is out of the scope for this article and there are plenty of sources for that on the internet. Just Google it :)
Marshalling
The solution for passing data between managed and unmanaged code is of course marshalling. What made the assignment a bit tricky was that some of the unmanaged functions returned an array of structs whose size I didn’t know when I was calling the function.
The Code
First I begin with the unmanaged DLL. Here is the struct declaration in C:
typedef struct {
Char id1[10];
Char id2[20];
}unmanagedStruct;
Here is the declaration of the function:
extern "C" __declspec(dllexport) unmanagedStruct* unmanagedFunction(int &size)
{
unmanagedStruct *outPrivileges = new
unmanagedStruct[myStructSize];
size = myStructSize;
//Do whatever your function is supposed to do
return outPrivileges;
}
As you can see, the function returns a pointer since what I need on the managed side is a pointer to the unmanaged memory I have allocated. The next thing to note is that I am taking the integer size as a reference call. This variable enables me to get the size of the array on the managed side. How the elements of the array are retrieved will be explained in detail when I present the managed code. Now I will just show the relevant parts of the unmanaged function. Of course the function should do a lot more such as fill the array with values and so on, but that is out of the scope for this article. So as you can see, I am declaring my array of structs and allocating memory for it. Then I set the size
variable to the size of the array so that the managed code will be able to know the size. Finally, I return the pointer to the caller of the function.
Now on the managed side, in Visual Studio, I start by declaring the struct I intent to use. Notice that I am using the MarshalAs
attribute class to specify the length of my fields in the structure. These lengths have to be the same as the lengths declared in the unmanaged DLL.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure managedStruct
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=10)> _
Public id1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=20)> _
Public id2 As String
End Structure
I continue by declaring a DllImport to my unmanaged DLL and the function which I intend to use.
<DllImport("myUnmanagedDll.dll")> _
Public Shared Function unmanagedFunction(ByRef size As Integer) As IntPtr
End Function
And finally, here comes the part where I call the unmanaged function from my managed code, retrieve the pointer to the unmanaged array of structs, and retrieve the values.
Dim p1 As IntPtr 'My struct pointer
'pointer to be used to cleanup the unmanaged memory
Dim cleanUpPtr as IntPtr
Dim numberOfResults As Integer 'Size of array from dll
Dim resultArray() As managedStruct
'The array where i will store the retrieved values
'Set the cleanup pointer to point on the same memory block
cleanUpPtr = p1
'Call the unmanaged function and get intpointer
p1 = unmanagedFunction(numberOfResults)
ReDim resultArray(numberOfResults - 1) 'Resize my array
Dim size As Integer = Marshal.SizeOf(GetType(managedStruct))
Dim start As Integer = (CType(p1, Integer) + _
(Marshal.SizeOf(GetType(managedStruct)) - size))
p1 = New IntPtr(start) 'Set the pointer at the first element of array
Dim i As Integer = 0
Do While (i < numberOfResults)
resultArray(i) = CType(Marshal.PtrToStructure(p1, _
GetType(managedStruct)), managedStruct)
If (i < (count - 1)) The
p1 = New IntPtr((CType(p1, Integer) + size))
End If
i = (i + 1)
Loop
After the call to the unmanaged function, I have the number of posts I have retrieved in the array. In the variable called size
, I store the size of every array element so I know how many bytes I have to increment to retrieve the next post. Then I proceed to set the pointer p1
to the first array element, and finally use Marshal.PtrToStructure
to retrieve the values. Once I have retrieved an array element, I continue to set the pointer to the next element. This step is repeated until I have retrieved all the elements and now I have the entire unmanaged array in the managed memory.
Now as a last thing, remember to clean up the unmanaged memory. That’s why I created an extra pointer called cleanupPtr
since I will loop over the array with the original pointer. cleanUpPtr
is still pointing to the beginning of the unmanaged memory. The cleanup operation can be performed directly from the managed memory by using the CoTaskMemFree()
attribute.
Final Words
As a final word, I would like to answer the question I guess many of you would ask: Why do it in VB.NET, and the answer to that question is that was part of the requirement when I got the task. If it was up to me, I would have done it in C#. I am a C# guy.
Post Comment
As a Newbie, I am continuously exploring online for articles that can be of assistance to me.
aAIOB4 When Someone googles something that relates to one of my wordpress blogs how can I get it to appear on the first page of their serach results?? Thanks!.
maxIBW Hello.This post was extremely motivating, especially because I was investigating for thoughts on this matter last week.
wzo0mc It's hard to search out knowledgeable folks on this matter, but you sound like you recognize what you're talking about! Thanks
4aPLym Normally I do not learn article on blogs, however I wish to say that this write-up very compelled me to check out and do it! Your writing style has been surprised me. Thanks, very great article.
nL9FGm Very nice info and right to the point. I don't know if this is in fact the best place to ask but do you folks have any ideea where to employ some professional writers? Thank you :)
8Hythe Very neat blog.Really looking forward to read more.
Q9uCox Thank you for your blog.Thanks Again. Fantastic.
XBdc2w Thank you ever so for you article post.Much thanks again. Really Great.
s70G53 Major thanks for the blog.Thanks Again. Great.
LX4SKe Im grateful for the article post.Really looking forward to read more. Will read on...
XbeGxN Very good post.Really looking forward to read more. Want more.
Wcj79e Really informative blog article.Really thank you!
x7cLLG Say, you got a nice blog post.Thanks Again. Will read on...
X0EnES Enjoyed every bit of your article post.Really thank you! Cool.
d22kLn I cannot thank you enough for the blog article.Really thank you! Awesome.