Принципы объектно-ориентированного программирования

         

Объявление экспортированной функции DLL



Объявление экспортированной функции DLL

Во-первых, мы рассмотрим объявление на С# простой функции из DLL. Мы используем пример, который для .NET PInvoke быстро становится каноническим, — Win32 "MessageBox". Затем мы перейдем к более сложным вопросам, связанным с преобразованием параметров.

Как вы узнали из главы 8, атрибуты служат для предоставления информации периода разработки о типе С#. В период же выполнения эту информацию позволяет получить отражение. В С# атрибут Dlllmport позволяет указать компилятору функцию DLL, которую будет вызывать приложение:

[ОШтроЛ(имя_с/1[)]



модификатор ^доступа static extern возвр_знанение функциями (парам!, парам2,...);

Как видите, чтобы импортировать функцию из DLL, нужно лишь прикрепить атрибут Dlllmport (передавая его конструктору имя DLL) к функции DLL, которую вы хотите вызвать. Заметьте: для определяемой вами функции нужно применять модификаторы static и extern. Вот пример MessageBox — он показывает, как легко использовать PInvoke: using System; using System.Runtime.InteropServices;

class PInvokelApp {

[Dlllmport("user32.dll")] static extern int MessageBoxA(int hWnd,

string msg, string caption, int type);

public static void MainQ

{

MessageBoxA(0,

"Hello, World!",

"This is called from a C# app!", 0); } }

В результате запуска этого приложения, как и ожидалось, будет показано окно с сообщением "Hello, World!".

Оператор using указывает пространство имен System.Runtime.InteropServices, которое определяет атрибут Dlllmport. Затем я определил метод MessageBoxA, вызываемый в Main. Но если вы хотите назвать свой внутренний метод на С# не так, как называется функция DLL? Это позволяет сделать один из именованных параметров атрибута Dlllmport.

В приведенной ниже программе происходит следующее. Я сообщаю компилятору, что хочу назвать свой метод MessageBoxA. Поскольку в атрибуте Dlllmport я не указал имя функции DLL, компилятор предполагает, что оба имени одинаковы.

[Dlllmport("user32.dll")]

static extern int MessageBoxA(int hWnd,

string msg, string caption, int type);

Чтобы понять, как изменить этот стандартный алгоритм, рассмотрим пример. На этот раз используется внутреннее имя MsgBox, но все равно вызывается функция DLL MessageBoxA.

using System;

using System.Runtime.InteropServices;

class PInvoke2App {

[Dlllmport("user32.dll", EntryPoint="MessageBoxA")] static extern int MsgBox(int hWnd,

string msg,

string caption,

int type);

public static void Main() {

MsgBox(0,

"Hello, World!",

"This is called from a C# app!", 0); > }

Как видите, чтобы получить возможность называть внутренний эквивалент внешней функции DLL, как мне вздумается, я должен лишь задать именованный параметр Entry Point атрибута Dlllmport.

Последнее, что мы рассмотрим, прежде чем перейти к преобразованию параметров, — параметр CharSet. Он позволяет задавать набор символов, используемый файлом DLL. Обычно при написании С++-при-ложений вы не задаете явно использование MessageBoxA или Message-BoxW — благодаря pragma, компилятор уже знает, какой набор символов вы применяете — ANSI или Unicode. Поэтому при вызове MessageBox на C++ компилятор определяет, какой набор символов использовать. Сходным образом на С# вы можете задать целевой набор символов для атрибута Dlllmport, который будет указывать, какую версию MessageBox вызвать. В следующем примере все равно будет вызвана функция MessageBoxA, так как Dlllmport через его именованный параметр CharSet передается соответствующее значение.

using System;

using System.Runtime.InteropServices;

class PInvokeSApp {

// При задании CharSet.Ansi будет вызвана MessageBoxA. // При задании CharSet.Unicode будет вызвана HessageBoxW. [Dlllmport("user32.dll", CharSet=CharSet.Ansi)] static extern int MessageBox(int hWnd,

string msg, string caption, int type);

public static void MainQ {

MessageBox(0,

"Hello, World!",

"This is called from a C# app!", 0); } }

Преимущество параметра CharSet в том, что я могу установить в приложении переменную, которая будет контролировать вызов той или иной версии функций (ANSI или Unicode). Мне не придется менять весь код при переходе от одной версии к другой.

 

Содержание раздела