Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.60/40: Рейтинг темы: голосов - 40, средняя оценка - 4.60
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
1

Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW)

07.02.2014, 17:22. Показов 8353. Ответов 46
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Не, реально, зачем он?

...Друзья! Много где в инете вы найдёте как кропать dll-ки, например тут. обратите внимание на командную строку:

Bash
1
gcc -c -DBUILD_DLL dllfct.c
Да, так вот. Много где приводят примеры с использованием этго параметра, но зачем он никто толком не знает.

А вот я не поленился и скомандовал --help для каждого из экзешников лежащих в папке mingw\bin- ни для одного из экзешников такого параметра нет. То есть он просто-напросто ни одним из экзешников не используется. Вот результаты, кому интересно, в архиве.

Так зачем же нужен параметр -DBUILD_DLL? Спасибо, кто откликнется.
Вложения
Тип файла: rar rez.rar (46.3 Кб, 14 просмотров)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
07.02.2014, 17:22
Ответы с готовыми решениями:

AddItem и TObject. Для чего нужен второй параметр в приведенном коде
Непонял для чего нужен второй параметр и как его использовать?...

Подскажите для чего нужен WdfCoInstallerXXXX.dll
Коллеги, приветствую! Пишу мини-фильтр клавиатуры. При создание Empty WDF проекта драйвера, в...

Создание и использование dll- Для чего dllimport?
Всем доброго времени суток Вот пытаюсь разобраться в создании и использовании dll. По данной...

Нужно найти путь к компилятору MinGW в QT Creator
Всем привет,установил недавно Qt creator,но сразу же появилась проблема - прога не видит мой...

46
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
07.02.2014, 19:04  [ТС] 2
Блин, тихий ужас. Оказывается, -DBUILD_DLL это -D BUILD_DLL! То есть то же самое, что
C++
1
#define BUILD_DLL
Почему бы просто не определять в исходнике макрос? Один кто-то бездумно скопипастил и пошёл-поехал гулять по инету вариант с -D
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
07.02.2014, 20:42 3
Цитата Сообщение от kravam Посмотреть сообщение
Почему бы просто не определять в исходнике макрос?
Потому что ленивые программисты используют один и тот же заголовок и для построения самой DLL, где содержащиеся в ней функции описаны как экспортируемые (со спецификатором __declspec(dllexport) в случае Visual Studio), и для использующих ту DLL программ, где их описывают уже как импортируемые, с модификатором __declspec(dllimport). Переключение как раз осуществляется проверкой макроса BUILD_DLL. Менять исходный код, добавляя-убирая этот макрос в зависимости от того, что мы собираем, неудобно. А удобно привязывать этот макрос к проекту, добавляя его в параметры компилятора, а лучше в свойства проекта в IDE, откуда он всё равно попадёт в командную строку.
2
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
07.02.2014, 21:00  [ТС] 4
Ну я так понял, в зависимости от параметра командной строки __declspec(dllexport) может поменяться на __declspec(dllimport) и наоборот. Это, наверное, удобно. Только вот я не понял, для чего мне нужно сообщать компилятору, чтобы, например функция foo экспортируется (то есть для чего, к примеру применять __declspec(dllexport)).

...Итак, я написал *.dll, определил в ней функцию foo и тело, что очень важно. Предполагаю, что функция из этой dll-ки будет экспортироваться. Теперь мне надо позаботиться об имени этой функции (модификаторы __cdecl, __stdcall и Extern "C") а также создать файл *.a, чтобы слинковать эту dll-ку с экзешником, из которого будет вызываться foo. И всё это БЕЗ __declspec(dllexport). То есть не пойму, зачем этот модификатор нужен.
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
07.02.2014, 21:45 5
А что делает файл *.a? Он всего лишь загружает DLL и вынимает из неё адреса функций, на которые и перекидывает вызовы. Это же можно делать и вручную, через LoadLibrary / GetProcAddress, без статических библиотек.
А чтобы вынуть из DLL адрес функции по её имени, необходимо, чтобы в DLL содержалась таблица имён и адресов экспортируемых функций. А чтобы компилятор знал, что в эту таблицу запихать, и нужна пометка __declspec(dllexport). Этот модификатор - единственная вещь, которая действительно необходима для экспорта функций из DLL, а остальные перечисленные вещи нужны только для согласования и/или удобства.
1
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
07.02.2014, 22:30  [ТС] 6
C++
1
2
3
4
5
6
7
8
#include <windows.h>
#include <stdio.h>
int main ()
{
     HMODULE deskriptor_modula= LoadLibrary ("tst.dll");
     FARPROC adres_funktsii= GetProcAddress (deskriptor_modula, "tstfunc");
     return 0;
}
Об этом речь?
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
08.02.2014, 09:15 7
Да, об этом. Там в примере не хватает ещё вызова tstfunc для полноты картины.
0
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
08.02.2014, 22:19  [ТС] 8
Эх, нехороший я человек. Так всё складывалось в стройную картину, а тут я залез со своим нытьём.

