.NET and working with unmanaged code. Part 1.NET and working with unmanaged code
Today I want to show one of the ways to work with unmanaged code, through a special class Marshal. Most of the methods defined in this class are typically used by developers who need to interface between the models of controlled and unmanaged programming.
')
Interaction marshaling determines which data is passed in the arguments and return values ​​of the methods between managed and unmanaged memory during a call. Interaction marshaling is a run-time process performed by the marshaling service of the CLR.
I would not like to fully describe the entire structure of the interaction, since this would take a large part of the article. In this article I will describe the principle of interaction with specific examples, I will describe how to allocate and clean up the allocated memory.
First, take an example of a small structure described in C and see how to make a similar structure for C #.
C codestruct test
{
struct innerstr
{
char str[300];
int Int;
int * in_pInt;
} in ;
char str[2][50];
int IntArr[10];
int * pInt;
innerstr* pStruct;
int * ptr;
};
* This source code was highlighted with Source Code Highlighter .
C # code[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public Innerstr _in;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50 * 2)]
public char [] str;;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int [] IntArr;
public IntPtr pInt;
public IntPtr pStruct;
public IntPtr ptr;
[StructLayout(LayoutKind.Sequential)]
public struct Innerstr
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 300)]
internal string str;
public int _Int;
public IntPtr in_pInt;
}
}
* This source code was highlighted with Source Code Highlighter .
As you can see, all the pointers from C were replaced with the IntPtr type from C #. Two-dimensional arrays - on one-dimensional, similar length. And the structure itself is signed with the [StructLayout] attribute. The Sequential parameter's LayoutKind value is used to force the sequential placement of members in the order they appear.
For arrays, you must specify their type as UnmanagedType.ByValArray and immediately specify their exact size. Even if the size of the variable itself will be different - during transmission, it will automatically be equalized to the required size.
Calling unmanaged codeC code
extern "C" __declspec(dllexport) int ExpFunc(test* s, bool message)
* This source code was highlighted with Source Code Highlighter .
C # code:
[ return :MarshalAs(UnmanagedType.I4)]
[DllImport( "TestTkzDLL.dll" )]
public static extern int ExpFunc([In, Out] IntPtr c, [In] bool message);
* This source code was highlighted with Source Code Highlighter .
As you probably noticed, before calling you must first declare all IntPtr. To do this, use the following code:
Test test = new Test();
...
// => int* pInt
int _pInt = 2010; //
IntPtr _pInt_buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(_pInt)); //
Marshal.StructureToPtr(_pInt, _pInt_buffer, false ); //
test.pInt = _pInt_buffer; //
* This source code was highlighted with Source Code Highlighter .
By analogy, and for innerstr * pStruct, and for all other pointers.
Test.Innerstr inner2 = new Test.Innerstr();
IntPtr _pStruct_buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(inner2));
Marshal.StructureToPtr(inner2, _pStruct_buffer, false );
test.pStruct = _pStruct_buffer;
* This source code was highlighted with Source Code Highlighter .
That's all, everything is simple. Now it is necessary to call the method from the code.
/////////////////////////////////////// <br ?>
// ( , )
/////////////////////////////////////
IntPtr ptr1 = Marshal.AllocCoTaskMem(Marshal.SizeOf(test));
Marshal.StructureToPtr(test, ptr1, false );
int retInt = ExpFunc(ptr1, false ); //
test = (StartClass.Test)Marshal.PtrToStructure(ptr1, typeof (StartClass.Test)); ///
* This source code was highlighted with Source Code Highlighter .
In this case, I transferred the entire structure to unmanaged memory, and then passed a link to this piece, which was then read in C. You can not do this if you pass by ref, but I was faced with the fact that huge ref structures simply could not be transferred to unmanaged memory, and the way to pass a link always works
Then do not forget to clean the memory. Unlike managed code, the garbage collector cannot clean unmanaged. Therefore, you must call
Marshal.FreeCoTaskMem(ptr);
for all IntPtr links
PS: added later ... the [StructLayout (LayoutKind.Sequential)] attribute can also indicate the symbol table used, ANSI or UNICODE. To do this, write [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)], [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)] or [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Auto)]. ANSI is used by default.
Well, that's all, this is the first part of the article ends. In the next part, I will describe how it is possible to use a dynamic size array, how to quickly convert multidimensional arrays into one-dimensional and vice versa, to transfer to unmanaged code, how to organize a convenient for programmer structure for transparent marshaling and some other
UpdatedAdded test project sources.
Download