- •Работа с неуправляемым кодом
- •Управляемый и неуправляемый код
- •Службы Platform Invoke
- •Службы com Interoperability
- •Обзор механизма преобразования
- •Работа службы Platform Invoke. Запуск функций WinApi
- •Управляемая библиотека
- •Управляемая библиотека в управляемом коде
- •Управляемая библиотека в неуправляемом коде
- •Вызов неуправляемых функций из управляемого модуля
- •Идентификация вызываемой функции
- •Создание класса для размещения библиотечной функции
- •Прототипы в управляемом коде
- •Небезопасный код
- •Григорьев с. .Net и работа с неуправляемым кодом. Часть 1 / с. Григорьев [Электронный ресурс] : http://habrahabr.Ru/blogs/net/84076/
Работа службы Platform Invoke. Запуск функций WinApi
Наиболее часто служба Platform Invoke применяется для запуска функций WinAPI, находящихся в файлах dll. Когда служба вызывает функцию, совершаются следующие операции:
обнаружение необходимой библиотеки;
загрузка найденной библиотеки в оперативную память;
обнаружение адреса библиотеки в памяти и передача аргументов функции, с их преобразованием при необходимости;
после этого Platform Invoke передает управление неуправляемой функции и ждет завершения.
Для подключения функции необходимо представить адрес библиотеки и данные о функции: название, входящие и исходящие аргументы.
Windows API — это набор функций, входящий в состав семейства операционной системы Microsoft Windows. Преимуществом использования этих функций является то, что они уже полностью готовы и не приходится тратить время на реализацию подобной функциональности. Однако с ними тяжело работать — в частности, довольно сложно отлавливать исключения, возникающие в работе приложения.
Первым шагом в запуске неуправляемой функции является объявление функции. Функция должна быть статической (static) и внешней (extern). Далее следует импорт библиотеки, содержащей эту функцию. Импортировать библиотеку нужно, используя атрибут DllImport, который находится в пространстве имен System.Runtime.InteropServices. Атрибут DllImport нуждается в названии библиотеки и может принимать один из параметров, указанных в таблице 2:
Таблица 2. Некоторые параметры атрибута DllImport
Аргумент |
Описание |
EntryPoint |
Указывает название функции. Если название метода совпадает с названием неуправляемой функции, то этот аргумент ставить необязательно |
CharSet |
Указывает кодировку строковых значений. Значением по умолчанию является ANSI |
ExactSpelling |
Предотвращает изменение точки входа (entry point) при изменении кодировки. Если значение аргумента CharSet установлено в Auto, то значение по умолчанию — true, в противном случае — false |
CallingConvention |
Указывает тип конвертирования аргументов, передаваемых в функцию. Значением по умолчанию является WinAPI |
PreserveSig |
Указывает, что сигнатура управляемого метода не должна трансформироваться в сигнатуру неуправляемого метода, который возвращает значение типа HRESULT и может иметь дополнительные аргументы в качестве возвращаемых значений Значением по умолчанию является true (сигнатура не должна трансформироваться). |
SetLastError |
Указывает возможность использования метода GetLastError для обработки ошибок при выполнении неуправляемого метода. Значением по умолчанию является false |
Аргументы, которые принадлежат к простым типам данных, такие как byte или int, Platform Invoke автоматически преобразовывает в соответствующие типы в неуправляемой платформе. В таблице 3 приводится соответствие типов данных в функциях WinAPI и библиотеки .NET Framework.
Таблица 3. Типы данных в неуправляемом и управляемом кодах
Тип данных в неуправляемом коде |
Тип данных в .NET Framework (C#) |
HANDLE |
Int |
BYTE |
Byte |
SHORT |
Short |
WORD |
Ushort |
INT |
Int |
UINT |
Uint |
LONG |
Int |
ULONG |
Uint |
BOOLEAN |
Int |
CHAR |
Char |
LPSTR (и большинство строковых типов данных) |
String для входящих аргументов, StringBuilder для двусторонних аргументов |
FLOAT |
Float |
DOUBLE |
double |
При указании библиотеки не нужно указывать путь до неё. CLR ищет библиотеку в каталоге приложения, затем в подкаталогах, в папке Windows и папке Window\System32. При указании адреса библиотеки могут возникнуть исключения — CLR проверяет только его. Например, если был указан адрес "C:\Windos\system32\someDll.dl", а у пользователя операционная система располагается на диске D — приложение работать не будет. При использовании функций WinAPI сами файлы библиотек не нужно включать в проект.
C++ .NET. Совмещение управляемого и неуправляемого кодов
Язык программирования C++ .NET является единственным, который позволяет создавать как управляемый, так и неуправляемый код. Это дает возможность не только использовать неуправляемый библиотечный код, но и смешивать управляемый и неуправляемый коды в одном приложении.
В приводимых ниже примерах кроме языка программирования C# будет использоваться язык C++.
Сначала создается C++ Win32 Console Project, то есть неуправляемое консольное приложение на C++. Visual Studio с версии .NET 2005 позволяет в широких пределах смешивать управляемый и неуправляемый коды.
// Транслятору задали опцию /clr.
// Эта опция преобразует все объявляемые в программе типы.
// От имени объекта элементарного типа (например, int) можно
// вызвать методы базового класса object.
// Но только вызываются они лишь из фрагментов управляемого
кода.
// Иначе – сообщение транслятора:
// ... managed type or function cannot be used in an unmanaged function
#include "stdafx.h"
#include <iostream>
using namespace std;
#using <mscorlib.dll> // Ядро CLR
using namespace System;
class uClass; // Предварительное неполное объявление класса.
class mClass; // Предварительное неполное объявление класса.
#pragma managed
class mClass
{
public:
uClass *ucP;
mClass *mcP;
int x;
mClass()
{
// Только в неуправляемом коде!
// cout << "Ha-Ha-Ha";
Console::WriteLine("mClass");
// Легко посмотрели значение непроинициализированной переменной.
Console::WriteLine(x.ToString());
}
~mClass()
{
Console::WriteLine("~mClass");
}
void mcF0()
{
Console::WriteLine("mcF0()");
}
mClass* mcGenerator();
};
#pragma unmanaged
class uClass
{
public:
uClass *ucP;
mClass *mcP;
int x;
uClass()
{
// Только в управляемом коде!
//Console::WriteLine("Ha-Ha-Ha");
printf("uClass\n");
}
~uClass()
{
printf("~uClass\n");
}
void ucF0()
{
cout << "ucF0()\n";
}
uClass* ucGenerator();
};
// Судя по всему, функция Управляемого
// класса может быть НеУправляемой!
mClass* mClass::mcGenerator()
{
//x.ToString();
//Console::WriteLine("Ha-Ha-Ha");
cout << "Ha-Ha-Ha from unmanaged function of managed class!" << endl;
ucP = new uClass();
ucP->ucF0();
delete ucP;
return new mClass();
}
#pragma managed
// А сделать Управляемой функцию НеУправляемого класса невозможно.
// Прагма managed для функции - члена неуправляемого класса игнорируется.
uClass* uClass::ucGenerator()
{
cout << "Ha-Ha-Ha from function of unmanaged class!" << endl;
//Console::WriteLine("Ha-Ha-Ha");
//x.ToString();
mcP = new mClass();
mcP->mcF0();
delete mcP;
return new uClass();
}
#pragma unmanaged
int _tmain(int argc, _TCHAR* argv[])
{
void *xPoint;
int x = 125;
// Только не смешивать!
//Console::WriteLine("Ha-Ha-Ha");
mClass *mc = new mClass();
mc->mcF0();
xPoint = mc->mcGenerator();
delete (mClass*)xPoint;
delete mc;
uClass *uc = new uClass();
uc->ucF0();
xPoint = uc->ucGenerator();
delete (uClass*)xPoint;
delete uc;
return 0;
}