Ctypes WindowsError: исключение: нарушение прав доступа, запись 0x0000000000000000 при вызове функции DLL из другого файла DLL - PullRequest
0 голосов
/ 24 декабря 2018

У меня есть программа, которая загружает .so файл из linux, который работает без проблем.Сейчас я пытаюсь сделать программу кроссплатформенной.После непродолжительных усилий мне удалось скомпилировать dll-файл для поддержки Windows, но когда я попытался загрузить из ctypes, я получаю эту ошибку:

"WindowsError: исключение: нарушение прав доступа, запись 0x0000000000000000"

Кажется, он даже не может правильно передать аргументы моей c-функции.Я думаю, что, возможно, допустил некоторую ошибку при конвертации моего c-кода для Windows DLL или моего кода python, возможно, потребуется больше работы для правильной загрузки dll и использования его в Windows.Я знаком с Python, но я новичок в обоих типах и C .Я пытался найти то, что мне не хватает, но не мог понять, что делать.: (

Я попробовал еще несколько вещей и нашел, где возникает ошибка, но все еще не знаю, как ее решить. Поэтому моя проблема возникает, когда функция dll пытается вызвать другую функцию dll внутри.обновил мой код, добавив эту часть.

Я проверил, что другой вызов dll ("mylib.dll") внутри моего c-кода работает нормально, вызывая initfunc внутри главной функции (в другом c-коде)с тем же соглашением о вызовах.) Так что мой mylib.dll не имеет проблемы. Я предполагаю, что мне, возможно, придется сделать что-то еще, если я хочу вызвать функцию dll из функции dll?

Ниже приведен мой c-код для linux и Windows и как я их называю на python.

Я отредактировал свой код так, чтобы это был пример Minimal, Complete и Verifiable, как предложил Антти.Я новичок в Stack Overflow и сначала не понимал, что значит делать «Минимальный, завершенный и проверяемый пример». Спасибо за совет и извините за мое невежество. Теперь я могу воспроизвести ту же проблему с моимкод ниже.

//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.Любая идея будет оценена.

1 Ответ

0 голосов
/ 26 декабря 2018

После исправления 2 ошибок в вашем ( Python ) коде я смог его успешно запустить.Вместо того, чтобы угадывать, в чем может быть ваша ошибка (я все еще думаю, что дело в том, что .dll не найдено, возможно, из-за неправильного именования), я пошел другим путем и реорганизовал ваш код.
Я хочу отметить одну вещь: ctypes page: [Python 3]: ctypes - библиотека сторонних функций для 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 ), но он должен работать
    • Если что-то идет не так ( GetProcAddress возвращает NULL ), выход initFunc вместо простой печати сообщения и продолжения.Это был хороший кандидат на нарушение прав доступа
    • Выгрузить (внутренний) .dll перед выходом initFunc
    • Reversedусловная логика Lnx / Win ( __ linux __ / _WIN32 проверка макроса) с dlfcn функции являются общими для всех Nix es (и если вы, например, попытаетесь построить свой код на OSX или Solaris (где __ linux __ не определено), оно попадет на ветку Win и, очевидно, завершится ошибкой)
  • Python :
    • Удалено np .Код не компилировался с ним, и он абсолютно не нужен
    • Изменено param_dict ключи от str до байтов для соответствия ctypes.c_char_p(как я использую Python 3 )

Вывод :

(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!HOOO!:)

...