Search code examples
pythonc++visual-studiopybind11

pybind11 conflict with GetModuleFileName


I am using pybind11 to let python call an existing C++ module (library). The connection is through, however, in the C++ library, ::GetModuleFileName (Visual Studio) is called to to determine the physical path of the loaded module as it is run in C++. But when I call the library from python (Jupyter Notebook) through pybind11, the physical path of python.exe is returned. How can I configure or change to make sure the physical path of the C++ library is obtained?

The C++ code is like this: Lib.h

#pragma once

void run();

Lib.cpp

#include <fstream>
#include <stdexcept>

#include <windows.h>
#include "libloaderapi.h"

#include "Lib.h"

void run()
{
    char buf[1024];
    ::GetModuleFileName(0, buf, sizeof(buf));

    std::ofstream of;
    of.open("logging.txt");

    if (!of.is_open()) {
        throw std::runtime_error("Cannot open logging.txt");
    }

    of << "The loaded module is " << buf << std::endl;
}

The pybind11 interface code: Direct.cpp

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>

#include "Lib.h"

namespace py = pybind11;

// wrap c++ function
void wrapper() {
    run();
}

PYBIND11_MODULE(run_app, m) {
    // optional module docstring
    m.doc() = "pybind11 plugin";

    m.def("run", &wrapper, "Run C++ Application");
}

The pybind11 setup file: setup.py

#from distutils.core import setup, Extension
#from distutils import sysconfig

from setuptools import setup, Extension
import pybind11

# The following is for GCC compiler only.
#cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
cpp_args = []

sfc_module = Extension(
    'run_app',
    sources=['Direct.cpp', 
             'Lib.cpp'],
    include_dirs=[pybind11.get_include(), '.'],
    language='c++',
    extra_compile_args=cpp_args,
    )

setup(
    name='run_app',
    version='1.0',
    description='Python package with RunApp C++ extension (PyBind11)',
    ext_modules=[sfc_module],
)

To build:

python setup.py build

The python code calling this library: py_run_app.py

import os
import sys
sys.path.append(os.path.realpath('build\lib.win-amd64-3.7'))

from run_app import run
run()

After the run:

python py_run_app.py

In the logging.txt The loaded module is C:....\python.exe

What I want to see is the physical location of the module.


Solution

  • "Module" in Windows parlance is a DLL or an executable file loaded to be a part of a process. Each module has a module handle; by convention, the special handle NULL signifies the executable file used to create the process.

    GetModuleFileName requires module handle as the first argument. You pass 0, you get back the name of the module with the special handle NULL, i.e. the executable. This is fully expected.

    In order to get the file name of a DLL, you need to find out what its handle is. You can find the handle of the current module:

    HMODULE handle;
    static char local;
    bool ok = GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                                 (LPCSTR)&local,
                                 &handle);
    

    local can be any function or static/extern variable in the current module. ref.