DLL interface to exe and python - We-the-People-civ4col-mod/Mod GitHub Wiki
The game code consists of 3 parts
- DLL file (C++)
- python code (.py)
- game exe (no source code)
In general it's preferred to have all the code in C++ as it's the fastest to execute and the easiest to find and fix bugs in. However once in a while a specific part is needed and for that reason each one can call the two others.
Python<->EXE
This one is fairly simple. The exe can call some python functions and python can call the exe with a fixed API. We do not have the complete API, but it's at least 99% overlap with the BTS python API. TODO: make a list of Colonization specific features in the API, like adding how to handle drag-n-draw.
DLL<->EXE
Common for both ways is that the function names, arguments and return types are set at compile time. Since they are already set in the EXE, we can't change them (see exception with redirection below if really needed).
DLL calling EXE
We have some header files (mainly CvDLLUtilityIFaceBase), which lists functions we can call in the exe. Leave the header file untouched and call them using the gFLL-> prefix, like
gDLL->getText(some arguments);
EXE calling DLL
The exe expects to be able to call a number of functions in the DLL. The compiler needs to be aware that those functions needs to be accessible to the outside world and say eliminate it entirely and just inline it whenever used. The way the compiler is aware of this is by using the keyword DllExport. Since the EXE won't change, we have a fixed list of which functions should have the DllExport keyword and the compiler will throw an error if this keyword is used for the wrong function or not used on functions which require them. It will also cause an error if the arguments are wrong. This way it shouldn't be possible to mess up DllExport, which in turn risk crashing the game when the EXE expectations aren't met.
Changing arguments in DllExport functions
If we have a function, which is used by both the exe and dll, then we might end up in a situation where changing the arguments makes out C++ code way better (read: more readable, less risk of bugs etc).
Let's say we have
class myClass
{
public:
DllExport int getCount();
};
We want to add an argument to tell which count we want and to avoid changing all the places it's called, we want the default value to be the default behavior. This will work in our own code as it's compiled with this header. However default arguments are still arguments and they are just set to a fixed value at compile time. Since the exe didn't know about the default value at compile time, it will not set it and hence the argument will end up containing random data, which will likely crash the game. What we do is to give the exe a new function to call.
class myClass
{
public:
int getCount(MyEnum eType = COUNT_DEFAULT);
DllExport int __EXE__getCount() { return getCount(); }
};
Now if we tell the compiler that all calls to myClass::getCount are actually calls to myClass::__EXE__getCount, then it will work because that function has the arguments and return type the exe expects. __EXE__getCount does contain a getCount call without arguments, but since it's compiled in the DLL, it will get the default argument. This way the EXE interface will not have stolen all the human readable function names and we can mod them as needed for clean code.
TODO: write how to redirect a function name
DLL<->Python
TODO
Note: the python interface doesn't care about DllExport. It is absolutely not needed to expose functions to python.