C dll эквивалентно Python ctypes

1

Я довольно способен кодировать в python, но хочу начать использовать C для некоторых моих функций. Используя ctypes, я могу открыть DLL и использовать функции довольно легко. Поиск простого примера C этого эквивалента кажется невозможным. Я всегда нахожу 2 страницы C++ примеров, полных кода котельной плиты и не могу сказать, что происходит. Или, что еще хуже, примеры.Net и visual studio, которые импортируют всевозможные зависимости. Ниже приведен код python, который отлично работает, включая документацию API о функции DLL.

from the API Doc:
Mcp2210_GetLibraryVersion
int Mcp2210_GetLibraryVersion(wchar_t *version)
Description: Provides the library version as NULL terminated UNICODE string.
Parameters: Inputs: - none Outputs: - wchar_t *version - Pointer to buffer string of 64 bytes
(MPC2210_LIBRARY_VERSION_SIZE) in size (So it can accommodate max 30 wide characters.)
The version string, including the NULL terminator is copied in this buffer.
Cannot be NULL.
Returns:
- positive value: version string size (bytes) including the null character
- negative value: E_ERR_NULL (-10) if *version is NULL
from ctypes import windll, c_wchar, byref # bring in some ctypes objects
version = (c_wchar * 64)() # declare 64 index array to put unicode characters into
mcp2210_dll = windll.LoadLibrary("mcp2210_dll_um_x86.dll") # get a handle to the dll
ret_code = mcp2210_dll.Mcp2210_GetLibraryVersion(byref(version)) # call the function
print(ret_code, version.value) # display results

outputs -> (12, u'2.1.0')

ниже моя неудачная попытка использовать эту DLL в code :: blocks

#include <windows.h>
#include <stdio.h>
#include "mcp2210_dll_um.h"

int main()
{   HINSTANCE hinstDLL;
    hinstDLL = LoadLibrary("mcp2210_dll_um_x86.dll");
    if (hinstDLL != 0){
        printf("DLL loaded ok");
        char *version;
        int VersionFunction;
        VersionFunction = GetProcAddress(hinstDLL, "Mcp2210_GetLibraryVersion");
        VersionFunction(*version);
        printf(version);
    }
    else{
        return -1;
    }
    FreeLibrary(hinstDLL);
    return 0;
}

Кажется, что сборка и загрузка DLL, если я не оставляю строки, где я пытаюсь запустить эту функцию. Каков самый простой способ использовать эту DLL в C? Ошибка, которую я получаю:

...\dll_test_use\main.c | 13 | error: вызванный объект 'VersionFunction' не является указателем функции или функции |

EDIT: **** Решенный **** @dgnuff правильный.

Это все, что мне нужно сделать:

#include <stdio.h>
#include "mcp2210_dll_um.h"
int main(){
    int ret_code;
    wchar_t version[64];
    ret_code = Mcp2210_GetLibraryVersion(version);
    printf("%d\n", ret_code);
    return 0;
}

После этого, поскольку я использую code :: blocks, мне нужно перейти к настройкам компоновщика и добавить файл.lib.