Не по теме:

Кстати, в разделе Java это называется хамством.



код dll-ки
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//A.cpp
#include <stdio.h>
#include <windows.h>
 
extern "C" void functsia () {
  printf ("это я и меня звать functsia");
}
 
 
extern "C"
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )  
{
    return TRUE; 
}
код экзешника
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//B.cpp
#include <windows.h>
#include <stdio.h>
 
 
int main ()
 
{
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 
 
 
      HMODULE DeskriptorModula= LoadLibrary ((char*)"A.dll");
      FARPROC adres_funktsii= GetProcAddress (DeskriptorModula, "functsia");
      
      (*adres_funktsii)();
      getchar ();
      
      
      return (0);
 
}
командуем
Bash
1
2
3
4
5
6
7
8
9
10
11
rem удаляем A.dll и B.exe Для чистоты эксперимента 
del A.dll 
 
rem Кропаем A.dll
gcc -shared -o A.dll A.cpp
 
rem Кропаем B.exe
gcc -o B.exe B.cpp 
 
rem удаляем
del libAdll.a
тычем на B.exe, вывод:

Bash
1
это я и меня звать functsia
и это, заметьте, безо всяких
C++
1
__declspec(dllexport)
. Извините...
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
10.02.2014, 18:19 9
Умный, хорошо выдрессированный компилятор, который в определённых случаях сам умеет догадываться, какие функции надо экспортировать - это хорошо и полезно. Но не каждый компилятор так хорошо выдрессирован, а в ряде случаев (обычно, при создании реальных DLL, в которых не каждую функцию надо экспортировать) даже с дрессированным компилятором лучше всё же вручную указывать, кто идёт на экспорт.
Для чистоты эксперимента - а что будет, если в этом примере добавить ещё одну функцию, уже со спецификатором экспорта? Будут ли экспортироваться обе?
1
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
10.02.2014, 19:05  [ТС] 10
Цитата Сообщение от kravam Посмотреть сообщение
__declspec(dllexport)
влияет на запихивание в таблицу экспорта? В смысле не куда-то там, а в определённой место, называемое таблицей экспорта?

+++++++++++++++++++++++++++++++++++++++++++++++++++++

экзешник
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//A.cpp
#include <stdio.h>
#include <windows.h>
 
extern "C"
__declspec(dllexport) void functsia_0 () {
  printf ("это я и меня звать %s, я объявлена со спецификатором __declspec(dllexport)\n", __FUNCTION__);
}
 
extern "C" void functsia_1 () {
  printf ("это я и меня звать %s, я объявлена без спецификатора __declspec(dllexport)\n", __FUNCTION__);
}
 
extern "C"
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )  
{
    return TRUE; 
}
C++
1
 
dll-ка
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//B.cpp
#include <windows.h>
#include <stdio.h>
 
 
int main ()
 
{
      SetConsoleCP(1251);
      SetConsoleOutputCP(1251);
      HMODULE DeskriptorModula= LoadLibrary ((char*)"A.dll");
      FARPROC 
                           //++++++
                           //++++++
                           //++++++
      adres_funktsii= GetProcAddress (DeskriptorModula, "functsia_0");
                           //коммент 
      (*adres_funktsii)();
 
      adres_funktsii= GetProcAddress (DeskriptorModula, "functsia_1");
                           if(adres_funktsii)
      (*adres_funktsii)();
                           //++++++
                           //++++++
                           //++++++
 
      printf ("%x\n", adres_funktsii);      
      
      
      return (0);
 
}

