(Внимание: данная функциональность предусмотрена для опытных пользователей)
WME позволяет Вашим сценариям вызывать внешние функции из подключаемых библиотек (DLL). Вы можете либо вызывать функции Win32 API, либо писать собственные DLL-библиотеки (плагины).
Чтобы использовать внешнюю функцию в сценарии, Вы должны сначала «объявить» её:
external "имя_библиотеки" [метод_вызова (calling_convention)] [тип_возвращаемых_данных] имя_функции ( [параметры] );
(компоненты, заключенные в квадратные скобки - необязательны)
имя_библиотеки - имя DLL-библиотеки, заключённое в кавычки. Может содержать как относительный путь до неё, так и абсолютный (не рекомендуется). Обратитесь к документации по WIN32 API, чтобы прочитать о том, как Windows ищет DLL-библиотеки.
метод_вызова (calling_convention) - Это может быть либо stdcall, либо cdecl. По умолчанию WME использует stdcall. Этот метод используется Win32 API, в то время как Ваши собственные DLL-библиотеки, написанные на C/C++, скорее всего будут использовать метод cdecl. За более подробной информацией обратитесь к документации по Вашему компилятору.
тип_возвращаемых_данных - Если фунция не возвращает никакого значения (её тип void) или Вам не требуется использовать его, то в этом случае Вам не нужно указывать данный параметр. Ниже приведён список поддерживаемых типов данных.
имя_функции - имя функции, экспортируемой DLL-библиотекой.
Оно должно точно совпадать, иначе WME выдаст ошибку.
- Замечание 1: если Вы написали Вашу DLL на C++, убедитесь, что Вы объявили функцию
как extern "C", в противном случае DLL экспортирует
её с «декоративным» именем C++.
- Замечание 2: Если Вы объявляете функцию Win32 API, помните, все функции, работающие со строками,
понимают только ANSI-кодировку или Юникод.
Таким образом, DLL-библиотеки Windows экспортируют два типа функций: ИмяФункцииA (для ANSI) и
ИмяФункцииW (для Юникода). WME в настоящий момент не поддерживает Юникод, т. е. Вы должны
использовать суффикс "A" (ANSI).
параметры - Список аргументов функции, разделённых запятыми. Параметры определяются своими типами и (опционально) именами. Вам не обязательно указывать имена параметров, т.к. они нигде не используются. Ниже приведён список поддерживаемых типов данных.
WME позволяет Вам передавать в DLL-функции только примитивные типы данных. Переменные WME при передаче в DLL-функции конвертируются в запрашиваемый тип данных.
Поддерживаемые типы данных:
int | 32-битное целочисленное значение, эквивалентное int, long или DWORD |
bool | логическое значение, true (истина) или false (ложь) |
byte | 8-битное значение, эквивалентное BYTE или беззнаковому char |
string | массив восьмибитных символов, эквивалентен char*, либо LPSTR или LPCSTR (см. ниже) |
float | 32-битное значение с плавающей точкой, эквивалентно float |
double | 64-битное значение с плавающей точкой, эквивалентно double |
membuffer | 32-битный указатель; должен быть представлен объектом WME MemBuffer |
Вы также можете использовать типы данных short и long, которые являются синонимами int.
Строки в WME имеют переменную длину. Но некоторые DLL-функции возвращают строковые значения в буфер памяти фиксированного размера. В этом случае Вы не можете использовать обычные строковые переменные, Вы должны создать объект String и указать требуемый размер буфера памяти:
var MyFixedString = new String(256); // будет создан буфер памяти на 255 символов |
Внимание: Вы можете узнать размер буфера памяти с помощью атрибута Capacity объекта String.
Когда WME вызывает внешнюю функцию, то загружает DLL-бибилиотеку непосредственно перед вызовом и выгружает сразу после него. По этой причине DLL не может хранить никаких внутренних данных между вызовами своих функций, т. к. все они будут потеряны после выгрузки библиотеки. Эта проблема может быть решена загрузкой DLL непосредственно из WME-сценария с помощью API-функции LoadLibraryA. В этом случае DLL будет храниться в памяти всё время. Но следует помнить, что пользователь может сохранить/загрузить игру в любое время, вследствие чего Ваша DLL может быть выгружена из памяти. Поэтому для сохранения/восстановления внутренних данных DLL-библиотеки используйте события BeforeSave (перед сохранением игры) и AfterLoad (после загрузки).
Некоторые из API-функций, а также, возможно, Ваших собственных, могут потребовать передачи им в качестве аргумента структурированных данных. Для решения этой задачи WME предоставляет объект MemBuffer. Создавая объект MemBuffer Вы резервируете некоторый объём памяти, который может быть передан DLL-функции. Этот объект предоставляет набор методов для записи/чтения данных различных типов из этих функций, благодаря чему возможна симуляция ключевого слова struct. Для более подробной информации см. раздел объект MemBuffer.
Используя DLL-функции, Вы катаетесь по тонкому льду. Обычно WME пытается отлавливать ошибки разработчика для предупреждения обрушения всей программы, но при использовании внешних функций этого не происходит. Помните это и будьте очень осторожны при передаче аргументов, их порядке записи и указании метода вызова (calling_convention).
// объявление external "user32.dll" MessageBeep(int uType); external "kernel32.dll" long GetTempPathA(int nBufferLength, string lpBuffer); external "kernel32.dll" int LoadLibraryA(string); external "kernel32.dll" FreeLibrary(int); function MyDllTest() { // MessageBeep воспроизводит звук MessageBeep(0); // GetTempPath возвращает имя временной папки Windows var TempPath = new String(256); GetTempPathA(TempPath.Capacity, TempPath); Game.Msg("Временная папка Windows: " + TempPath); // загрузка и выгрузка пользовательской DLL var hDll = LoadLibraryA("MyDll.dll"); FreeLibrary(hDll); } |