(Note: this functionality is aimed at advanced users with programming experience)
WME allows your scripts to call external functions from DLL libraries. This way you can either call Win32 API functions or write your own extension DLL libraries (plug-ins).
To use an external function from a script you must "declare" it first. The declaration syntax is:
external "dll_name" [calling_convention] [return_type] function_name ( [parameters] );
(components enclosed in square brackets are optional)
dll_name - The name of the DLL library, enclosed in quotes. It can also contain a relative or absolute path (not recommended). Please refer to the WIN32 API documentation for the information on how Windows searches for a DLL library.
calling_convention - This specifies which calling convention does the specified function use. It can be either stdcall or cdecl. If you don't specify a calling convention, WME uses stdcall by default. The stdcall convention is used by Win32 API, while your custom DLL library written in C/C++ will probably use cdecl convention. Please refer to you compiler documentation for more information about calling conventions.
return_type - Specifies a data type of the value returned by the function. If the function doesn't return a value (its return type is void) or if you don't want to use its return value, you don't need to specify any return type. See below for the list of supported data types.
function_name - This is the name of the function as exported by the
DLL library. It must match the name exactly otherwise WME will fail to
call the function.
- Note 1: If you are using C++ to write your DLL, be sure to declare your
function as extern "C" otherwise your DLL will
export a "decorated" C++ name.
- Note 2: If you are declaring a Win32 API function, remember that all
functions working with strings are provided in two variations, ANSI and Unicode.
Thus Windows DLLs export two functions: FunctionNameA (for ANSI) and
FunctionNameW (for Unicode). WME currently doesn't support Unicode, therefore
you must use the "A" (ANSI) functions.
parameters - This is the list of parameters delimited by commas. Parameters are defined by their types followed (optionally) by their names. You don't need to specify parameter names, they are not used for anything. See below for the list of supported data types.
WME only allows you to pass primitive types to the DLL functions. While WME script uses untyped variables, they are converted to a requested primitive data type when passed to a DLL function.
Supported data types are:
int | 32 bit integer number, equivalent to int, long or DWORD |
bool | logical value, true or false |
byte | 8 bit value, equivalent to BYTE or unsigned char |
string | array of 8 bit characters, equivalent to char* or LPSTR or LPCSTR (see below) |
float | 32 bit floating-point value, equivalent to float of FLOAT |
double | 64 bit floating-point value, equivalent to double |
membuffer | 32 bit memory pointer value; it must be represented by WME's MemBuffer object |
You can also use short and long data types, they are synonymous to int.
Strings in WME scripts have variable length, dependent on the value they currently contain. But some DLL functions are able to return you a string value into a provided buffer of a fixed size. In this case you can't use ordinary string variables, but you must construct a String object and specify the requested buffer size:
var MyFixedString = new String(256); // this will create a 255 characters long string buffer |
Note: You can query string buffer size using the string object's Capacity attribute.
When WME calls an external DLL function, it only loads the DLL immediately before the call and unloads the DLL immediately after. Therefore the DLL cannot hold any internal data between subsequent calls to its functions, because they would be lost after unloading the library. This can be overcome by explicitly loading your DLL from a script using the LoadLibraryA API function and keeping it loaded all the time, but you must remember that the player can save/load game at anytime, therefore your loaded DLL can get unloaded anyway. You can use game's BeforeSave and AfterLoad events to store/restore private data before saving and after loading game.
Some of the API functions and possibly your own DLL functions may require passing structures to them. WME provides a built-in MemBuffer object type for this purpose. By creating a MemBuffer object in a script you reserve a memory space which can be then passed to a DLL function. MemBuffer objects provide a number of methods for writing/reading values of different types to/from them thus simulating the "struct" keyword known from some other programming languages. See the MemBuffer scripting reference for more details.
By using external DLL functions, you are skating on a thin ice. While normally WME tries to catch developer's mistakes to prevent fatal errors and crashes, using the external functions you can crash the engine quite easily. Keep this in mind and be very careful about the passed parameters, their order and about the calling convention.
The following example demonstrates declaring and calling several Win32 API functions from a WME script.
// declaration 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 plays a sound MessageBeep(0); // GetTempPath returns the Windows' temporary folder var TempPath = new String(256); GetTempPathA(TempPath.Capacity, TempPath); Game.Msg("Windows temporary folder is: " + TempPath); // load our custom DLL and unload it immediately var hDll = LoadLibraryA("MyDll.dll"); FreeLibrary(hDll); } |