Я довольно способен кодировать в 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
Здесь есть пара вопросов. Сначала 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.
VersionFunction
какint
(вместо указателя на что-то). Но аргумент*version
, безусловно, неверен для получения строки версии. В зависимости от того, что делает функция реальной версии, вам может потребоваться вызвать ее с помощью:char *version; VersionFunction(&version);
илиchar version[100]; VersionFunction(version)
. И вам понадобится (например,void (*VersionFunction)(char **);
илиvoid (*VersionFunction)(char *);
соответственно объявить указатель на функцию. И сделатьprintf("%s\n",version);
потому что версия может иметь%
формат символов в нем, что испортит printf