Почему я не могу открыть / прочитать текстовый файл с расширением C, вызываемым из Python? - PullRequest
2 голосов
/ 18 июня 2020

Я пытаюсь передать имя файла из python в C \ C ++ .dll и заставить код C \ C ++ открыть / прочитать этот файл.

Вот очень простой пример того, что я пытаюсь добиться с помощью ctypes. Прошу простить меня за глупость моего кода на C ++. Я учусь здесь.

//C++ code in example.cpp compiled to example.dll
#include <stdio.h>


extern "C" {

char* _line;
char* test(char* txt){
FILE* f_name;
f_name = fopen(txt, "r");

//... read data from file and do something 

_line = txt;
return _line; // this is just my sanity check to see if "file.txt" got passed from python
}
}

Я использую cygwin-64 для компиляции в .dll:

g++ -fPIC -shared example.cpp -o example.dll

Python оболочка:

import ctypes

print("works!")  #just sanity check...

lib = ctypes.CDLL("example.dll")
cpp_file_access = lib.test
cpp_file_access.argtypes = [ctypes.c_char_p]
cpp_file_access.restype = ctypes.c_char_p

print(cpp_file_access(b"file.txt")) #sanity check to see if file name got passed 

Когда test () в .dll вызывается из python, программа не проходит мимо строки:

f_name = fopen(txt, "r");

Независимо от того, какой метод я использую для открытия / доступа к указанному текстовому файлу. Он либо «зависает» и ничего не делает, либо выдает ошибку WindowsError, указывающую на нарушение прав доступа 0x00000 (...). То же самое происходит, когда я использую любую функцию «печати» в коде C ++, например:

printf("test print !");
std::cout << "another test print";

.dll-код работает в других случаях должным образом, если я не пытаюсь открыть / прочитать текстовый файл или "напечатать" что-то в консоли в коде C ++, который вызывается из python. На самом деле, когда я компилирую его в файл .exe, он открывает / читает файл и без проблем выводит все на консоль.

Я не могу найти решение / ответ своей проблемы. Может, кто-нибудь здесь сможет просветить меня, что я делаю не так?

[РЕДАКТИРОВАТЬ # 1] Я кое-что заметил. Когда я попытался запустить этот код в Python3 .8, я получил сообщение об ошибке, что файлы:

cygwin1.dll
cygstdc++-6.dll
cyggcc_s-seh-1.dll

не могут быть найдены, даже если они находятся в папке bin cygwin и путь env задан правильно. Мне пришлось скопировать и вставить эти файлы в папку, где были example.dll и python wrapper (wrapper.py). Я не знаю, имеет ли это какое-либо отношение к моей проблеме, но я думаю, что об этом стоит упомянуть. Я начинаю очень подозрительно относиться к этому компилятору cygwin. Копание продолжается.

[РЕДАКТИРОВАТЬ # 2] Я изменил код C ++, добавив void test_2 (), чтобы между python и .dll не передавались аргументы.

__declspec(dllexport) void test_2(){
FILE* f_name;
f_name = fopen("file.txt", "r");
}

И назовите его от python:

void_acc = lib.test_2
void_acc.restype = None
void_acc()
print ("program ended, go away !") // .. sanity check

Проблема осталась прежней. Выполнение программы останавливается, когда она достигает строки fopen ().

Ответы [ 4 ]

1 голос
/ 18 июня 2020

.argtypes и .restype неверны. В коде C используется char*, поэтому используйте c_char_p:

import ctypes

lib = ctypes.CDLL("example.dll")
cpp_file_access = lib.test
cpp_file_access.argtypes = ctypes.c_char_p,
cpp_file_access.restype = ctypes.c_char_p

print(cpp_file_access(b'file.txt')) # pass byte string

Обратите внимание, что вам не нужно заключать параметр в тип ctypes. ctypes уже знает тип из .argtypes и будет жаловаться, если вы не передадите тип Python, совместимый с объявленным типом.

1 голос
/ 18 июня 2020

Две вещи с первого взгляда:

  • Вы должны убедиться, что функция экспортируется с использованием либо __declspe c (dllexport) перед объявлением функции, либо путем включения ее в файл def. (Если вы дойдете до строки fopen, это не проблема - просто упомяну для полноты.)
  • Вы указали широкий c строковый указатель (c_wchar_t) в Python, но объявили узким c строки (char *) в c, они должны совпадать.
0 голосов
/ 29 июня 2020

Если по какой-то причине ссылка на yt не работает или вам не хочется смотреть видео, я отправлю шаги из этого виудео для сохранности.

Go на: https://www.msys2.org/ и нажмите кнопку загрузки установщика. В моем случае это было (это может измениться в будущем):

msys2-x86_64-20200602.exe

Установите его. Помните, где вы его установили. Это пригодится через несколько минут. После установки запустите его (или выберите автоматический запуск после завершения установки). Появится окно командной строки. В этом окне командной строки напишите / pasete:

pacman -Syu --disable-download-timeout

Подождите, пока он завершит sh. После этого закройте окно. Go в выбранную вами папку установки. Запустите msys2.exe. Появится окно командной строки. Снова напишите или вставьте:

pacman -Syu --disable-download-timeout

... и дождитесь завершения sh. Оставьте окно командной строки открытым. Мы будем использовать его потом. Как только это будет сделано, go перейдите на исходную веб-страницу: https://www.msys2.org/ и прокрутите вниз, а go - чтобы «просмотреть: список пакетов». Слева go в «Поиск». Установите "Search in" в поле "base packages" и введите "gcc" в поле поиска. Выберите:

mingw-w64-gcc

Затем из списка "binary packages" выберите:

mingw-w64-x86_64-gcc

Найдите тег "Installation" (он должен быть в верхней части страницы). Там есть команда. Скопируйте / вставьте его в терминал:

pacman -S mingw-w64-x86_64-gcc --disable-download-timeout

("--disable-download-timeout" был добавлен) Подождите, пока он установится. Оставьте окно командной строки открытым. 2 вещи до go. В окне командной строки повторно вставьте предыдущую команду, но измените форму последних букв "gcc" на "gdb" или используйте это:

pacman -S mingw-w64-x86_64-gdb --disable-download-timeout

Подождите, пока она установится. Оставьте окно командной строки открытым. 1 штука на go. В окне командной строки повторно вставьте предыдущую команду, но измените форму последних букв "gdb" на "make" или используйте это:

pacman -S mingw-w64-x86_64-make --disable-download-timeout

Подождите, пока она установится. Мы закончили с установкой вещей. Однако есть еще один шаг - системная переменная пути. Go в папку, в которую вы установили msys2-x86_64-20200602.exe, там должны быть файл "msys2.exe" и папка "mingw64". Go в эту папку. Найдите папку "bin", скопируйте ее путь. Go в системные переменные. Щелкните правой кнопкой мыши значок вашего компьютера. Go к расширенным настройкам системы. Go в переменные среды (в нижней части). Найдите переменную с именем "path", отредактируйте ее. Ставить ";" в конце, если его еще нет. Вставьте путь к папке <rest of the path>/mingw64/bin в конце после «;».

Теперь вы можете использовать команду g ++ в терминале, например:

g++ -fPIC -shared example.cpp -o example.dll

(для создания файла .dll). Готово.

0 голосов
/ 26 июня 2020

РЕШЕНО.

В конце концов, это была проблема компилятора. Вместо использования cygwin мне следовало использовать mingw-64 bit.

https://www.msys2.org/

Я следовал инструкциям в этом видео yt: https://www.youtube.com/watch?v=aXF4A5UeSeM

Работает нормально.

...