Атрибут DllImport Platform Invocation Services (PInvoke) поддерживает средства для вызова кода неуправляемых функций, которые находятся в DLL. При вызове неуправляемой функции из DLL компилятор C# должен иметь информацию • о прототипе неуправляемой функции; • в какой Dll-библиотеке находится функция; • как передаются параметры и возвращаемое значение. Атрибут DllImport дает возможность связать статический метод класса с внешними функциями из DLL. [AttributeUsage(AttributeTargets.Method)] public sealed class DllImportAttribute : Attribute Атрибут DllImport имеет один обязательный параметр типа string – имя библиотеки динамической компоновки. Остальные параметры атрибута – необязательные (именованные). Сервис PInvoke Platform Invocation Services (PInvoke) использует метаданные для поиска экспортируемой функции и маршалинга параметров в период выполнения. Рисунок из раздела MSDN – A Closer Look at Platform Invoke Platform Invocation Services (PInvoke) загружает Dll-библиотеку в память, размещает параметры в стеке, передает исключения из неуправляемого кода в управляемый. Взаимодействие управляемого и неуправляемого кода Sonja Keserovic, David Mortenson, Adam Nathan An Overview of Managed/Unmanaged Code Interoperability Microsoft Corporation, October 2003 Атрибут DllImport. Пример using System; using System.Runtime.InteropServices; namespace PInvoke_Demo { class Class1 { static void Main(string[] args) { bool res; try { double par1 = 3; double par2 = 0; res = F1(par1, ref par2); } catch (System.Runtime.InteropServices.ExternalException ex ) { Console.WriteLine(ex.Message);} } [DllImport("DLL_ForCSharp.dll")] public static extern bool F1(double par1, ref double par2); }} Атрибут DllImport. Пример. Код C++ extern "C“ { _declspec(dllexport) bool F1(double d1, double& d2) { if ( d1<100) {d2 = d1*2; d1 = -1; } else throw 1; return true; }} При создании Dll-файла компоновщик добавляет в исполняемый код раздел экспорта – таблицу экспортируемых функций. Экспортируемая функция • либо имеет модификатор _declspec(dllexport); • либо находится в разделе EXPORTS файла определения модуля (*.def); extern “C” – спецификация для компоновщика, определяет соглашения о вызове функций (calling convention). Список функций, которые экспортирует Dll, можно посмотреть с помощью утилиты dumpbin.exe. Каталог Program Files \ Microsoft Visual Studio 8 \ VC \ bin Именованные параметры атрибута DllImport Некоторые именованные параметры атрибута DllImport: string EntryPoint Имя входной точки в Dll-библиотеке. По умолчанию – имя метода, к которому прикреплен атрибут. CallingConvention CallingConvention Значение перечисления CallingConvention, которое определяет соглашение о вызове функций, используемое для входной точки Dll-библиотеки. По умолчанию – CallingConvention. WinAPI. CharSet CharSet Значение перечисления CharSet, которое определяет набор символов, используемых во входной точке Dllбиблиотеки. По умолчанию – CharSet.Auto, т.е. при маршалинге в зависимости от OC выбирается Unicode или ANSI. bool ExactSpelling Умолчание false. При значении true EntryPoint должно точно совпадать с именем точки входа (для Win32 API). CallingConvention Значение параметра CallingConvention определяет соглашение о вызове функций, используемое для входной точки Dll-библиотеки. Значение параметра должно отвечать тому соглашению, которое использовалось при компиляции библиотеки. Соглашение о вызове функций определяет • порядок, в котором параметры функции размещаются в стеке; • какая из функций – вызывающая или вызываемая - удаляет параметры из стека при завершении вызова; • внутреннее имя для функции ( decorated name). При создании Dll-библиотеки соглашения о вызове функции определяются опциями компилятора, которые устанавливают соглашения для всех функций, для которых они не указаны явно. В коде C/C++ можно явно указать соглашения с помощью ключевых слов __cdecl __stdcall __fastcall CallingConvention -2 Значения перечисления CallingConvention Cdecl Соглашения С StdCall Соглашения Win32 API (умолчание при вызове неуправляемого метода) ThisCall Соглашения С++ для методов классов Winapi Умолчание для платформы, например, Cdecl для Windows CE. FastCall .Net Framework 2.0 не поддерживается. Атрибут MarshalAs Атрибут MarshalAs определяет, как передаются данные между управляемым и неуправляемым кодом. Используется в основном при взаимодействии с COMобъектами. Для каждого примитивного .NET типа есть принятый по умолчанию соответствующий тип в неуправляемом коде. Атрибут MarshalAs дает возможность переопределить эти соглашения. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] public sealed class MarshalAsAttribute : Attribute Атрибут MarshalAs имеет один обязательный параметр – значение перечисления UnmanagedType и 7 именованных (необязательных). Перечисление UnmanagedType имеет более 30 полей. Copying and pinning Маршалинг (marshaling) – упаковка данных в стандартный формат для обмена между COM-объектами. При маршалинге данные могут копироваться (copying) или прикрепляться (pinning). copying (копирование) interop marshaler копирует данные из одной области памяти в другую. pinning (прикрепление) данные в управляемой куче временно фиксируются (их не перемещает сборщик мусора). По сравнению с копированием это более быстрый способ передачи данных. Способ маршалинга определяется типом данных. Маршалинг типов-значений Для типов-значений всегда выполняется копирование. Рисунок из раздела MSDN - Copying and Pinning Маршалинг ссылочных типов по значению Для ссылочных типов, которые передаются по значению, может выполняться копирование или прикрепление. Рисунок из раздела MSDN - Copying and Pinning Blittable типы Типы, которые имеют одинаковое представление в управляемой и неуправляемой памяти, и не требуют преобразования при передаче из управляемого кода в неуправляемый, называются blittable. Прикрепление (pinning) при маршалинге выполняется только для blittable типов. blittable типы: System.Byte System.Int32 System.IntPtr System.SByte System.UInt32 System.UIntPtr System.Int16 System.Int64 System.UInt16 одномерные массивы из элементов blittable-типов. System.String и System.Boolean - non-blittable типы. Маршалинг типа bool Данные типа bool имеют разные представления в неуправляемом коде. Если явно не указан атрибут MarshalAs , по умолчанию используется значение UnmanagedType.Bool. Значение перечисления UnmanagedType Формат в неуправляемом коде UnmanagedType.Bool 4-байтовое целое значение, любое ненулевое значение трактуется как TRUE, 0 как FALSE. UnmanagedType.U1 1-байтовое целое значение, значение 1 используется для TRUE, 0 для FALSE. Используется для типа bool в C++. UnmanagedType.VariantBool 2-байтовое целое значение, где значение -1 используется для TRUE, 0 для FALSE. Маршалинг строк Типы, которые передаются по значению, по умолчанию передаются как Inпараметры. Исключение – System.Text.StringBuilder. Когда объекты этого типа передаются по значению, код получает доступ к внутреннему буферу с массивом символов. Неуправляемый код может изменять символы, но не может выходить за границы массива. Строки System.String всегда копируются в промежуточный буфер. Маршалинг ссылочных типов по ссылке Для ссылочных типов, которые передаются по ссылке, всегда выполняется копирование. Рисунок из раздела MSDN - Copying and Pinning