Подключение внешних библиотек (DLL) из сценариев WME

(Внимание: данная функциональность предусмотрена для опытных пользователей)

Объявление

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.

Внутренние данные DLL-библиотек

Когда 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);
}