.NET and work with unmanaged code. Part 1

    .NET and work with unmanaged code. Part 1.

    NET and work 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 commonly used by developers who need to provide a interface between the managed and unmanaged programming models.

    Interaction marshaling determines what data is passed in the arguments and return values ​​of methods between managed and unmanaged memory during a call. Interaction marshaling is a run-time process performed by the CLR marshaling service.

    I would not want to fully describe the entire structure of the interaction, because this would take up a significant part of the article. In this article I will describe the principle of interaction with specific examples, I will describe how to allocate and clear allocated memory.


    To get started, take an example of a small structure described in C and see how to make a similar structure for C #.

    C code

    struct 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, of similar length. And the structure itself is signed with the [StructLayout] attribute. The Sequential parameter's LayoutKind value is used to force members to be sequentially arranged in the order they appear.

    For arrays, you must specify their type as UnmanagedType.ByValArray and immediately indicate 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.

    Call unmanaged code

    C 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 approximately 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.


    Similarly, 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, it’s all simple. Now it remains to call the method from the code

      ///////////////////////////////////////

      // ГЕНЕРИРУЕМ ЗАПРОС (способ с маршилингом данных в память, затем передачей ссылки)
      /////////////////////////////////////
      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’t do this if you pass it 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);IntPtr for all links.

    PS: added later ... the [StructLayout (LayoutKind.Sequential)] attribute can also point to the character 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)]. The default is ANSI.



    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 dynamic size with an array, how you can quickly convert multidimensional arrays to one-dimensional arrays, and vice versa, to transfer to unmanaged code, how to organize a programmer-friendly structure for transparent work with marshaling, and some others


    Updated
    Added source code for a test project . Download


    Also popular now: