[Python] Correct way to embed and bundle python in C++ to avoid "ModuleNotFoundError"...

Discussão em 'Python' iniciado por Stack, Junho 12, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to embed python inside my C++ DLL.

    The idea is that the DLL once distributed, should be sufficient and not rely on other installations and downloads.

    Interestingly, the below "sort of" works, only in my solution directory, since that is where my vcpkg_installed is.

    Platform: Windows

    IDE: Visual Studio

    Use Vcpkg Manifest: Yes

    Target triplet: "x64-windows-static-md"

    Code

    py_wrap.cpp (The DLL)


    void assertPyInit() {
    if (!Py_IsInitialized()) {
    PyConfig config;
    PyConfig_InitPythonConfig(&config);

    // Get the executable path instead of current working directory
    wchar_t exePath[MAX_PATH];
    GetModuleFileNameW(NULL, exePath, MAX_PATH);

    // Remove the executable name to get the directory
    std::wstring exeDir = exePath;
    size_t lastSlash = exeDir.find_last_of(L"\\");
    if (lastSlash != std::wstring::npos) {
    exeDir = exeDir.substr(0, lastSlash);
    }

    // Now build Python path relative to executable location
    std::wstring pythonHome = exeDir + L"\\..\\..\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3";

    // Resolve the full path to eliminate .. references
    wchar_t resolvedPath[MAX_PATH];
    GetFullPathNameW(pythonHome.c_str(), MAX_PATH, resolvedPath, NULL);
    pythonHome = resolvedPath;

    std::wstring pythonLib = pythonHome + L"\\Lib";
    std::wstring pythonSitePackages = pythonLib + L"\\site-packages";
    std::wstring pythonDLLs = pythonHome + L"\\DLLs";

    // Set the Python home directory
    PyConfig_SetString(&config, &config.home, pythonHome.c_str());

    // Set the module search paths
    std::wstring pythonPathEnv = pythonLib + L";" + pythonSitePackages + L";" + pythonDLLs;
    PyConfig_SetString(&config, &config.pythonpath_env, pythonPathEnv.c_str());

    PyStatus status = Py_InitializeFromConfig(&config);
    PyConfig_Clear(&config);

    if (PyStatus_Exception(status)) {
    PyErr_Print();
    return;
    }

    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append(\".\")");
    }
    }


    void MY_DLL pyPrint(const char* message) {
    assertPyInit();

    PyObject* pyStr = PyUnicode_FromString(message);
    if (pyStr) {
    PyObject* builtins = PyEval_GetBuiltins();
    PyObject* printFunc = PyDict_GetItemString(builtins, "print");
    if (printFunc && PyCallable_Check(printFunc)) {
    PyObject* args = PyTuple_Pack(1, pyStr);
    PyObject_CallObject(printFunc, args);
    Py_DECREF(args);
    }
    Py_DECREF(pyStr);
    }
    }

    DLLTester.cpp (client app)


    #include <iostream>
    #include "py_wrap.h"
    int main()
    {
    std::cout << "Hello\n";
    pyPrint("Hello from python:D !");

    }

    File structure and IO


    PS D: \RedactedLabs\Dev > ls AsyncDLLMQL\x64\Release\
    Directory:
    D: \RedactedLabs\Dev\AsyncDLLMQL\x64\Release Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 6/11/2025 10:48 PM 476672 AsyncDLLMQL.dll -a---- 6/11/2025 10:48 PM 4297 AsyncDLLMQL.exp -a---- 6/11/2025 10:26 PM 7752 AsyncDLLMQL.lib -a---- 6/11/2025 10:48 PM 7376896 AsyncDLLMQL.pdb -a---- 6/11/2025 10:48 PM 12288 DLLTester.exe -a---- 6/11/2025 10:48 PM 790528 DLLTester.pdb -a---- 6/6/2025
    4: 39 PM 56320 python3.dll -a---- 6/6/2025
    4: 39 PM 7273984 python312.dll PS


    D: \RedactedLabs\Dev > ls AsyncDLLMQL\x64\Release\
    Directory:
    D: \RedactedLabs\Dev\AsyncDLLMQL\x64\Release Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 6/11/2025 10:48 PM 476672 AsyncDLLMQL.dll -a---- 6/11/2025 10:48 PM 4297 AsyncDLLMQL.exp -a---- 6/11/2025 10:26 PM 7752 AsyncDLLMQL.lib -a---- 6/11/2025 10:48 PM 7376896 AsyncDLLMQL.pdb -a---- 6/11/2025 10:48 PM 12288 DLLTester.exe -a---- 6/11/2025 10:48 PM 790528 DLLTester.pdb -a---- 6/6/2025
    4: 39 PM 56320 python3.dll -a---- 6/6/2025
    4: 39 PM 7273984 python312.dll PS


    D: \RedactedLabs\Dev > ls .\scraps\
    Directory:
    D: \RedactedLabs\Dev\scraps Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 6/11/2025 10:48 PM 476672 AsyncDLLMQL.dll -a---- 6/11/2025 10:48 PM 4297 AsyncDLLMQL.exp -a---- 6/11/2025 10:26 PM 7752 AsyncDLLMQL.lib -a---- 6/11/2025 10:48 PM 7376896 AsyncDLLMQL.pdb -a---- 6/11/2025 10:48 PM 12288 DLLTester.exe -a---- 6/11/2025 10:48 PM 790528 DLLTester.pdb -a---- 6/6/2025
    4: 39 PM 56320 python3.dll -a---- 6/6/2025
    4: 39 PM 7273984 python312.dll PS



    D: \RedactedLabs\Dev > .\AsyncDLLMQL\x64\Release\DLLTester.exe Hello Hello from
    python: D ! PS


    D: \RedactedLabs\Dev > .\scraps\DLLTester.exe Hello Python path
    configuration: PYTHONHOME= 'D:\RedactedLabs\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\tools\python3' PYTHONPATH = 'D:\RedactedLabs\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\tools\python3\Lib;D:\RedactedLabs\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\tools\python3\Lib\site-packages;D:\RedactedLabs\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\tools\python3\DLLs' program name = 'python' isolated = 0 environment = 1 user site = 1 safe_path = 0 import site = 1 is in build tree = 0 stdlib dir = 'D:\RedactedLabs\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\tools\python3\Lib' sys._base_executable = 'D:\\RedactedLabs\\Dev\\scraps\\DLLTester.exe' sys.base_prefix = 'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3' sys.base_exec_prefix = 'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3' sys.platlibdir = 'DLLs' sys.executable = 'D:\\RedactedLabs\\Dev\\scraps\\DLLTester.exe' sys.prefix = 'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3' sys.exec_prefix = 'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3' sys.path = [
    'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3\\Lib',
    'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3\\Lib\\site-packages',
    'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3\\DLLs',
    'D:\\RedactedLabs\\Dev\\scraps\\python312.zip',
    'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3\\DLLs',
    'D:\\RedactedLabs\\vcpkg_installed\\x64-windows-static-md\\x64-windows-static-md\\tools\\python3\\Lib',
    'D:\\RedactedLabs\\Dev\\scraps',
    ]
    ModuleNotFoundError: No module named 'encodings'

    Finally, my linker settings and other useful info # :


    Additional include directories: $(SolutionDir)vcpkg_installed\x64-windows-static-md\x64-windows-static-md\include\python3.12

    Additional Library Directories: $(VcpkgInstalledDir)\x64-windows-static-md\lib

    TLDR: How can I make my DLL not be required to be near vcpkg_installed?

    Continue reading...

Compartilhe esta Página