Bash
1
2
это я и меня звать functsia_0, я объявлена со спецификатором __declspec(dllexport)
0
Что же, ТЕПЕРЬ можно сказать, что для первого случая компилятор заточен правильно- действительно- если в dll есть ОДНА функция, да и та не экспортируется, на кой она нужна? Поэтому на экспорт её, безо всяких __declspec(dllexport)

Для настоящего случая, компилятор мог быть и поумнее- если есть две или больше АВТОНОМНЫХ функций, то опять, на кой они нужны, как не на экспорт? Но он, видать, считает, что одна может вызываться из другой и тем самым не идти на экспорт, (поэтому на экспорт отправляет ту, которая __declspec(dllexport)) На месте компилятора я бы так и думал. Хотя анализ кода доказывает обратное. Но без претензий. Главное, что разобрались. Но вернёмся к нашим баранам.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Итак, необходимость явно указывать __declspec(dllexport) несомненна. Изначально вопрос стоял так- мы либо указываем __declspec(dllexport) (директивой компилятора -DBUILD_DLL), либо указываем- внимание, в том же самом месте __declspec(dllimport):

C++
1
2
3
extern "C"
Export //Тут будет либо __declspec(dllexport), либо __declspec(dllimport)
void foo () {}
и это, заметье, не хидер, а сырец. Минутку, это я если я не укажу компилятору директиву -DBUILD_DLL, то эта функция пойдёт на иморт? То есть я в коде dll-ки напишу функцию и укажу компилятор, что я же эту функцию буду в эту же dll-ку импортировать? Это прямо нонсенс какой-то. Все функции, у которых есть тела по определению не могут ОТКУДА-ТО импортироваться. То есть они УЖЕ ЕСТЬ в этом модуле. Экспортироваться- пожалуйста, импортироваться- нет. Вообще определять функцию как импортируемую- нелепость. Это всё равно что сказать компилятору- "Эта функция у меня есть. Но я её импортирую." Или в переводе на человеческий язык: "У меня есть яблоко, но я его куплю".
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
10.02.2014, 20:43 11
Сырцу второй раз указывать экспортность уже необязательно, достаточно того, что она объявлена в заголовке (разумеется, заголовок должен включаться в сырцы A.DLL). Там обычно и не указывают. Список экспортируемых функций выносят в A.H, который в проекте A служит списком экспортируемых функций.

Директива импорта предназначена для другого проекта B.EXE, который нашу A.DLL использует. Как использует? Подключает implib A.lib или A.a, который содержит одноимённые с экспортными функции, состоящие из простого перекидывания вызова на нужный адрес в DLL. При инициализации приложения implib загружает DLL и вынимает оттуда адреса. Тот же самый заголовочный файл A.H, который мы использовали при компиляции A.DLL, здесь работает у нас как объявление прототипов импортируемых функций. Из-за отсутствия ключа BUILD_DLL в настройках проекта B заголовок A.H подставляет директиву импорта в их описание. Благодаря тому, что мы в обоих случаях используем один и тот же заголовочный файл A.H, мы страхуемся от ошибок в объявлениях функций и необходимости вручную вести заголовок с импортируемыми функциями.
0
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
11.02.2014, 07:46  [ТС] 12
Второй абзац, честно говоря, не понял. Нельзя ли какой-нибудь маленький примерчик ПОЛЕЗНОГО применения __declspec(dllimport)? Для начала понять хотя бы, что даёт сей модификатор, а потом вернуться к ключу BUILD_DLL.
0
Jupiter
11.02.2014, 14:47
  #13

Не по теме:

Цитата Сообщение от Nick Alte Посмотреть сообщение
Потому что ленивые программисты используют один и тот же заголовок и для построения самой DLL
это не лень, это лекарство от идиосинкразии синхронизации

0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
11.02.2014, 15:12 14

