从 VC++ 延迟加载 Python3 dll
Delayload Python3 dll from VC++
我正在开发一个支持 Python 扩展的程序,我注意到如果用户的机器上没有 Python 或使用 x64 版本而不是 x32,它就不会打开一。 (我无法更改最后一部分,因为它不取决于我)。
所以我一直在阅读延迟加载以稍后检查库是否可用并这样做:
// linker: /DELAYLOAD:python3.dll
#include <delayimp.h>
#include <Python.h>
#pragma comment(lib, "delayimp")
#pragma comment(lib, "python3")
...一切正常,直到工作室给我这个问题:
LNK1194 cannot delay-load 'python3.dll' due to import of data symbol
'__imp__PyType_Type'; link without /DELAYLOAD:python3.dll
所以我的问题是:这个问题有解决方法吗?
我一直在考虑从他们的 GitHub 页面编辑包含并直接在我的程序中定义 PyType_Type,但我害怕破坏某些东西...
谢谢。
所以我找到了 Raymond Chen 写的一篇关于 dll 转发的 post。这可能是一个解决方案,所以我暂时 post 在这里,如果可行的话稍后更新。
陈百强post:https://devblogs.microsoft.com/oldnewthing/20080204-00/?p=23593
编辑:对于其他库来说这似乎是一个不错的选择,但对于 Python 却不是。他们已经在这样做了(python3 dll 是对 python3X 的转发)并且 __imp__PyType_Type 仍然是一个问题。
最后我只是创建了一个 lib 的副本并将其放在一个名为 PyLibrary 的文件夹中,该文件夹使用 Python.
编译
Edit2:最后我可能会 python 关闭并进入另一个模块并延迟加载该模块。
这是一个小演示(正如我在评论中所想的那样)
dll00.h:
#pragma once
#if defined(_WIN32)
# if defined DLL00_EXPORTS
# define DLL00_EXPORT_API __declspec(dllexport)
# else
# define DLL00_EXPORT_API __declspec(dllexport)
# endif
#else
# define DLL00_EXPORT_API
#endif
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API int dllPyFunc(const char *pyStr);
#if defined(__cplusplus)
}
#endif
dll00.c:
#define DLL00_EXPORTS
#include "dll00.h"
#include <Python.h>
#include <stdio.h>
int dllPyFunc(const char *pyStr)
{
if (!pyStr) {
printf("NULL PY test!\n");
return -1;
}
int res = 0;
if (!Py_IsInitialized())
Py_InitializeEx(0);
res = PyRun_SimpleString(pyStr);
res |= Py_FinalizeEx();
return res;
}
main00.c:
#if defined(DELAYLOAD)
# include "dll00.h"
# pragma comment(lib, "delayimp")
# pragma comment(lib, "dll00")
#endif
#include <Windows.h>
#include <stdio.h>
#if !defined(DELAYLOAD)
typedef int (*DllPyFuncFuncPtr)(const char *pyStr);
DllPyFuncFuncPtr dllPyFunc = NULL;
#endif
int main(int argc, char *argv[])
{
int ret = 0;
printf("Arg count: %d\n", argc);
if (argc == 1) {
printf("NO PYTHON WHATSOEVER!!!\n");
ret = 0;
} else {
printf("Attempt to run [%s] from Python\n", argv[1]);
#if !defined(DELAYLOAD)
HMODULE hDll00 = LoadLibrary("dll00.dll");
if (hDll00 == NULL) {
printf("Error loading dll: %d\n", GetLastError());
return -1;
}
dllPyFunc = (DllPyFuncFuncPtr)GetProcAddress(hDll00, "dllPyFunc");
if (dllPyFunc == NULL) {
printf("Error getting function: %d\n", GetLastError());
FreeLibrary(hDll00);
hDll00 = NULL;
return -2;
}
#endif
ret = dllPyFunc(argv[1]);
#if !defined(DELAYLOAD)
FreeLibrary(hDll00);
hDll00 = NULL;
#endif
}
printf("\nDone.\n");
return ret;
}
输出:
[cfati@CFATI-W10PC064:e:\Work\Dev\Whosebug\q069418904]> sopr.bat
### Set shorter prompt to better fit when pasted in Whosebug (or other) pages ###
[prompt]> "c:\Install\pc032\MS\VisualStudioCommunity19\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul
[prompt]> dir /b
dll00.c
dll00.h
main00.c
[prompt]> :: Build .dll
[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python.08\include" dll00.c /link /NOLOGO /DLL /OUT:dll00.dll /LIBPATH:"c:\Install\pc064\Python\Python.08\libs"
dll00.c
Creating library dll00.lib and object dll00.exp
[prompt]> :: Build .exe (dynamic .dll load)
[prompt]> cl /nologo /MD /W0 main00.c /link /NOLOGO /OUT:main00_pc064.exe
main00.c
[prompt]> :: Build .exe (delayed .dll load)
[prompt]> cl /nologo /MD /W0 /DDELAYLOAD main00.c /link /NOLOGO /OUT:main00_dl_pc064.exe /DELAYLOAD:dll00.dll
main00.c
[prompt]> dir /b
dll00.c
dll00.dll
dll00.exp
dll00.h
dll00.lib
dll00.obj
main00.c
main00.obj
main00_dl_pc064.exe
main00_pc064.exe
[prompt]> :: Save current path (which doesn't have python38.dll's parent)
[prompt]> set _PATH=%PATH%
[prompt]> :: Add python38.dll's parent to PATH
[prompt]> set PATH=%_PATH%;c:\Install\pc064\Python\Python.08
[prompt]>
[prompt]> main00_pc064.exe
Arg count: 1
NO PYTHON WHATSOEVER!!!
Done.
[prompt]> main00_pc064.exe "import os;print(os.getcwd())"
Arg count: 2
Attempt to run [import os;print(os.getcwd())] from Python
e:\Work\Dev\Whosebug\q069418904
Done.
[prompt]> main00_dl_pc064.exe
Arg count: 1
NO PYTHON WHATSOEVER!!!
Done.
[prompt]> main00_dl_pc064.exe "import os;print(os.getcwd())"
Arg count: 2
Attempt to run [import os;print(os.getcwd())] from Python
e:\Work\Dev\Whosebug\q069418904
Done.
[prompt]> :: NO python38.dll
[prompt]> set PATH=%_PATH%
[prompt]> main00_pc064.exe
Arg count: 1
NO PYTHON WHATSOEVER!!!
Done.
[prompt]> main00_pc064.exe "import os;print(os.getcwd())"
Arg count: 2
Attempt to run [import os;print(os.getcwd())] from Python
Error loading dll: 126
[prompt]> main00_dl_pc064.exe
Arg count: 1
NO PYTHON WHATSOEVER!!!
Done.
[prompt]> main00_dl_pc064.exe "import os;print(os.getcwd())"
Arg count: 2
Attempt to run [import os;print(os.getcwd())] from Python
<<<<<<<< CRASH HERE >>>>>>>>
备注:
- 虽然这个例子太琐碎无法证明,动态加载库需要更多代码(在 .exe 端)
- 另一方面,如果 Python 是必需的但不存在于 %PATH%[ 中,则延迟加载方法会崩溃=44=]
我正在开发一个支持 Python 扩展的程序,我注意到如果用户的机器上没有 Python 或使用 x64 版本而不是 x32,它就不会打开一。 (我无法更改最后一部分,因为它不取决于我)。
所以我一直在阅读延迟加载以稍后检查库是否可用并这样做:
// linker: /DELAYLOAD:python3.dll
#include <delayimp.h>
#include <Python.h>
#pragma comment(lib, "delayimp")
#pragma comment(lib, "python3")
...一切正常,直到工作室给我这个问题:
LNK1194 cannot delay-load 'python3.dll' due to import of data symbol '__imp__PyType_Type'; link without /DELAYLOAD:python3.dll
所以我的问题是:这个问题有解决方法吗?
我一直在考虑从他们的 GitHub 页面编辑包含并直接在我的程序中定义 PyType_Type,但我害怕破坏某些东西...
谢谢。
所以我找到了 Raymond Chen 写的一篇关于 dll 转发的 post。这可能是一个解决方案,所以我暂时 post 在这里,如果可行的话稍后更新。
陈百强post:https://devblogs.microsoft.com/oldnewthing/20080204-00/?p=23593
编辑:对于其他库来说这似乎是一个不错的选择,但对于 Python 却不是。他们已经在这样做了(python3 dll 是对 python3X 的转发)并且 __imp__PyType_Type 仍然是一个问题。
最后我只是创建了一个 lib 的副本并将其放在一个名为 PyLibrary 的文件夹中,该文件夹使用 Python.
编译Edit2:最后我可能会 python 关闭并进入另一个模块并延迟加载该模块。
这是一个小演示(正如我在评论中所想的那样)
dll00.h:
#pragma once
#if defined(_WIN32)
# if defined DLL00_EXPORTS
# define DLL00_EXPORT_API __declspec(dllexport)
# else
# define DLL00_EXPORT_API __declspec(dllexport)
# endif
#else
# define DLL00_EXPORT_API
#endif
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API int dllPyFunc(const char *pyStr);
#if defined(__cplusplus)
}
#endif
dll00.c:
#define DLL00_EXPORTS
#include "dll00.h"
#include <Python.h>
#include <stdio.h>
int dllPyFunc(const char *pyStr)
{
if (!pyStr) {
printf("NULL PY test!\n");
return -1;
}
int res = 0;
if (!Py_IsInitialized())
Py_InitializeEx(0);
res = PyRun_SimpleString(pyStr);
res |= Py_FinalizeEx();
return res;
}
main00.c:
#if defined(DELAYLOAD)
# include "dll00.h"
# pragma comment(lib, "delayimp")
# pragma comment(lib, "dll00")
#endif
#include <Windows.h>
#include <stdio.h>
#if !defined(DELAYLOAD)
typedef int (*DllPyFuncFuncPtr)(const char *pyStr);
DllPyFuncFuncPtr dllPyFunc = NULL;
#endif
int main(int argc, char *argv[])
{
int ret = 0;
printf("Arg count: %d\n", argc);
if (argc == 1) {
printf("NO PYTHON WHATSOEVER!!!\n");
ret = 0;
} else {
printf("Attempt to run [%s] from Python\n", argv[1]);
#if !defined(DELAYLOAD)
HMODULE hDll00 = LoadLibrary("dll00.dll");
if (hDll00 == NULL) {
printf("Error loading dll: %d\n", GetLastError());
return -1;
}
dllPyFunc = (DllPyFuncFuncPtr)GetProcAddress(hDll00, "dllPyFunc");
if (dllPyFunc == NULL) {
printf("Error getting function: %d\n", GetLastError());
FreeLibrary(hDll00);
hDll00 = NULL;
return -2;
}
#endif
ret = dllPyFunc(argv[1]);
#if !defined(DELAYLOAD)
FreeLibrary(hDll00);
hDll00 = NULL;
#endif
}
printf("\nDone.\n");
return ret;
}
输出:
[cfati@CFATI-W10PC064:e:\Work\Dev\Whosebug\q069418904]> sopr.bat ### Set shorter prompt to better fit when pasted in Whosebug (or other) pages ### [prompt]> "c:\Install\pc032\MS\VisualStudioCommunity19\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul [prompt]> dir /b dll00.c dll00.h main00.c [prompt]> :: Build .dll [prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python.08\include" dll00.c /link /NOLOGO /DLL /OUT:dll00.dll /LIBPATH:"c:\Install\pc064\Python\Python.08\libs" dll00.c Creating library dll00.lib and object dll00.exp [prompt]> :: Build .exe (dynamic .dll load) [prompt]> cl /nologo /MD /W0 main00.c /link /NOLOGO /OUT:main00_pc064.exe main00.c [prompt]> :: Build .exe (delayed .dll load) [prompt]> cl /nologo /MD /W0 /DDELAYLOAD main00.c /link /NOLOGO /OUT:main00_dl_pc064.exe /DELAYLOAD:dll00.dll main00.c [prompt]> dir /b dll00.c dll00.dll dll00.exp dll00.h dll00.lib dll00.obj main00.c main00.obj main00_dl_pc064.exe main00_pc064.exe [prompt]> :: Save current path (which doesn't have python38.dll's parent) [prompt]> set _PATH=%PATH% [prompt]> :: Add python38.dll's parent to PATH [prompt]> set PATH=%_PATH%;c:\Install\pc064\Python\Python.08 [prompt]> [prompt]> main00_pc064.exe Arg count: 1 NO PYTHON WHATSOEVER!!! Done. [prompt]> main00_pc064.exe "import os;print(os.getcwd())" Arg count: 2 Attempt to run [import os;print(os.getcwd())] from Python e:\Work\Dev\Whosebug\q069418904 Done. [prompt]> main00_dl_pc064.exe Arg count: 1 NO PYTHON WHATSOEVER!!! Done. [prompt]> main00_dl_pc064.exe "import os;print(os.getcwd())" Arg count: 2 Attempt to run [import os;print(os.getcwd())] from Python e:\Work\Dev\Whosebug\q069418904 Done. [prompt]> :: NO python38.dll [prompt]> set PATH=%_PATH% [prompt]> main00_pc064.exe Arg count: 1 NO PYTHON WHATSOEVER!!! Done. [prompt]> main00_pc064.exe "import os;print(os.getcwd())" Arg count: 2 Attempt to run [import os;print(os.getcwd())] from Python Error loading dll: 126 [prompt]> main00_dl_pc064.exe Arg count: 1 NO PYTHON WHATSOEVER!!! Done. [prompt]> main00_dl_pc064.exe "import os;print(os.getcwd())" Arg count: 2 Attempt to run [import os;print(os.getcwd())] from Python <<<<<<<< CRASH HERE >>>>>>>>
备注:
- 虽然这个例子太琐碎无法证明,动态加载库需要更多代码(在 .exe 端)
- 另一方面,如果 Python 是必需的但不存在于 %PATH%[ 中,则延迟加载方法会崩溃=44=]