Настройки/Компилятор/LinkerSettings/LinkLibraries: и добавьте mcp2210_dll_um_x86.lib

  • 1
    Это потому, что вы объявили VersionFunction как int (вместо указателя на что-то). Но аргумент *version , безусловно, неверен для получения строки версии. В зависимости от того, что делает функция реальной версии, вам может потребоваться вызвать ее с помощью: char *version; VersionFunction(&version); или char version[100]; VersionFunction(version) . И вам понадобится (например, void (*VersionFunction)(char **); или void (*VersionFunction)(char *); соответственно объявить указатель на функцию. И сделать printf("%s\n",version); потому что версия может иметь % формат символов в нем, что испортит printf
Теги:
dll
ctypes

1 ответ

2
Лучший ответ

Здесь есть пара вопросов. Сначала GetProcAddress() возвращает адрес точки входа названной функции. Хранение этого в int - это не очень полезная вещь, потому что вы не можете значимо назвать значение int.

С самого начала я не могу сказать вам подпись Mcp2210_GetLibraryVersion() но образованное предположение, основанное на python, говорит, что это нечто похожее на int Mcp2210_GetLibraryVersion(wchar_t *version); , Поэтому вам нужно объявить переменную Mcp2210_GetLibraryVersion в качестве указателя на соответствующую подпись функции. Там информация о деталях этого в Интернете: Здесь одна страница.

Все это сказало, в то время как LoadLibrary() - это один из способов получить доступ к DLL из C-кода, он обычно лучше по многим причинам, если вы статически связываете его. Я отмечаю, что у вас есть эта строка в вашем источнике:

#include "mcp2210_dll_um.h"

который имеет очень высокую вероятность быть "заголовком импорта" для загружаемой DLL. Если это так, то вам действительно не нужно делать LoadLibrary() GetProcAddress() shenanigans, вы можете просто напрямую обратиться к рассматриваемой рутине, а компоновщик/исполняемый загрузчик позаботится обо всем волшебстве для вас.

Это mcp2210_dll_um.lib что они являются подходящим mcp2210_dll_um.lib библиотеки mcp2210_dll_um.lib где-то, что вы можете связать в своем проекте: что "библиотека импорта", которая играет важную роль в обеспечении этого, все работает.

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

В нижней строке здесь подразумевается, что Mcp2210_GetLibraryVersion() действительно принимает широкий указатель на символ и заполняет указанное в местоположении строку с версией.

В этом случае вы хотите объявить версию как массив, а не указатель:

wchar_t version[64];

который должен точно соответствовать объявлению python:

version = (c_wchar * 64)()

Это создает массив, в то время как ваша первоначальная char *version; создал только указатель. wchar_t является эквивалентом C python c_wchar.

Теперь, чтобы передать этот массив в Mcp2210_GetLibraryVersion() вы просто назовите его следующим образом:

Mcp2210_GetLibraryVersion(version);

C (и C++) могут немного смущать, как работают массивы и указатели, что каждый из них и как они взаимодействуют друг с другом.

На самом деле короткий ответ заключается в том, что массивы фиксируются на месте, где бы они ни объявлялись и состояли из указанного количества объектов, все смежные друг с другом в памяти.

Указатели, с другой стороны, являются адресом чего-то. Поэтому просто объявление указателя на (например) wchar_t фактически не выделяет никаких wchar_t, оно просто дает вам то, что может содержать адрес любого wchar_t по вашему выбору.

- Изменить для разъяснения при использовании статической библиотеки DLL -

Из того, что вы сказали в своих комментариях, оно должно быть таким же простым, как включение файла заголовка в исходный файл C, который вы уже делаете, и просто вызов функции непосредственно в источнике C:

wchar_t version[64];
int retval = Mcp2210_GetLibraryVersion(version);

Вы должны быть немного осторожны при передаче version printf() поскольку версия представляет собой широкую символьную строку, и по умолчанию printf() имеет дело с нормальными строками char. Посмотрите на _wprintf() или _tprintf() чтобы это работало правильно.

Что касается связывания в библиотеке, то как вы это делаете, зависит от того, ссылаетесь ли вы на компоновщик непосредственно из командной строки или make файла или строите ли вы как проект Visual Studio.

В первом случае у вас будет что-то вроде

link.exe /option /option filename.obj other_filename.obj etc. etc. etc.

Вам просто нужно добавить библиотеку mcp2210_dll_um_x86.lib в командную строку, и она должна работать. Обратите внимание, что если библиотека отключена в другом каталоге, вы можете использовать ее полный путь для ее имени или предоставить параметр /LIBPATH чтобы сообщить компоновщику, в какой директории он находится. Подробнее см. Документацию для LINK.EXE.

Если вы делаете это с помощью Visual Studio, все это зависит от свойств проекта, раздела Linker. На общей странице укажите каталог библиотеки в поле " Additional Library Directories, а затем на следующей странице вниз: " Input предоставит имя библиотеки, включая .lib в Additional Dependencies

К сожалению, если вы делаете это с Cygwin/GCC, я не уверен в точном механизме, так как я никогда не использую эти инструменты под Windows.

  • 0
    Отличный ответ. Я надеюсь, что вы можете подробнее рассказать об использовании заголовка и файла lib. Я имею и то и другое и предположил, что был более элегантный способ вызова функций. Строка в файле заголовка для этой функции: / * Определение API DLL MCP2210 UM * / MCP2210_DLL_UM_API int __stdcall Mcp2210_GetLibraryVersion (wchar_t * version); Если я #include заголовок, есть ли способ вызвать это напрямую, без LoadLibrary ()? Я предполагаю, что мне нужно связать файл .lib в настройках компоновщика.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню