diff --git a/src/plugin_python_en.ts b/src/plugin_python_en.ts index 1aa9ef7..1d715ab 100644 --- a/src/plugin_python_en.ts +++ b/src/plugin_python_en.ts @@ -4,70 +4,70 @@ ProxyPython - + Python Initialization failed - + On a previous start the Python Plugin failed to initialize. Do you want to try initializing python again (at the risk of another crash)? Suggestion: Select "no", and click the warning sign for further help.Afterwards you have to re-enable the python plugin. - + Python Proxy - + Proxy Plugin to allow plugins written in python to be loaded - + ModOrganizer path contains a semicolon - + Python DLL not found - + Invalid Python DLL - + Initializing Python failed - - + + invalid problem key %1 - + The path to Mod Organizer (%1) contains a semicolon. <br>While this is legal on NTFS drives, many softwares do not handle it correctly.<br>Unfortunately MO depends on libraries that seem to fall into that group.<br>As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon. - + The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem. - + The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem. - + The initialization of the Python plugin DLL failed, unfortunately without any details. diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 5cff16d..3921653 100644 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -15,6 +15,12 @@ target_link_libraries(proxy PRIVATE runner) set_target_properties(proxy PROPERTIES OUTPUT_NAME ${PLUGIN_NAME}) mo2_install_target(proxy FOLDER) +set(PLUGIN_PYTHON_DIR ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}) + +# install runner +target_link_options(proxy PRIVATE "/DELAYLOAD:runner.dll") +mo2_install_target(runner INSTALLDIR ${PLUGIN_PYTHON_DIR}/dlls) + # translations (custom location) mo2_add_translations(proxy TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../${PLUGIN_NAME}_en.ts @@ -25,14 +31,14 @@ mo2_add_translations(proxy ${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt) # install DLLs files needed -set(DLL_DIRS ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}/dlls) +set(DLL_DIRS ${PLUGIN_PYTHON_DIR}/dlls) file(GLOB dlls_to_install ${PYTHON_BUILD_PATH}/libffi*.dll ${PYTHON_BUILD_PATH}/python${Python_VERSION_SHORT}.dll) install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS}) # install Python files -set(PYLIB_DIR ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}/libs) +set(PYLIB_DIR ${PLUGIN_PYTHON_DIR}/libs) file(GLOB libs_to_install ${PYTHON_BUILD_PATH}/pythoncore/*.pyd) install(FILES ${libs_to_install} DESTINATION ${PYLIB_DIR}) install(FILES ${PYTHON_BUILD_PATH}/pythoncore/python${Python_VERSION_SHORT}.zip diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index 3fd5eac..6ac78e1 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -54,7 +54,8 @@ fs::path getPluginFolder() } ProxyPython::ProxyPython() - : m_MOInfo{nullptr}, m_Runner{nullptr}, m_LoadFailure(FailureType::NONE) + : m_MOInfo{nullptr}, m_RunnerLib{nullptr}, m_Runner{nullptr}, + m_LoadFailure(FailureType::NONE) { } @@ -81,16 +82,6 @@ bool ProxyPython::init(IOrganizer* moInfo) return false; } - // load the pythonrunner library - const auto dllPaths = pluginFolder / "dlls"; - if (SetDllDirectoryW(dllPaths.c_str()) == 0) { - DWORD error = ::GetLastError(); - m_LoadFailure = FailureType::DLL_NOT_FOUND; - log::error("failed to add python DLL directory ({}): {}", error, - qUtf8Printable(windowsErrorString(::GetLastError()))); - return false; - } - if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) { m_LoadFailure = FailureType::INITIALIZATION; if (QMessageBox::question( @@ -115,6 +106,25 @@ bool ProxyPython::init(IOrganizer* moInfo) m_MOInfo->setPersistent(name(), "tryInit", true); } + // load the pythonrunner library, this is done in multiple steps: + // + // 1. we set the dlls/ subfolder (from the plugin) as the DLL directory so Windows + // will look for DLLs in it, this is required to find the Python and libffi DLL, but + // also the runner DLL + // + const auto dllPaths = pluginFolder / "dlls"; + if (SetDllDirectoryW(dllPaths.c_str()) == 0) { + DWORD error = ::GetLastError(); + m_LoadFailure = FailureType::DLL_NOT_FOUND; + log::error("failed to add python DLL directory ({}): {}", error, + qUtf8Printable(windowsErrorString(::GetLastError()))); + return false; + } + + // 2. we create the Python runner, we do not need to use ::LinkLibrary and + // ::GetProcAddress because we use delayed load for the runner DLL (see the + // CMakeLists.txt) + // m_Runner = mo2::python::createPythonRunner(); if (m_Runner) { diff --git a/src/proxy/proxypython.h b/src/proxy/proxypython.h index 60cd5a3..fb352dd 100644 --- a/src/proxy/proxypython.h +++ b/src/proxy/proxypython.h @@ -59,6 +59,7 @@ public: // IPluginDiagnose private: MOBase::IOrganizer* m_MOInfo; + HMODULE m_RunnerLib; std::unique_ptr m_Runner; enum class FailureType : unsigned int { diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index dde6288..9457ab6 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -add_library(runner STATIC) +add_library(runner SHARED) mo2_configure_library(runner SOURCE_TREE WARNINGS 4 @@ -11,7 +11,9 @@ mo2_configure_library(runner ) target_link_libraries(runner PRIVATE pybind11::embed pybind11::qt) target_include_directories(runner PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -mo2_install_target(runner) +target_compile_definitions(runner PRIVATE RUNNER_BUILD) + +# proxy will install runner # force runner to build mobase add_dependencies(runner mobase) diff --git a/src/runner/pythonrunner.h b/src/runner/pythonrunner.h index a7f5d58..78dbe95 100644 --- a/src/runner/pythonrunner.h +++ b/src/runner/pythonrunner.h @@ -8,6 +8,12 @@ #include #include +#ifdef RUNNER_BUILD +#define RUNNER_DLL_EXPORT Q_DECL_EXPORT +#else +#define RUNNER_DLL_EXPORT Q_DECL_IMPORT +#endif + namespace mo2::python { // python runner interface @@ -34,7 +40,7 @@ namespace mo2::python { // create the Python runner // - std::unique_ptr createPythonRunner(); + RUNNER_DLL_EXPORT std::unique_ptr createPythonRunner(); } // namespace mo2::python diff --git a/tests/runner/CMakeLists.txt b/tests/runner/CMakeLists.txt index 0b17188..12bf544 100644 --- a/tests/runner/CMakeLists.txt +++ b/tests/runner/CMakeLists.txt @@ -33,6 +33,7 @@ set(PYTHONPATH "${PYLIB_DIR}\\;$\\;${pythoncore}\\;${pyt set(extra_paths "${MO2_INSTALL_PATH}/bin/dlls") string(APPEND extra_paths "\\;${PYTHON_ROOT}/PCbuild/amd64") +string(APPEND extra_paths "\\;$") set_tests_properties(${runner-tests_gtests} PROPERTIES WORKING_DIRECTORY "${MO2_INSTALL_PATH}/bin"