UDN
Search public documentation:

DLLBind
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > UnrealScript > Calling DLLs from UnrealScript (DLLBind)

Calling DLLs from UnrealScript (DLLBind)


Overview


Starting from the December 2009 version, UnrealScript has the ability to call functions implemented in a Windows DLL. This functionality is intended for use by UDK developers, and for mod makers developing extensions to 3rd party Unreal Engine 3 products. Full-license Unreal Engine 3 developers are able to create Native Classes to call native code directly and should not use this feature. It is disabled by default in licensee source code. To enable it, enable the libffi section of UE3BuildWin32.cs.

UnrealScript side


A single UnrealScript class can bind to only a single DLL. The DLL to bind to is specified with the DLLBind directive, and the DLL name to bind to is specified in parentheses. Do not include a path or .DLL extension. DLLs can only be loaded from the Binaries\Win32\UserCode folder.

Imported functions are declared like UnrealScript functions using the dllimport function directive.

DLL import functions must be declared final, and it is not possible to create a subclass of a DLLBind class.

For example:

class TestDLLPlayerController extends PlayerController
   DLLBind(TestDLL);

dllimport final function CallDLL1(out string s);
dllimport final function vector CallDLL2(float x, float y, float z);
dllimport final function bool CallDLL3(string s, int i[2], out float f, out vector v);

When the TestDLLPlayerController class is loaded, it will bind to Binaries\Win32\UserCode\TestDLL.dll. If the DLL cannot be bound, a warning will be printed in the log. It will attempt to find entry points to three exports in the DLL (CallDLL1, CallDLL2 and CallDLL3). Once bound, these functions can be called as normal from other UnrealScript functions. If the bind fails, making a call to one of these functions will have no effect.

DLL side


These functions can be implemented in a C++ DLL like this:

extern "C"
{
   struct FVector
   {
      float x,y,z;
   };

   __declspec(dllexport) void CallDLL1(wchar_t* s)
   {
      MessageBox(0, s, L"Inside the DLL: CallDLL1", MB_OK);
      // reverse the out parameter string
      int len = wcslen(s);
      for(int i=0; i<len>>1;i++)
      {
         wchar_t temp = s[i];
         s[i] = s[len-i-1];
         s[len-i-1] = temp;
      }
   }

   __declspec(dllexport) FVector* CallDLL2(float x, float y, float z)
   {
      static FVector result;   // declared static so that the struct's memory is still valid after the function returns.
      result.x = x;
      result.y = y;
      result.z = z;
      return &result;
   }

   __declspec(dllexport) bool CallDLL3(wchar_t* s, int i[2], float* f, FVector* V)
   {
      wchar_t temp[1024];
      swprintf_s(temp, 1024, L"string: %s, i: {%d,%d}, float: %f, V was (%f,%f,%f)", s, i[0], i[1], *f, V->x, V->y, V->z);
      V->x = (float)i[0];
      V->y = (float)i[1];
      V->z = (*f);
      return (MessageBox(0, temp, L"Inside the DLL: CallDLL3", MB_OKCANCEL) == IDOK);
   }
}

Unloading


When the DLLBind class's Class object is destroyed, the DLL will be unloaded using the FreeLibrary API. This should occur when the last reference to the Class object is deleted and garbage collection occurs. You should be able to perform cleanup in your DLL's DllMain() function by responding to the DLL_PROCESS_DETACH fdwReason.

Supported Parameter Types


The following parameter types are supported:

UnrealScript type C type  
int value int value (32-bit) passed by value
int value[..] int* value (32-bit) passed by reference
out int value int* value (32-bit) passed by reference
float value float value passed by value
float value[..] float* value passed by reference
out float value float* value passed by reference
byte value unsigned char value passed by value
byte value[..] unsigned char* value passed by reference
out byte value unsigned char* value passed by reference
string value wchar_t* value passed by reference
out string value wchar_t* value passed by reference SEE WARNINGS BELOW
struct foo struct foo* value passed by reference. The DLL should declare a struct to match the UnrealScript struct. See below for information about strings inside the struct.
out struct foo struct foo* value passed by reference

The following return types are supported:

