Ctypes WindowsError: exception: access violation writing 0x0000000000000000 while calling a DLL function from another dll file
Ctypes WindowsError: exception: access violation writing 0x0000000000000000 while calling a DLL function from another dll file
我有一个程序可以从 linux 加载 .so 文件,它工作正常没有问题。
现在,我正在尝试使该程序跨平台。经过一段时间的努力,我设法编译了一个 dll 文件以支持 Windows 但是当我尝试从 ctypes 加载时,我得到这个错误:
"WindowsError: exception: access violation writing 0x0000000000000000"
它似乎甚至不能正确地将参数传递给我的 c 函数。我想我在为 Windows dll 转换我的 c 代码时可能犯了一些错误,或者我的 python 代码可能需要更多工作才能正确加载 dll 并在 Windows 中使用它.我熟悉 python 但我是 ctypes 和 C 的新手。我试图搜索我丢失的东西,但不知道该怎么做。 :(
我尝试了更多的东西,找到了错误发生的地方,但仍然不知道如何解决。所以当dll函数试图调用里面的另一个dll函数时,我的问题就出现了。我已经更新了我的代码以包含该部分。
我已经检查了我的 c 代码中的另一个 dll("mylib.dll") 调用是否正常,方法是在主函数中调用 initfunc(在另一个具有相同调用约定的 c 代码中。)所以我的"mylib.dll" 没有问题。我想如果我想从 dll 函数内部调用 dll 函数,我可能需要做更多的事情?
下面是我的 linux 和 Windows 的 C 代码以及我如何在 python 中调用它们。
我已经编辑了我的代码,使其成为最小的、完整的和可验证的示例,正如 Antti 所建议的那样。我是 Stack Overflow 的新手,一开始不明白 "Minimal, Complete, and Verifiable example" 是什么意思。感谢您的建议,并为我的无知感到抱歉。现在我可以用下面的代码重现同样的问题。
//header param_header.h
typedef struct MYSTRUCT MYSTRUCT;
struct MYSTRUCT
{
double param1;
double param2;
};
//mylib.c this was compiled as an .so(gcc mylib.c -fPIC -shared -o mylib.so) and .dll
#include "param_header.h"
#include <stdio.h>
#ifdef __linux__
int update_param(char *pstruct, char *paramname, double param)
#else
__declspec(dllexport) int update_param(char *pstruct, char *paramname, double param)
#endif
{
printf("Print this if function runs");
return 0;
}
//my_c_code.c --> this compiled again an .so & .dll and called by python ctypes
#include "param_header.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __linux__
#include <dlfcn.h>
#else
#include <windows.h>
#endif
#ifdef __linux__
MYSTRUCT *initfunc(flag, number, params, paramnames)
#else
__declspec(dllexport) MYSTRUCT *initfunc(flag, number, params, paramnames)
#endif
int flag;
int number;
double params[100];
char *paramnames[100];
{
int index;
int check;
MYSTRUCT *pstruct=(MYSTRUCT *)malloc(sizeof(MYSTRUCT));
memset(pstruct,0,sizeof(MYSTRUCT));
#ifdef __linux__
void *pHandle;
pHandle=dlopen("./mylib.so",RTLD_LAZY);
int(*update_param)(char*, char*, double) = dlsym(pHandle, "update_param");
#else
HINSTANCE pHandle;
pHandle=LoadLibrary("./mylib.dll");
int(__cdecl *update_param)(char*,char*, double);
FARPROC updateparam = GetProcAddress(pHandle, "update_param");
if (!updateparam)
{
check = GetLastError();
printf("%d\n", check);
}
update_param = (int(__cdecl *)(char*, char*, double))updateparam;
#endif
for (index=0;index < number;index++) {
(*update_param)((char*)pstruct, paramnames[index],params[index]); // <--this line fails only for the windows.
}
return pstruct;
}
下面是我的 python 访问函数的代码。
//mystruct.py
from ctypes import *
class MYSTRUCT(Structure):
_fields_ = [("param1",c_double),
("param2",c_double)]
//mypython code
from ctypes import *
from mystruct import *
mydll=cdll.LoadLibrary("./my_c_code.so")#"./my_c_code.dll" for windows.
libhandle=mydll._handle
c_initfunc=mydll.initfunc
c_initfunc.restype=POINTER(MYSTRUCT)
c_initfunc.argtypes=[c_int,c_int,c_double*100,c_char_p*100]
import numpy as np
param_dict={"a":1,"b":2}
params=(c_double * 100)(*np.float_(param_dict.values()))
paramnames=(c_char_p * 100)(*param_dict.keys())
flag=c_int(1)
number=c_int(len(param_dict.values()))
out=c_initfunc(flag, number, params, paramnames) <-- Error here.
我不确定这是否是调试的足够信息...但是结合以上 python 代码和 Linux c 代码编译的“.so”文件。我没有任何问题.. 但我得到了 dll 案例的错误。任何想法将不胜感激。
修复了您的 (Python) 代码中的 2 个错误后,我能够成功 运行 它。我没有猜测您的错误可能是什么(我仍然认为这是找不到 .dll 的问题,可能是由于命名错误),我反其道而行之并重构了您的代码。
我想指出的一件事是 ctypes 页面:[Python 3]: ctypes - A foreign function library for Python.
header.h:
#if defined(_WIN32)
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API
#endif
#define PRINT_MSG_0() printf("From C - [%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__)
typedef struct STRUCT_ {
double param1;
double param2;
} STRUCT;
dll0.c:
#include "header.h"
#include <stdio.h>
#define DLL0_API GENERIC_API
DLL0_API int updateParam(char *pstruct, char *paramname, double param) {
PRINT_MSG_0();
return 0;
}
dll1.c:
#include "header.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#define DLL1_API GENERIC_API
#define UPDATE_PARAM_FUNC_NAME "updateParam"
typedef int(__cdecl *UpdateParamFuncPtr)(char*, char*, double);
DLL1_API STRUCT *initFunc(flag, number, params, paramnames)
int flag;
int number;
double params[100];
char *paramnames[100];
{
int index = 0;
UpdateParamFuncPtr updateParam = NULL;
STRUCT *pStruct = (STRUCT*)malloc(sizeof(STRUCT));
memset(pStruct, 0, sizeof(STRUCT));
#if defined(_WIN32)
HMODULE pHandle = LoadLibrary("./dll0.dll");
if (!pHandle) {
printf("LoadLibrary failed: %d\n", GetLastError());
return NULL;
}
updateParam = (UpdateParamFuncPtr)GetProcAddress(pHandle, UPDATE_PARAM_FUNC_NAME);
if (!updateParam) {
printf("GetProcAddress failed: %d\n", GetLastError());
FreeLibrary(pHandle);
return NULL;
}
#else
void *pHandle = dlopen("./dll0.so", RTLD_LAZY);
if (!pHandle) {
printf("dlopen failed: %s\n", dlerror());
return NULL;
}
updateParam = dlsym(pHandle, UPDATE_PARAM_FUNC_NAME);
if (!updateParam) {
printf("dlsym failed: %s\n", dlerror());
dlclose(pHandle);
return NULL;
}
#endif
PRINT_MSG_0();
for (index = 0; index < number; index++) {
(*updateParam)((char*)pStruct, paramnames[index], params[index]);
}
#if defined(_WIN32)
FreeLibrary(pHandle);
#else
dlclose(pHandle);
#endif
return pStruct;
}
DLL1_API void freeStruct(STRUCT *pStruct) {
free(pStruct);
}
code.py:
#!/usr/bin/env python3
import sys
import traceback
from ctypes import c_int, c_double, c_char_p, \
Structure, CDLL, POINTER
class Struct(Structure):
_fields_ = [
("param1", c_double),
("param2", c_double),
]
StructPtr = POINTER(Struct)
DoubleArray100 = c_double * 100
CharPArray100 = c_char_p * 100
def main():
dll1_dll = CDLL("./dll1.dll")
init_func_func = dll1_dll.initFunc
init_func_func.argtypes = [c_int, c_int, DoubleArray100, CharPArray100]
init_func_func.restype = StructPtr
free_struct_func = dll1_dll.freeStruct
free_struct_func.argtypes = [StructPtr]
param_dict = {
b"a": 1,
b"b": 2,
}
params = DoubleArray100(*param_dict.values())
paramnames = CharPArray100(*param_dict.keys())
flag = 1
number = len(param_dict)
out = init_func_func(flag, number, params, paramnames)
print(out)
try:
struct_obj = out.contents
for field_name, _ in struct_obj._fields_:
print(" {:s}: {:}".format(field_name, getattr(struct_obj, field_name)))
except:
traceback.print_exc()
finally:
free_struct_func(out)
print("Done.")
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
备注:
- 做了一些重构
- 重命名:文件,变量,...(以“My”开头的名称,挠我的脑袋)
- 试图避免代码重复(这可以进一步改进)- 在公共区域(如文件、变量等)中提取它
- 其他非关键内容
- 添加了一个 freeStruct 函数,它将释放 initFunc 返回的指针,否则会内存泄漏
- C:
- 没有在 Lnx 上测试(没有启动 VM),但它应该有效
- 如果出错(GetProcAddressreturnsNULL),退出initFunc 而不是仅仅打印消息并走得更远。这是访问冲突
的一个很好的候选者
- 退出前卸载(内部).dll initFunc
- 颠倒了 Lnx / Win 条件逻辑(__linux__ / _WIN32 宏检查)因为 dlfcn 函数是通用的所有 Nixes(例如,如果您尝试在 OSX 或 Solaris[=94 上构建代码=](其中__linux__没有定义),会落在Win分支,明显失败)
- Python:
- 删除了 np。代码没有用它编译,绝对没有必要
- 将 param_dict 键从 str 更改为 bytes 以匹配
ctypes.c_char_p
(因为我使用的是 Python 3)
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>dir /b
code.py
dll0.c
dll1.c
header.h
original_code_dir
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>cl /nologo /DDLL /MD dll0.c /link /NOLOGO /DLL /OUT:dll0.dll
dll0.c
Creating library dll0.lib and object dll0.exp
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>cl /nologo /DDLL /MD dll1.c /link /NOLOGO /DLL /OUT:dll1.dll
dll1.c
Creating library dll1.lib and object dll1.exp
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>dir /b
code.py
dll0.c
dll0.dll
dll0.exp
dll0.lib
dll0.obj
dll1.c
dll1.dll
dll1.exp
dll1.lib
dll1.obj
header.h
original_code_dir
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
From C - [dll1.c] (56) - [initFunc]
From C - [dll0.c] (9) - [updateParam]
From C - [dll0.c] (9) - [updateParam]
<__main__.LP_STRUCT object at 0x000001B2D3AA80C8>
param1: 0.0
param2: 0.0
Done.
吼!吼!呼! :)
我有一个程序可以从 linux 加载 .so 文件,它工作正常没有问题。 现在,我正在尝试使该程序跨平台。经过一段时间的努力,我设法编译了一个 dll 文件以支持 Windows 但是当我尝试从 ctypes 加载时,我得到这个错误:
"WindowsError: exception: access violation writing 0x0000000000000000"
它似乎甚至不能正确地将参数传递给我的 c 函数。我想我在为 Windows dll 转换我的 c 代码时可能犯了一些错误,或者我的 python 代码可能需要更多工作才能正确加载 dll 并在 Windows 中使用它.我熟悉 python 但我是 ctypes 和 C 的新手。我试图搜索我丢失的东西,但不知道该怎么做。 :(
我尝试了更多的东西,找到了错误发生的地方,但仍然不知道如何解决。所以当dll函数试图调用里面的另一个dll函数时,我的问题就出现了。我已经更新了我的代码以包含该部分。
我已经检查了我的 c 代码中的另一个 dll("mylib.dll") 调用是否正常,方法是在主函数中调用 initfunc(在另一个具有相同调用约定的 c 代码中。)所以我的"mylib.dll" 没有问题。我想如果我想从 dll 函数内部调用 dll 函数,我可能需要做更多的事情?
下面是我的 linux 和 Windows 的 C 代码以及我如何在 python 中调用它们。
我已经编辑了我的代码,使其成为最小的、完整的和可验证的示例,正如 Antti 所建议的那样。我是 Stack Overflow 的新手,一开始不明白 "Minimal, Complete, and Verifiable example" 是什么意思。感谢您的建议,并为我的无知感到抱歉。现在我可以用下面的代码重现同样的问题。
//header param_header.h
typedef struct MYSTRUCT MYSTRUCT;
struct MYSTRUCT
{
double param1;
double param2;
};
//mylib.c this was compiled as an .so(gcc mylib.c -fPIC -shared -o mylib.so) and .dll
#include "param_header.h"
#include <stdio.h>
#ifdef __linux__
int update_param(char *pstruct, char *paramname, double param)
#else
__declspec(dllexport) int update_param(char *pstruct, char *paramname, double param)
#endif
{
printf("Print this if function runs");
return 0;
}
//my_c_code.c --> this compiled again an .so & .dll and called by python ctypes
#include "param_header.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __linux__
#include <dlfcn.h>
#else
#include <windows.h>
#endif
#ifdef __linux__
MYSTRUCT *initfunc(flag, number, params, paramnames)
#else
__declspec(dllexport) MYSTRUCT *initfunc(flag, number, params, paramnames)
#endif
int flag;
int number;
double params[100];
char *paramnames[100];
{
int index;
int check;
MYSTRUCT *pstruct=(MYSTRUCT *)malloc(sizeof(MYSTRUCT));
memset(pstruct,0,sizeof(MYSTRUCT));
#ifdef __linux__
void *pHandle;
pHandle=dlopen("./mylib.so",RTLD_LAZY);
int(*update_param)(char*, char*, double) = dlsym(pHandle, "update_param");
#else
HINSTANCE pHandle;
pHandle=LoadLibrary("./mylib.dll");
int(__cdecl *update_param)(char*,char*, double);
FARPROC updateparam = GetProcAddress(pHandle, "update_param");
if (!updateparam)
{
check = GetLastError();
printf("%d\n", check);
}
update_param = (int(__cdecl *)(char*, char*, double))updateparam;
#endif
for (index=0;index < number;index++) {
(*update_param)((char*)pstruct, paramnames[index],params[index]); // <--this line fails only for the windows.
}
return pstruct;
}
下面是我的 python 访问函数的代码。
//mystruct.py
from ctypes import *
class MYSTRUCT(Structure):
_fields_ = [("param1",c_double),
("param2",c_double)]
//mypython code
from ctypes import *
from mystruct import *
mydll=cdll.LoadLibrary("./my_c_code.so")#"./my_c_code.dll" for windows.
libhandle=mydll._handle
c_initfunc=mydll.initfunc
c_initfunc.restype=POINTER(MYSTRUCT)
c_initfunc.argtypes=[c_int,c_int,c_double*100,c_char_p*100]
import numpy as np
param_dict={"a":1,"b":2}
params=(c_double * 100)(*np.float_(param_dict.values()))
paramnames=(c_char_p * 100)(*param_dict.keys())
flag=c_int(1)
number=c_int(len(param_dict.values()))
out=c_initfunc(flag, number, params, paramnames) <-- Error here.
我不确定这是否是调试的足够信息...但是结合以上 python 代码和 Linux c 代码编译的“.so”文件。我没有任何问题.. 但我得到了 dll 案例的错误。任何想法将不胜感激。
修复了您的 (Python) 代码中的 2 个错误后,我能够成功 运行 它。我没有猜测您的错误可能是什么(我仍然认为这是找不到 .dll 的问题,可能是由于命名错误),我反其道而行之并重构了您的代码。
我想指出的一件事是 ctypes 页面:[Python 3]: ctypes - A foreign function library for Python.
header.h:
#if defined(_WIN32)
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API
#endif
#define PRINT_MSG_0() printf("From C - [%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__)
typedef struct STRUCT_ {
double param1;
double param2;
} STRUCT;
dll0.c:
#include "header.h"
#include <stdio.h>
#define DLL0_API GENERIC_API
DLL0_API int updateParam(char *pstruct, char *paramname, double param) {
PRINT_MSG_0();
return 0;
}
dll1.c:
#include "header.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#define DLL1_API GENERIC_API
#define UPDATE_PARAM_FUNC_NAME "updateParam"
typedef int(__cdecl *UpdateParamFuncPtr)(char*, char*, double);
DLL1_API STRUCT *initFunc(flag, number, params, paramnames)
int flag;
int number;
double params[100];
char *paramnames[100];
{
int index = 0;
UpdateParamFuncPtr updateParam = NULL;
STRUCT *pStruct = (STRUCT*)malloc(sizeof(STRUCT));
memset(pStruct, 0, sizeof(STRUCT));
#if defined(_WIN32)
HMODULE pHandle = LoadLibrary("./dll0.dll");
if (!pHandle) {
printf("LoadLibrary failed: %d\n", GetLastError());
return NULL;
}
updateParam = (UpdateParamFuncPtr)GetProcAddress(pHandle, UPDATE_PARAM_FUNC_NAME);
if (!updateParam) {
printf("GetProcAddress failed: %d\n", GetLastError());
FreeLibrary(pHandle);
return NULL;
}
#else
void *pHandle = dlopen("./dll0.so", RTLD_LAZY);
if (!pHandle) {
printf("dlopen failed: %s\n", dlerror());
return NULL;
}
updateParam = dlsym(pHandle, UPDATE_PARAM_FUNC_NAME);
if (!updateParam) {
printf("dlsym failed: %s\n", dlerror());
dlclose(pHandle);
return NULL;
}
#endif
PRINT_MSG_0();
for (index = 0; index < number; index++) {
(*updateParam)((char*)pStruct, paramnames[index], params[index]);
}
#if defined(_WIN32)
FreeLibrary(pHandle);
#else
dlclose(pHandle);
#endif
return pStruct;
}
DLL1_API void freeStruct(STRUCT *pStruct) {
free(pStruct);
}
code.py:
#!/usr/bin/env python3
import sys
import traceback
from ctypes import c_int, c_double, c_char_p, \
Structure, CDLL, POINTER
class Struct(Structure):
_fields_ = [
("param1", c_double),
("param2", c_double),
]
StructPtr = POINTER(Struct)
DoubleArray100 = c_double * 100
CharPArray100 = c_char_p * 100
def main():
dll1_dll = CDLL("./dll1.dll")
init_func_func = dll1_dll.initFunc
init_func_func.argtypes = [c_int, c_int, DoubleArray100, CharPArray100]
init_func_func.restype = StructPtr
free_struct_func = dll1_dll.freeStruct
free_struct_func.argtypes = [StructPtr]
param_dict = {
b"a": 1,
b"b": 2,
}
params = DoubleArray100(*param_dict.values())
paramnames = CharPArray100(*param_dict.keys())
flag = 1
number = len(param_dict)
out = init_func_func(flag, number, params, paramnames)
print(out)
try:
struct_obj = out.contents
for field_name, _ in struct_obj._fields_:
print(" {:s}: {:}".format(field_name, getattr(struct_obj, field_name)))
except:
traceback.print_exc()
finally:
free_struct_func(out)
print("Done.")
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
备注:
- 做了一些重构
- 重命名:文件,变量,...(以“My”开头的名称,挠我的脑袋)
- 试图避免代码重复(这可以进一步改进)- 在公共区域(如文件、变量等)中提取它
- 其他非关键内容
- 添加了一个 freeStruct 函数,它将释放 initFunc 返回的指针,否则会内存泄漏
- C:
- 没有在 Lnx 上测试(没有启动 VM),但它应该有效
- 如果出错(GetProcAddressreturnsNULL),退出initFunc 而不是仅仅打印消息并走得更远。这是访问冲突 的一个很好的候选者
- 退出前卸载(内部).dll initFunc
- 颠倒了 Lnx / Win 条件逻辑(__linux__ / _WIN32 宏检查)因为 dlfcn 函数是通用的所有 Nixes(例如,如果您尝试在 OSX 或 Solaris[=94 上构建代码=](其中__linux__没有定义),会落在Win分支,明显失败)
- Python:
- 删除了 np。代码没有用它编译,绝对没有必要
- 将 param_dict 键从 str 更改为 bytes 以匹配
ctypes.c_char_p
(因为我使用的是 Python 3)
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q053909121>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\Whosebug\q053909121>dir /b code.py dll0.c dll1.c header.h original_code_dir (py35x64_test) e:\Work\Dev\Whosebug\q053909121>cl /nologo /DDLL /MD dll0.c /link /NOLOGO /DLL /OUT:dll0.dll dll0.c Creating library dll0.lib and object dll0.exp (py35x64_test) e:\Work\Dev\Whosebug\q053909121>cl /nologo /DDLL /MD dll1.c /link /NOLOGO /DLL /OUT:dll1.dll dll1.c Creating library dll1.lib and object dll1.exp (py35x64_test) e:\Work\Dev\Whosebug\q053909121>dir /b code.py dll0.c dll0.dll dll0.exp dll0.lib dll0.obj dll1.c dll1.dll dll1.exp dll1.lib dll1.obj header.h original_code_dir (py35x64_test) e:\Work\Dev\Whosebug\q053909121>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32 From C - [dll1.c] (56) - [initFunc] From C - [dll0.c] (9) - [updateParam] From C - [dll0.c] (9) - [updateParam] <__main__.LP_STRUCT object at 0x000001B2D3AA80C8> param1: 0.0 param2: 0.0 Done.
吼!吼!呼! :)