Не по теме:

Цитата Сообщение от Jupiter Посмотреть сообщение
это не лень, это лекарство
Я в курсе, но начинать объяснения лучше с более простых понятий.



Цитата Сообщение от kravam Посмотреть сообщение
примерчик ПОЛЕЗНОГО применения __declspec(dllimport)? Для начала понять хотя бы, что даёт сей модификатор
Для функций он ничего принципиального не даёт, кроме разве что подсказок оптимизатору. И программисту. Впрочем, при импорте переменных из DLL он уже необходим. Само собой, этот модификатор имеет смысл только в контексте использования implib.
0
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
11.02.2014, 15:21  [ТС] 15
implib это что такое? НУ то есть я понимаю, что это импорт библиотек, но это программа или что?
0
Эксперт С++
1674 / 1046 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
11.02.2014, 21:36 16
Это библиотека импорта: та статическая библиотека, которая генерируется вместе с DLL. Она содержит в себе функции, совпадающие по именам с функциями DLL, которые просто перебрасывают вызовы в DLL. Она же содержит код, который при загрузке приложения загружает DLL и читает оттуда адреса всех функций, а при завершении приложения выгружает DLL.
Всё это можно проделывать и вручную, без implib, но с ней, понятное дело, удобнее.
1
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
16.02.2014, 15:05 17
Лучший ответ Сообщение было отмечено kravam как решение

Решение

Цитата Сообщение от kravam Посмотреть сообщение
Второй абзац, честно говоря, не понял. Нельзя ли какой-нибудь маленький примерчик ПОЛЕЗНОГО применения __declspec(dllimport)?
Тут подробно написано.

Цитата Сообщение от kravam Посмотреть сообщение
а потом вернуться к ключу BUILD_DLL.
Да BUILD_DLL не ключ, а макрос. -D - это ключ. Вот пример:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifdef LIBNAME_USING_WIN32
#   ifdef LIBNAME_USING_CXX_GCC
#       define LIBNAME_EXPORT_DIRECTIVE __attribute__ ((__dllexport__))
#       define LIBNAME_IMPORT_DIRECTIVE __attribute__ ((__dllimport__))
#   elif defined(LIBNAME_USING_CXX_MSVC)
#       define LIBNAME_EXPORT_DIRECTIVE __declspec(dllexport)
#       define LIBNAME_IMPORT_DIRECTIVE __declspec(dllimport)
#   endif
#else
#   ifdef LIBNAME_USING_GCC4
#       define LIBNAME_EXPORT_DIRECTIVE __attribute__ ((visibility("default")))
#       define LIBNAME_IMPORT_DIRECTIVE __attribute__ ((visibility("default")))
#   elif defined(LIBNAME_USING_CXX_GCC)
#       define LIBNAME_EXPORT_DIRECTIVE
#       define LIBNAME_IMPORT_DIRECTIVE
#   endif
#endif
 
#ifdef LIBNAME_BUILD_DLL
#   define LIBNAME_API  LIBNAME_EXPORT_DIRECTIVE
#else
#   define LIBNAME_API  LIBNAME_IMPORT_DIRECTIVE
#endif
Определяя его, мы включаем экспорт функций помеченных LIBNAME_API (где LIBNAME - имя нашей библиотеки). В случае же когда приложение начинает использовать заголовочные файлы нашей библиотеки - этот макрос не определяется и используется по умолчанию импорт.
1
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
17.02.2014, 20:40  [ТС] 18
На счёт __declspec(dllimport)- небольшой тык скыть эксперимент с отладчиком, показал, что всё так.

Не по теме:

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

То есть в

1) связке экзешник + системная dll-ка модификатор __declspec(dllimport) должен отсутсвовать. Где же он есть? Ну, наверное, в

2)связке экзешник+ dll-ка, поставляемая вместе с ним. ТО есть написан экзешник, к нему паровозом идут dll-ка(и), предполагается что эта связка неизменна, поэтому неизменен и адрес функции, вызываемой из этого экзешника, не фиг зря тратить загрузчицкое время на правку адреса.