UnrealScript type C type  
int int (32-bit) returns value directly
float float value returns value directly
byte unsigned char returns value directly
bool unsigned int (32-bit) returns value directly, non-zero is true
string wchar_t* pointer value is strcpy'd into a UnrealScript string. SEE WARNINGS BELOW
struct foo struct foo* struct data is memcpy'd into the UnrealScript struct. SEE WARNINGS BELOW

Limitations


  • Only Unicode (UTF-16) strings are supported. Compile your DLL with Unicode enabled.
  • As only the 32-bit version of UDK is currently available, only 32-bit DLLs are supported. At such time as a 64-bit version of UDK is available, 64-bit DLLs will be supported.
  • DLLs can only be loaded from Binaries\Win32\UserCode.
  • Under Win32, only stdcall is supported. (See this link for information about calling conventions).

Debugging


  • A Debug build of your DLL should bind correctly, and you should be able to attach to UDK.exe with your debugger and set breakpoints in your DLL.
  • You should compile your DLL in Release mode if you intend to distribute it to others, otherwise you will likely have problems as the debug version of the C runtime will be unavailable.

About UnrealScript strings, and using strings inside structs


Unreal represents dynamic strings internally as a struct called an FString. It containing 3 members:

  • Data, a 32-bit pointer to the memory allocated for the string
  • ArrayNum, a 32-bit int storing the current number of 16-bit WORDs (UTF-16 characters) in the string (including the NULL terminator). This is normally equal to wcslen(Data)+1 except in empty strings, where it is 0.
  • ArrayMax, a 32-bit int storing the number of 16-bit WORDs of memory currently allocated for the string.

Strings can grow or shrink in length up to ArrayMax-1 WORDs, at which point Unreal will reallocate the Data memory. Empty strings are a special case; the Data pointer is NULL, and ArrayNum = ArrayMax = 0.

This creates a problem for DLLBind. The current DLLBind code looks for any string parameters or return values and passes the FString's Data pointer to the DLL. Upon returning, Unreal checks the current length of any string out parameters and adjusts the ArrayNum if necessary. For wchar_t* return values, it copies the data into a new Unreal string after the DLL returns.

However none of this is performed for strings inside structs passed by reference to the DLL. The solution to this problem is for the DLL to declare its own local FString class to mirror Unreal's memory layout.

C++:

struct FString
{
   wchar_t* Data;
   int ArrayNum;
   int ArrayMax;
};

struct MyPlayerStruct
{
   FString PlayerName;
   FString PlayerPassword;
   float Health;
   float Score;         
};

UnrealScript:

struct MyPlayerStruct
{
   var string PlayerName;
   var string PlayerPassword;
   var float Health;
   var float Score;
};

Notes

  • If the struct is an out parameter, it is possible to change the value of the strings inside the struct up to the length of ArrayMax, provided you adjust ArrayNum to match afterwards. See the StringDLL example attached.
  • Do not attempt to allocate or reallocate the Data pointer, as your DLL will not use Unreal's memory allocator.
  • Do not adjust the ArrayMax value.
  • Do not return structs with strings because Unreal will attempt to free the Data memory when the struct variable is destroyed.

Warnings


  • When modifying a string out parameter, the new string must be the same length or shorter than the previous value of the string passed in. This can be achieve by initializing the string in UnrealScript to something longer than the maximum value the DLL will store. Failure to do this will likely overwrite memory and cause a crash.
  • When returning structs or strings, the DLL returns a pointer and Unreal will copy the value back to UnrealScript. However the DLL is responsible for allocating the memory to store the data pointed to. For example, it could return a pointer to a static variable, as is done in CallDLL2. Returning a pointer to a variable declared on the stack will result in corrupt data or a crash, because the stack is destroyed when the DLL function returns.
  • In UnrealScript struct members are aligned on 4-byte boundaries, so you should use the /Zp4 compiler option when building your DLL.
  • It is possible to declare structs containing Unreal data types that the DLL cannot understand. It is not recommended you do this!
  • Do not attempt to return a struct containing UnrealScript strings. Out parameters are OK if you follow the directions above.
  • Prior to February 2010 UDK, the current working directory was set to the Binaries\Win32 folder and NOT UserCode. Since the February 2010 version, the working directory is changed to the UserCode folder before calling any DLLBind functions.

Example