.NET и работа с неуправляемым кодом. Часть 1
.NET и работа с неуправляемым кодом
Сегодня я хочу показать один из способов работы с неуправляемым кодом, посредством специального класса Marshal. Большинство методов, определенных в этом классе, обычно используются разработчиками, которым нужно обеспечить сопряжение между моделями управляемого и неуправляемого программирования.
Маршалинг взаимодействия определяет, какие данные передаются в аргументах и возвращаемых значений методов между управляемой и неуправляемой памятью во время вызова. Маршалинг взаимодействия — это процесс времени выполнения, выполняемый службой маршалинга среды CLR.
Мне не хотелось бы полностью описывать всю структуру взаимодействия, т.к. это заняло бы значительную часть статьи. В этой статье я опишу принцип взаимодействия на конкретных примерах, опишу способы выделения и очистки выделенной памяти.
Для начала возьмём пример небольшой структуры, описанной в C и посмотрим, как сделать аналогичную структуру для C#.
Код на C
Код на C#
Как можете заметить, все указатели из C были заменения на тип IntPtr из C#. Двумерные массивы — на одномерные, аналогичной длины. А сама структура подписана аттрибутом [StructLayout]. Значение LayoutKind параметра Sequential используется для принудительного последовательного размещения членов в порядке их появления.
Для массивов необходимо указать их тип как UnmanagedType.ByValArray и сразу же указать их точный размер. Даже если размер самой переменной будет отличаться — при передаче, он автоматически будет уравнен в необходимый размер.
Вызов неуправляемого кода
Код на C
Код на C#:
Как вы наверное заметили, перед вызово необходимо сначало объявить все IntPtr. Для этого необходимо использовать примерно следующий код:
По аналогии, и для innerstr* pStruct, и для всех остальных указателей.
Вот и всё, всё просто. Теперь осталось из кода вызвать метод
В данном случае я перенес всю структуру в неуправляемую память, а затем передал ссылку на этот кусок, который затем в C был прочитан. Этого можно не делать, если передавать по ref, но я столкнулся с тем, что огромные структуры ref просто не мог перенести в неуправляемую память, а способ передавать ссылку работает всегда
Затем не забудьте почистить память. В отличие от управляемого кода, сборщик мусора не может чистить неуправляемый. Поэтому необходимо вызвать
PS: добавлено позже… аттрибут [StructLayout(LayoutKind.Sequential)], также может указывать на используемую таблицу символов, ANSI или UNICODE. Для этого необходимо написать [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)], [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] или [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]. По-умолчанию используется ANSI.
Ну вот и всё, на это первая часть статьи завершается. В следующей части я опишу, каким образом возможно использовать динамический размер массивом, как можно быстро преобразовать многомерные массивы в одномерные и наоборот, чтобы передавать в неуправляемый код, как организовать удобную для программиста структуру для прозрачной работы с маршалингом и некоторые другие
Обновлено
Добавлены исходники тестового проекта. Скачать
.NET и работа с неуправляемым кодом
Сегодня я хочу показать один из способов работы с неуправляемым кодом, посредством специального класса Marshal. Большинство методов, определенных в этом классе, обычно используются разработчиками, которым нужно обеспечить сопряжение между моделями управляемого и неуправляемого программирования.
Маршалинг взаимодействия определяет, какие данные передаются в аргументах и возвращаемых значений методов между управляемой и неуправляемой памятью во время вызова. Маршалинг взаимодействия — это процесс времени выполнения, выполняемый службой маршалинга среды CLR.
Мне не хотелось бы полностью описывать всю структуру взаимодействия, т.к. это заняло бы значительную часть статьи. В этой статье я опишу принцип взаимодействия на конкретных примерах, опишу способы выделения и очистки выделенной памяти.
Для начала возьмём пример небольшой структуры, описанной в C и посмотрим, как сделать аналогичную структуру для C#.
Код на C
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#
[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.
Как можете заметить, все указатели из C были заменения на тип IntPtr из C#. Двумерные массивы — на одномерные, аналогичной длины. А сама структура подписана аттрибутом [StructLayout]. Значение LayoutKind параметра Sequential используется для принудительного последовательного размещения членов в порядке их появления.
Для массивов необходимо указать их тип как UnmanagedType.ByValArray и сразу же указать их точный размер. Даже если размер самой переменной будет отличаться — при передаче, он автоматически будет уравнен в необходимый размер.
Вызов неуправляемого кода
Код на C
extern "C" __declspec(dllexport) int ExpFunc(test* s, bool message)
* This source code was highlighted with Source Code Highlighter.
Код на C#:
[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.
Как вы наверное заметили, перед вызово необходимо сначало объявить все IntPtr. Для этого необходимо использовать примерно следующий код:
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.
По аналогии, и для innerstr* pStruct, и для всех остальных указателей.
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.
Вот и всё, всё просто. Теперь осталось из кода вызвать метод
///////////////////////////////////////<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.
В данном случае я перенес всю структуру в неуправляемую память, а затем передал ссылку на этот кусок, который затем в C был прочитан. Этого можно не делать, если передавать по ref, но я столкнулся с тем, что огромные структуры ref просто не мог перенести в неуправляемую память, а способ передавать ссылку работает всегда
Затем не забудьте почистить память. В отличие от управляемого кода, сборщик мусора не может чистить неуправляемый. Поэтому необходимо вызвать
Marshal.FreeCoTaskMem(ptr);
для всех ссылок IntPtrPS: добавлено позже… аттрибут [StructLayout(LayoutKind.Sequential)], также может указывать на используемую таблицу символов, ANSI или UNICODE. Для этого необходимо написать [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)], [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] или [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]. По-умолчанию используется ANSI.
Ну вот и всё, на это первая часть статьи завершается. В следующей части я опишу, каким образом возможно использовать динамический размер массивом, как можно быстро преобразовать многомерные массивы в одномерные и наоборот, чтобы передавать в неуправляемый код, как организовать удобную для программиста структуру для прозрачной работы с маршалингом и некоторые другие
Обновлено
Добавлены исходники тестового проекта. Скачать