Это всё понятно. Но мы-то говорим об __declspec(dllimport) в исходинке dll-ки! Получается связка такая:

3) dll-ка с __declspec(dllimport)+ dll-ка (источник функции)

Ну я таких конструкций навороченных с двумя dll-ками не писал ещё, ну ладно, всё это вполне может быть.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=


А теперь, когда мы так успешно выяснили, зачем всё это нужно по отдельности, возвращаемся к нашему вопросу- на фига такой вот монстр?

C++
1
2
3
4
5
6
7
#ifdef BUILD_DLL
   // the dll exports
   #define EXPORT __declspec(dllexport)
#else
   // the exe imports
   #define EXPORT __declspec(dllimport)
#endif
Мне ситуация видится просто: пишем dll-ку, в ней сколько-то импортируемых функций из других dll-ок, сколоько-то экспортируемых и сколько-то ни тех ни других, для внутреннего, тык скыть, потребления. Так вот, прототип каждой из импортируемых функций мы объявляем так:

C++
1
2
3
4
extern "C" __declspec(dllimport) void functsia_imp_0 ();
extern "C" __declspec(dllimport) void functsia_imp_1 ();
extern "C" __declspec(dllimport) void functsia_imp_2 ();
...
А экспортируемые пишем так:
C++
1
2
3
4
extern "C" __declspec(dllexport) void functsia_exp_0 () {};
extern "C" __declspec(dllexport) void functsia_exp_1 () {};
extern "C" __declspec(dllexport) void functsia_exp_2 () {};
extern "C" __declspec(dllexport) void functsia_exp_3 () {};
Или даже так, если уж нам неймётся с макросами:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#define EXPORT __declspec(dllexport)
#define IMPORT __declspec(dllimport)
////...
 
extern "C" IMPORT void functsia_imp_0 ();
extern "C" IMPORT void functsia_imp_1 ();
extern "C" IMPORT void functsia_imp_2 ();
 
//...
extern "C" EXPORT void functsia_exp_0 () {};
extern "C" EXPORT void functsia_exp_1 () {};
extern "C" EXPORT void functsia_exp_2 () {};
extern "C" EXPORT void functsia_exp_3 () {};
Но где тут может быть полезен вышеприведённый блок макроопределений? Ума не приложу. Уславливаемся, что в DLL-ке у нас определены, например 4 экмпортируемые функции и используются 3 импортируемые. (наверное, последний абзац последнего ответа был как раз про это, но уж больно он громоздкий, извините.)
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
17.02.2014, 23:57 19
Цитата Сообщение от kravam Посмотреть сообщение
А теперь, когда мы так успешно выяснили, зачем всё это нужно по отдельности, возвращаемся к нашему вопросу- на фига такой вот монстр?
Ссылочку на мсдн мою смотрел?
0
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
18.02.2014, 10:17  [ТС] 20
DrOffset, опять вы думаете обо мне плохо. Там именно то и написано, зачем нужен модификатор
C++
1
__declspec(dllimport)
Но зачем нужно вот это вот:
C++
1
2
3
4
5
6
7
#ifdef BUILD_DLL
   // the dll exports
   #define EXPORT __declspec(dllexport)
#else
   // the exe imports
   #define EXPORT __declspec(dllimport)
#endif
там об этом ни слова.
0
18.02.2014, 10:17
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
18.02.2014, 10:17
Помогаю со студенческими работами здесь

Нужен glaux для mingw
glaux for mingw в гугле ничё не даёт ++++++++++++++++++++++++++++++++++ Теперь вот ещё, у...

Подлючение библиотеки sfml 2.0 sjlj к дефолтному компилятору mingw sjlj code::blocks 13.12 x32
Напишите, что неверно. 1. Новый пустой проект, потому что в code::blocks 13.12 не функционирует...

Для чего нужен Seed() и для чего его override?
Привет, ребята. Прочитал много разных статей про Seed(). И так и не могу понять зачем он нужен на...

MinGW - скомпилированная программа требует наличия libstdc++-6.dll и libgcc_s_seh-1.dll
Добрый день. При компиляции с помощью Visual Studio программа не требует никаких dll, можно ли в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru