Awasu » Embedding Python: How to use a DLL from your program
Sunday 30th November 2014 8:09 AM []

DLL's typically contain code and data (e.g. the Python interpreter) that can be loaded into a program. There are two ways to do this, to get access to those functions and/or data: explicitly or implicitly.

Explicitly loading a DLL

To explicitly load a DLL, we call LoadLibrary() to load the DLL into our program, then GetProcAddress() to get the address of functions we want to call. We also need to tell the compiler what parameters each function takes, and what it returns.

So, for example, to call Py_InitializeEx(), we might do something like this:

// load the Python DLL
#ifdef _DEBUG
LPCWSTR pDllName = L"python27_d.dll" ;
LPCWSTR pDllName = L"python27.dll" ;
HMODULE hModule = LoadLibrary( pDllName ) ;
assert( hModule != NULL ) ;

// locate the Py_InitializeEx() function
FARPROC pInitializeExFn = GetProcAddress( hModule , "Py_InitializeEx" ) ;
assert( pInitializeExFn != NULL ) ;

// call Py_InitializeEx()
typedef void (*PINITIALIZEEXFN)( int ) ;
((PINITIALIZEEXFN)pInitializeExFn)( 0 ) ;

Note that we no longer need to #include "python.h", nor link in the import library (python27.lib or python27_d.lib), since we are doing everything "manually" i.e. locating the functions in the DLL ourselves, and defining the call signatures.

The only tricky thing here is the typedef - it defines a type called PINITIALIZEEXFN that is a pointer to a function, that takes a single int parameter, and returns nothing (i.e. void)[1]We know Py_InitializeEx() has this function signature since it's declared in python.h.. We then cast the function pointer returned to us by GetProcAddress() to this type, thus allowing the C++ compiler to correctly set up the parameters we want to pass in.

We then repeat this process of calling GetProcAddress() and casting the returned pointer for every function we want to use in the DLL. This is obviously very tedious, so it's much easier to implicitly load the DLL.

Implicitly loading a DLL

When we compiled the Python DLL, in addition to the expected python27.dll file, the compiler also produced a corresponding python27.lib file (the import library). This library contains function stubs for every exported function in the corresponding DLL, so all you have to do is link the import library into your program, and you then have instant access to every public function in the DLL. To call Py_InitializeEx(), all you have to write is:

    Py_InitializeEx( 0 ) ;

Obviously much easier!

This is what happens:

This is much more convenient, but there is one down-side to implicitly loading DLL's: you have no control over where the DLL will be loaded from.

Your program will attempt to find the DLL using the standard search path for DLL's:

  1. The directory where the executable module for the current process is located.
  2. The current directory.
  3. The Windows system directory. The GetSystemDirectory() function retrieves the path of this directory.
  4. The Windows directory. The GetWindowsDirectory() function retrieves the path of this directory.
  5. The directories listed in the PATH environment variable.

This means that your program could potentially load the wrong thing, if another DLL with the same name appears before yours in the search path. Putting your DLL in the same directory as your EXE is usually the safest way to ensure that the correct DLL is loaded, but if the DLL is not there, your program will not start!

Explicitly loading the DLL using LoadLibrary() and GetProcAddress() is more work, but it allows you to:

  1. Specify precisely where to load the DLL from (if you pass in a full path for the DLL).
  2. Gracefully degrade if the DLL is not present (your program can still run, although you obviously won't be able to use any of the features that require the missing DLL).

   [ + ]

1. We know Py_InitializeEx() has this function signature since it's declared in python.h.
Have your say