Скомпилируйте DLL в C / C ++, затем вызовите ее из другой программы - PullRequest
54 голосов
/ 11 мая 2009

Я хочу создать простую, простую DLL, которая будет экспортировать одну или две функции, а затем попытаться вызвать ее из другой программы ... Везде, где я до сих пор смотрел, есть сложные вопросы, разные способы связать вещи вместе, странные проблемы, которые я даже не начал осознавать, пока существуют ... Я просто хочу начать, сделав что-то вроде этого:

Создайте DLL, которая экспортирует некоторые функции, например,

int add2(int num){
   return num + 2;
}

int mult(int num1, int num2){
   int product;
   product = num1 * num2;
   return product;
}

Я компилирую с MinGW, я хотел бы сделать это в C, но если есть какие-то реальные различия, делающие это в C ++, я хотел бы знать и их. Я хочу знать, как загрузить эту DLL в другую C (и C ++) программу, а затем вызывать эти функции из нее. Моя цель, немного поиграв с DLL-библиотеками, состоит в том, чтобы сделать VB-интерфейс для C (++) кода, загрузив DLL-файлы в Visual Basic (у меня Visual Studio 6, я просто хочу сделать несколько форм и события для объектов в тех формах, которые вызывают DLL).

Мне нужно знать, как вызывать gcc (/ g ++), чтобы он создавал DLL, а также как писать (/ генерировать) файл экспорта ... и что я могу / не могу делать в DLL (например, я могу взять аргументы указателем / ссылкой из внешнего интерфейса VB? Может ли DLL вызывать теоретическую функцию во внешнем интерфейсе? Или у функции есть «указатель на функцию» (я даже не знаю, возможно ли это) из VB и назовите это?) Я совершенно уверен, что не могу передать вариант в DLL ... но это все, что я знаю на самом деле.

обновить снова

Хорошо, я понял, как скомпилировать его с помощью gcc, чтобы сделать dll, который я запускал

gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--out-implib,libmessage.a

а потом у меня была другая программа, загрузившая его и протестировавшая функции, и она отлично работала, Большое спасибо за совет, но я попытался загрузить его с VB6, как это

Public Declare Function add2 Lib "C:\c\dll\mydll.dll" (num As Integer) As Integer

тогда я просто вызвал add2 (text1.text) из формы, но он дал мне ошибку во время выполнения:

«Не удается найти точку входа DLL add2 в C: \ c \ dll \ mydll.dll»

это код, который я скомпилировал для DLL:

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

EXPORT int __stdcall add2(int num){
  return num + 2;
}

EXPORT int __stdcall mul(int num1, int num2){
  return num1 * num2;
}

вызов из программы на С, как это работает, хотя:

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

int main(){

  HANDLE ldll;
  int (*add2)(int);
  int (*mul)(int,int);

  ldll = LoadLibrary("mydll.dll");
  if(ldll>(void*)HINSTANCE_ERROR){
    add2 = GetProcAddress(ldll, "add2");
    mul = GetProcAddress(ldll, "mul");
    printf("add2(3): %d\nmul(4,5): %d", add2(3), mul(4,5));
  } else {
    printf("ERROR.");
  }

}

есть идеи?

решил

Чтобы решить предыдущую проблему, мне просто нужно было скомпилировать ее так:

gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--add-stdcall-alias

и используйте этот вызов API в VB6

Public Declare Function add2 Lib "C:\c\dll\mydll" _
    (ByVal num As Integer) As Integer

Я научился не забывать явно указывать ByVal или ByRef - я просто возвращал адрес аргумента, который я передал, похоже, -3048.

Ответы [ 5 ]

24 голосов
/ 11 мая 2009

Что касается построения DLL с использованием MinGW, вот несколько очень кратких инструкций.

Во-первых, вам нужно пометить свои функции для экспорта, чтобы они могли быть использованы вызывающими из DLL. Для этого измените их так, чтобы они выглядели (например)

__declspec( dllexport ) int add2(int num){
   return num + 2;
}

затем, предполагая, что ваши функции находятся в файле с именем funcs.c, вы можете скомпилировать их:

gcc -shared -o mylib.dll funcs.c

Флаг -shared указывает gcc создать DLL.

Чтобы проверить, действительно ли DLL экспортировала функции, возьмите бесплатный инструмент Dependency Walker и используйте его для проверки DLL.

Для бесплатной IDE, которая автоматизирует все флаги и т. Д., Необходимые для создания DLL, взгляните на превосходный Code :: Blocks , который очень хорошо работает с MinGW.

Редактировать: Подробнее об этом см. Статью Создание библиотеки MinGW для использования с Visual Basic в MinGW Wiki.

7 голосов
/ 11 мая 2009

Вот как вы это делаете:

В .ч

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

extern "C" // Only if you are using C++ rather than C
{    
  EXPORT int __stdcall add2(int num);
  EXPORT int __stdcall mult(int num1, int num2);
}

в .cpp

extern "C" // Only if you are using C++ rather than C
{    
EXPORT int __stdcall add2(int num)
{
  return num + 2;
}


EXPORT int __stdcall mult(int num1, int num2)
{
  int product;
  product = num1 * num2;
  return product;
}
}

Макрос сообщает вашему модулю (т.е. вашим файлам .cpp), что они предоставляют материал dll внешнему миру. Люди, использующие ваш файл .h, хотят импортировать те же функции, поэтому они продают EXPORT, а говорят компоновщику импортировать. Вам нужно добавить BUILD_DLL в опции компиляции проекта, и вы можете переименовать его в нечто явно специфичное для вашего проекта (в случае, если DLL использует вашу DLL).

Вам также может понадобиться создать файл .def, чтобы переименовать функции и удалить из них имена (C / C ++ искажает эти имена). Эта запись в блоге может быть интересной отправной точкой об этом.

Загрузка ваших собственных библиотек DLL аналогична загрузке системных библиотек. Просто убедитесь, что DLL находится на вашем системном пути. C: \ windows \ или рабочий каталог вашего приложения - это простое место для размещения вашей dll.

5 голосов
/ 11 мая 2009

Есть только одно отличие. Вы должны позаботиться или назвать mangling win C ++. Но на окнах вы должны заботиться о 1) определение функций, которые нужно экспортировать из DLL 2) написать так называемый файл .def, в котором перечислены все экспортируемые символы.

В Windows при компиляции DLL приходится использовать

__declspec (dllexport)

но, используя его, вы должны написать __declspec (DllImport)

Так что обычный способ сделать это что-то вроде

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

Именование немного сбивает с толку, потому что его часто называют EXPORT. Но это то, что вы найдете в большинстве заголовков где-то. Так что в вашем случае вы бы написали (с приведенным выше #define)

int DLL_EXPORT добавить .... int DLL_EXPORT mult ...

Помните, что вы должны добавить директиву препроцессора BUILD_DLL во время сборки общей библиотеки.

С уважением Friedrich

4 голосов
/ 11 мая 2009

При написании DLL на C ++ следует обращать внимание на искажение имен. Если вы хотите совместимости между C и C ++, вам будет лучше, если вы экспортируете не искаженные функции в стиле C из dll.

У вас есть два варианта использования DLL

  • Либо используйте файл lib для связи символов - динамическое связывание во время компиляции
  • Используйте LoadLibrary() или другую подходящую функцию для загрузки библиотеки, получения указателя на функцию (GetProcAddress) и вызова ее - динамическое связывание во время выполнения

Экспорт классов не будет работать, если вы будете следовать второму методу.

3 голосов
/ 11 мая 2009

Для VB6:

Вам необходимо объявить ваши функции C как __stdcall, в противном случае вы получите ошибки типа «недопустимое соглашение о вызовах». По поводу других ваших вопросов:

можно ли принимать аргументы указателем / ссылкой из внешнего интерфейса VB?

Да, используйте модификаторы ByRef / ByVal.

Может ли DLL вызывать теоретическую функцию во внешнем интерфейсе?

Да, используйте оператор AddressOf. Вы должны передать указатель функции в dll.

Или заставить функцию взять «указатель на функцию» (я даже не знаю, возможно ли это) из VB и вызвать ее?)

Да, используйте оператор AddressOf.

обновление (появилось больше вопросов:)):

чтобы загрузить его в VB, я просто делаю обычный метод (что я бы сделал, чтобы загрузить winsock.ocx или какую-то другую среду выполнения, но вместо этого нашел бы мою DLL) или я вставил вызов API в модуль?

Вам нужно удалить функцию API в коде VB6, как показано ниже:

Private Declare Function SHGetSpecialFolderLocation Lib "shell32" _
   (ByVal hwndOwner As Long, _
    ByVal nFolder As Long, _
    ByRef pidl As Long) As Long
...