Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 19, средняя оценка - 4.79
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
#1

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

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

Не, реально, зачем он?

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

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

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

Так зачем же нужен параметр -DBUILD_DLL? Спасибо, кто откликнется.
Вложения
Тип файла: rar rez.rar (46.3 Кб, 9 просмотров)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.02.2014, 17:22
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW) (C++):

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

Параметр rhs, что это и для чего (конструктор-копировщик) - C++
Вот код: #include <iostream> class jurnal { public: jurnal() { }

Для чего используется DLL? - C++
исходники кода написанного на C++ хранятся в DLL верно?? Для чего используется DLL?? Добавлено через 2 минуты вернее не исходники а...

Может ли MinGW x64 компилить приложения x32? Или нужно для этого отдельно ещё MinGW х32 качать? - C++
Просто решил поиграться с CodeLite.

Для чего используются DLL-библиотеки? - C++
Для чего используются DLL-библиотеки Неужели заголовочного файла мало.

Создание программы, которая будет обращаться к компилятору - C++
Хочу написать программу, которая будет обращаться к компилятору, в нём же она будет проверять работоспособность кода, который напишут в...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
07.02.2014, 19:04  [ТС] #2
Блин, тихий ужас. Оказывается, -DBUILD_DLL это -D BUILD_DLL! То есть то же самое, что
C++
1
#define BUILD_DLL
Почему бы просто не определять в исходнике макрос? Один кто-то бездумно скопипастил и пошёл-поехал гулять по инету вариант с -D
Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
07.02.2014, 20:42 #3
Цитата Сообщение от kravam Посмотреть сообщение
Почему бы просто не определять в исходнике макрос?
Потому что ленивые программисты используют один и тот же заголовок и для построения самой DLL, где содержащиеся в ней функции описаны как экспортируемые (со спецификатором __declspec(dllexport) в случае Visual Studio), и для использующих ту DLL программ, где их описывают уже как импортируемые, с модификатором __declspec(dllimport). Переключение как раз осуществляется проверкой макроса BUILD_DLL. Менять исходный код, добавляя-убирая этот макрос в зависимости от того, что мы собираем, неудобно. А удобно привязывать этот макрос к проекту, добавляя его в параметры компилятора, а лучше в свойства проекта в IDE, откуда он всё равно попадёт в командную строку.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
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). То есть не пойму, зачем этот модификатор нужен.
Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
07.02.2014, 21:45 #5
А что делает файл *.a? Он всего лишь загружает DLL и вынимает из неё адреса функций, на которые и перекидывает вызовы. Это же можно делать и вручную, через LoadLibrary / GetProcAddress, без статических библиотек.
А чтобы вынуть из DLL адрес функции по её имени, необходимо, чтобы в DLL содержалась таблица имён и адресов экспортируемых функций. А чтобы компилятор знал, что в эту таблицу запихать, и нужна пометка __declspec(dllexport). Этот модификатор - единственная вещь, которая действительно необходима для экспорта функций из DLL, а остальные перечисленные вещи нужны только для согласования и/или удобства.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
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;
}
Об этом речь?
Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
08.02.2014, 09:15 #7
Да, об этом. Там в примере не хватает ещё вызова tstfunc для полноты картины.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
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)
. Извините...
Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
10.02.2014, 18:19 #9
Умный, хорошо выдрессированный компилятор, который в определённых случаях сам умеет догадываться, какие функции надо экспортировать - это хорошо и полезно. Но не каждый компилятор так хорошо выдрессирован, а в ряде случаев (обычно, при создании реальных DLL, в которых не каждую функцию надо экспортировать) даже с дрессированным компилятором лучше всё же вручную указывать, кто идёт на экспорт.
Для чистоты эксперимента - а что будет, если в этом примере добавить ещё одну функцию, уже со спецификатором экспорта? Будут ли экспортироваться обе?
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
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-ку импортировать? Это прямо нонсенс какой-то. Все функции, у которых есть тела по определению не могут ОТКУДА-ТО импортироваться. То есть они УЖЕ ЕСТЬ в этом модуле. Экспортироваться- пожалуйста, импортироваться- нет. Вообще определять функцию как импортируемую- нелепость. Это всё равно что сказать компилятору- "Эта функция у меня есть. Но я её импортирую." Или в переводе на человеческий язык: "У меня есть яблоко, но я его куплю".
Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
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, мы страхуемся от ошибок в объявлениях функций и необходимости вручную вести заголовок с импортируемыми функциями.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
11.02.2014, 07:46  [ТС] #12
Второй абзац, честно говоря, не понял. Нельзя ли какой-нибудь маленький примерчик ПОЛЕЗНОГО применения __declspec(dllimport)? Для начала понять хотя бы, что даёт сей модификатор, а потом вернуться к ключу BUILD_DLL.
Jupiter
11.02.2014, 14:47
  #13

Не по теме:

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

Nick Alte
Эксперт С++
1636 / 1008 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
11.02.2014, 15:12 #14

Не по теме:

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



Цитата Сообщение от kravam Посмотреть сообщение
примерчик ПОЛЕЗНОГО применения __declspec(dllimport)? Для начала понять хотя бы, что даёт сей модификатор
Для функций он ничего принципиального не даёт, кроме разве что подсказок оптимизатору. И программисту. Впрочем, при импорте переменных из DLL он уже необходим. Само собой, этот модификатор имеет смысл только в контексте использования implib.
kravam
быдлокодер
1694 / 881 / 44
Регистрация: 04.06.2008
Сообщений: 5,441
11.02.2014, 15:21  [ТС] #15
implib это что такое? НУ то есть я понимаю, что это импорт библиотек, но это программа или что?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.02.2014, 15:21
Привет! Вот еще темы с ответами:

Для чего нужен с++? - C++
Я школьник, мне 16 лет, учусь в 10 классе, планирую поступить на факультет защиты информационных технологий. Сейчас начал изучать с++, не...

Для чего нужен Sizeof - C++
Вот строка memcpy(pMatrPr, pMatr, sizeof(double) * n * m); Это копирование исходной матрицы в преобразованную А что такое...

Для чего нужен define? - C++
Для чего нужен оператор define? например код #define N 20 /* сколько первых чисел посчитать */ void main(){ ...

Для чего нужен break? - C++
для чего нужен break?


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
11.02.2014, 15:21
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru