I have a program that load .so file from linux which works fine with no problem. Now, I'm trying to make the program cross-platform. After struggling for a while, I have managed to compile a dll file to support Windows but when I tried to load from ctypes, I get this error:
"WindowsError: exception: access violation writing 0x0000000000000000"
It seems it can't even properly pass the arguments to my c-function. I'm thinking I might have made some mistake while converting my c-code for Windows dll or the my python code probably need some more work for properly loading dll and utilize it in Windows. I'm familiar with python but am an novice to both ctypes and C. I tried to search what I'm missing but couldn't figure out what to do. :(
I've tried few more things and found where the error occurs but still don't know how to solve. So my problem occurs when the dll function tries to call another dll function inside. I've updated my code to include that part.
I've checked that another dll("mylib.dll") call inside my c-code works fine by calling the initfunc inside a main function(In another c-code with the same calling convention.) So my "mylib.dll" doesn't have a problem. I'm guessing I might have to do something more if I want to call a dll function from inside a dll function?
Below is my c-code for linux and Windows and How I call them in python.
I have edited my code so it would be Minimal, Complete, and Verifiable example, as Antti suggested. I'm quite new to Stack Overflow and didn't understand what it means to make "Minimal, Complete, and Verifiable example" at first. Thanks for the advice and sorry for my ignorance. Now I can reproduce the same problem with my code below.
//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;
}
And below is my python code to access the function.
//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.
I'm not sure if this is an enough information to debug... but with the combination of above python code and Linux c-code compiled ".so" file. I don't have any problem.. but I get the error for the dll case. Any idea will be appreciated.
After fixing 2 errors in your (Python) code, I was able to successfully run it. Instead of guessing what your error might be (I still think it's a matter of .dll not being found, maybe due to wrong naming), I went the other way around and refactored your code.
One thing that I want to point out is ctypes page: [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()
Notes:
ctypes.c_char_p
(as I'm using Python 3)Output:
(py35x64_test) e:\Work\Dev\StackOverflow\q053909121>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\StackOverflow\q053909121>dir /b code.py dll0.c dll1.c header.h original_code_dir (py35x64_test) e:\Work\Dev\StackOverflow\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\StackOverflow\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\StackOverflow\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\StackOverflow\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.
Ho! Ho! Hooo! :)