Как скомпилировать gcc dll

Обновлено: 21.11.2024

Получите полный доступ к C++ Cookbook и более чем 60 000 других наименований с бесплатной 10-дневной пробной версией O'Reilly.

Есть также прямые онлайн-мероприятия, интерактивный контент, материалы для подготовки к сертификации и многое другое.

Проблема

Вы хотите использовать инструменты командной строки для создания динамической библиотеки из набора исходных файлов C++, таких как файлы, перечисленные в примере 1-2.

Решение

Выполните следующие действия:

Используйте свой компилятор для компиляции исходных файлов в объектные файлы. Если вы используете Windows, используйте параметр -D для определения любых макросов, необходимых для обеспечения экспорта символов вашей динамической библиотеки. Например, чтобы построить динамическую библиотеку в примере 1-2, вам нужно определить макрос GEORGERINGO_DLL. Если вы создаете стороннюю библиотеку, в инструкциях по установке должно быть указано, какие макросы нужно определить.

Используйте компоновщик для создания динамической библиотеки из объектных файлов, созданных на шаге 1.

Если ваша динамическая библиотека зависит от других библиотек, вам нужно указать компилятору, где искать заголовки библиотек, и сообщить компоновщику имена других библиотек и где их найти. Это подробно обсуждается в рецепте 1.5.

Основные команды для выполнения первого шага приведены в таблице 1-8; вам нужно будет соответствующим образом изменить имена входных и выходных файлов. Команды для выполнения второго шага приведены в таблице 1-11. Если вы используете набор инструментов, который поставляется со статическими и динамическими вариантами библиотек времени выполнения, укажите компилятору и компоновщику использовать динамически связанную среду выполнения, как описано в рецепте 1.23.

Таблица 1-11. Команды для создания динамической библиотеки libgeorgeringo.so, libgeorgeringo.dll или libgeorgeringo.dylib

g++ -shared -fPIC -o libgeorgeringo.so george.o ringo.o georgeringo.o

g++ -dynamiclib -fPIC -o libgeorgingo.dylib george.o ringo.o georgeringo.o

g++ -shared -o libgeorgeringo.dll -Wl,—out-implib,libgeorgeringo.dll.a-W1,—export-all-symbols -Wl,—enable-auto-image-base george.o ринго.о джорджинго.о

g++ -shared -o libgeorgeringo.dll -Wl,—out-implib,libgeorgeringo.a -W1,—export-all-symbols-Wl,—enable-auto-image-base george.o ringo.o georgeringo.o

link -nologo -dll -out:libgeorgeringo.dll -implib:libgeorgeringo.lib george.obj ringo.obj georgeringo.obj

xilink -nologo -dll -out:libgeorgeringo.dll -implib:libgeorgeringo.lib george.obj ringo.obj georgeringo.obj

g++ -shared -fPIC -lrt -o libgeorgingo.so george.o ringo.o georgeringo.o georgeringo.obj

mwld -shared -export dllexport -runtime dm -o libgeorgeringo.dll -implib libgeorgeringo.lib george.obj ringo.obj georgeringo.obj

Metrowerks (Mac OS X)

mwld -shared -export pragma -o libgeorgingo.dylib george.o ringo.o georgeringo.o

CodeWarrior 10.0 (Mac OS X) [4]

Обратитесь к документации Metrowerks.

bcc32 -q -WD -WR -elibgeorgeringo.dll george.obj ringo.obj georgeringo.objimplib -c libgeorgeringo.lib libgeorgeringo.dll

dmc -WD -L/implib:libgeorgeringo.lib -o libgeorgeringo.dll george.obj ringo.obj georgeringo.obj user32.lib kernel32.lib

[4] CodeWarrior 10.0 для Mac OS X будет предоставлять динамические варианты своих библиотек поддержки среды выполнения; их следует использовать при сборке libgeorgeringo.dylib. (См. рецепт 1.23.)

По состоянию на сентябрь 2005 г. набор инструментов Comeau не поддерживает создание динамических библиотек в Unix или Windows. Однако Comeau Computing в настоящее время работает над поддержкой динамических библиотек и ожидает, что она будет реализована для некоторых платформ Unix, включая Linux, к концу 2005 года.

Например, чтобы скомпилировать исходные файлы из примеров 1-2 в объектные файлы с помощью компилятора Borland, предполагая, что каталог, содержащий инструменты Borland, находится в вашем PATH , перейдите в каталог georgeringo и введите следующие команды:

Опция компилятора -WR используется здесь для указания динамического варианта библиотеки времени выполнения. Эти три команды создадут объектные файлы george.obj, ringo.obj и georgeringo.obj. Далее введите команду:

Это создаст динамическую библиотеку libgeorgeringo.dll. Наконец, введите команду:

При этом будет создана библиотека импорта libgeorgeringo.lib.

Обсуждение

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

Видимость символа

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

В большинстве наборов инструментов Windows, чтобы символ, определенный в динамической библиотеке, был доступен для кода, использующего динамическую библиотеку, он должен быть явно экспортирован при построении динамической библиотеки и импортируется при создании исполняемой или динамической библиотеки, использующей динамическую библиотеку. Некоторые наборы инструментов Unix также предлагают эту гибкость; это верно для последних версий GCC на нескольких платформах, для Metrowerks для Mac OS X и для Intel для Linux. Однако в некоторых случаях нет другого выхода, кроме как сделать видимыми все символы.

Передача библиотек компоновщику

В Unix динамическая библиотека может быть указана в качестве входных данных для компоновщика при компоновке кода, использующего динамическую библиотеку. В Windows, за исключением случаев использования GCC, динамические библиотеки не указываются напрямую в качестве входных данных для компоновщика; вместо этого используется библиотека импорта или файл определения модуля.

Импорт библиотек и файлов определения модулей

Библиотеки импорта, грубо говоря, представляют собой статические библиотеки, содержащие информацию, необходимую для вызова функций в DLL во время выполнения. Необязательно знать, как они работают, достаточно знать, как их создавать и использовать. Большинство компоновщиков автоматически создают библиотеки импорта при сборке библиотеки DLL, но в некоторых случаях может потребоваться использование отдельного инструмента под названием библиотека импорта. В Таблице 1-11 я использовал библиотеку импорта Borland implib.exe, чтобы избежать особого синтаксиса командной строки, необходимого компоновщику Borland ilink32.exe.

Файл определения модуля, или файл .def, представляет собой текстовый файл, описывающий функции и данные, экспортируемые библиотекой DLL. Файл .def можно написать вручную или автоматически сгенерировать с помощью инструмента. Пример файла .def для библиотеки libgeorgeringo.dll показан в примере 1-5.

Пример 1-5. Файл определения модуля для libgeorgeringo.dll

Экспорт символов из DLL

Существует два стандартных метода экспорта символов из Windows DLL:

Используйте атрибут _ _declspec(dllexport) в заголовках DLL и создайте библиотеку импорта для использования при компоновке кода, использующего вашу DLL.

Атрибут _ _declspec(dllexport) должен быть вставлен в начале объявлений экспортируемых функций и данных, после любых описателей связи и непосредственно после ключевого слова class или struct для экспортируемых классов. Это показано в примере 1-6. Обратите внимание, что _ _declspec(dllexport) не является частью языка C++; это расширение языка, реализованное большинством компиляторов Windows.

Создайте файл .def с описанием функций и данных, экспортируемых вашей динамической библиотекой.

Пример 1-6. Использование атрибута _ _declspec(dllexport)

Использование файла .def имеет определенные преимущества; например, он может разрешить доступ к функциям в DLL по номеру, а не по имени, что уменьшает размер DLL. Это также устраняет необходимость в запутанных директивах препроцессора, таких как в заголовке georgeringo.hpp из примера 1-2. Однако у него есть серьезные недостатки. Например, файл .def нельзя использовать для экспорта классов. Кроме того, бывает сложно не забыть обновить файл .def при добавлении, удалении или изменении функций в DLL. Поэтому я рекомендую вам всегда использовать _ _declspec(dllexport) . Чтобы узнать полный синтаксис файлов .def, а также способы их использования, обратитесь к документации по вашему набору инструментов.

Импорт символов из DLL

Точно так же, как существует два способа экспорта символов из DLL, существует два способа импорта символов:

В заголовках, включенных в исходный код, использующий вашу библиотеку DLL, используйте атрибут __declspec(dllimport) и передайте библиотеку импорта компоновщику при компоновке кода, использующего вашу библиотеку DLL.

Укажите файл .def при компоновке кода, который зависит от вашей DLL.

Как и при экспорте символов, я рекомендую использовать в исходном коде атрибут _ _decl-spec(dllimport) вместо файлов .def. Атрибут _ _decl-spec(dllimport) используется точно так же, как и атрибут _ _declspec(dllexport), рассмотренный ранее. Как и _ _declspec(dllexport), _ _declspec(dllimport) не является частью языка C++, а является расширением, реализуемым большинством компиляторов Windows.

Если вы решите использовать _ _declspec(dllexport) и _ _declspec(dllimport) , обязательно используйте _ _declspec(dllexport) при создании DLL и _ _declspec(dllimport) при компиляции кода, использующего вашу DLL. Один из подходов заключается в использовании двух наборов заголовков: один для создания вашей библиотеки DLL, а другой — для компиляции кода, использующего вашу библиотеку DLL. Однако это неудовлетворительно, так как сложно поддерживать две отдельные версии одних и тех же заголовков.

Вместо этого обычный подход заключается в определении макроса, который расширяется до _ _declspec(dllexport) при построении библиотеки DLL и до _ _declspec(dllimport) в противном случае. В примере 1-2 для этой цели я использовал макрос GEORGERINGO_DECL. В Windows GEORGERINGO_DECL заменяется на _ _declspec(dllexport), если определен макрос GEORGERING_SOURCE, и на _ _declspec(dllimport) в противном случае. Указав GEORGERING_SOURCE при сборке библиотеки DLL libgeorgeringo.dll, но не при компиляции кода, использующего libgeorgeringo.dll, вы получите желаемый результат.

Создание DLL с помощью GCC

Переносы Cygwin и MinGW GCC, описанные в рецепте 1.1, обрабатывают библиотеки DLL не так, как другие наборы инструментов Windows. Когда вы создаете DLL с помощью GCC, все функции, классы и данные экспортируются по умолчанию. Это поведение можно изменить, передав компоновщику параметр —no-export-all-symbols, используя атрибут _ _declspec-(dllexport) в исходных файлах или используя .def файл. В каждом из этих трех случаев, если вы не используете параметр —export-all-symbols, чтобы заставить компоновщик экспортировать все символы, единственными экспортируемыми функциями, классами и данными будут отмеченные _ _decl- spec(dllexport) или перечислены в файле .def.

Поэтому набор инструментов GCC можно использовать для создания библиотек DLL двумя способами: как обычный набор инструментов Windows, явно экспортируя символы с помощью __declspec , или как набор инструментов Unix, экспортируя все символы автоматически. [5] Последний метод я использовал в примере 1-2 и в таблице 1-11. Если вы выберете этот метод, вам следует рассмотреть возможность использования параметра —export-all-symbols в качестве меры безопасности на тот случай, если вы включите заголовки, содержащие __declspec(dllexport) .

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

В Cygwin библиотека импорта для DLL xxx.dll обычно называется xxx.dll.a, а в MinGW она обычно называется xxx. а. Это всего лишь вопрос соглашения.

опция -fvisibility в GCC 4.0

Последние версии GCC на нескольких платформах, включая Linux и Mac OS X, дают программистам возможность точного управления экспортируемыми символами в динамической библиотеке: параметр командной строки -fvisibility может быть используется для установки видимости символов по умолчанию в динамической библиотеке, а специальный синтаксис атрибута, аналогичный __declspec(dllexport) в Windows, может использоваться в исходном коде для изменения видимости символов в каждом конкретном случае. Параметр -fvisibility имеет несколько возможных значений, но два интересных случая: по умолчанию и скрытый . Грубо говоря, видимость по умолчанию означает, что символ доступен для кода в других модулях; скрытая видимость означает, что это не так. Чтобы включить выборочный экспорт символов, укажите -fvisibility=hidden в командной строке и используйте атрибут видимости, чтобы пометить выбранные символы как видимые, как показано в примере 1-7.

Пример 1-7. Использование атрибута видимости с параметром командной строки -fvisibility=hidden

В примере 1-7 атрибут _ _attribute_ _((visibility("default"))) играет ту же роль, что и _ _declspec(dllexport) в коде Windows.

При использовании атрибута видимости возникают некоторые из тех же проблем, что и при использовании _ _declspec(dllexport) и _ _declspec(dllimport) , поскольку вы хотите, чтобы атрибут присутствовал при создании общей библиотеки, но не при компиляции кода, использующего общую библиотеку, и вы хотите, чтобы она была полностью скрыта на платформах, которые ее не поддерживают. Как и в случае с __declspec(dllexport) и __declspec(dllimport), эту проблему можно решить с помощью препроцессора. Например, вы можете изменить заголовок georgeringo.hpp из примера 1-2, чтобы использовать атрибут видимости следующим образом:

Чтобы это работало, необходимо определить макрос HAS_GCC_VISIBILITY при сборке на системах, поддерживающих параметр -fvisibility.

Последние версии компилятора Intel для Linux также поддерживают параметр -fvisibility.

Отображение символов с помощью Metrowerks для Mac OS X

Параметры командной строки

Давайте кратко рассмотрим параметры, используемые в Таблице 1-11. Каждая командная строка указывает:

Имя входных файлов: george.obj, ringo.obj и georgeringo.obj

Имя создаваемой динамической библиотеки

В Windows имя библиотеки импорта

Кроме того, компоновщику требуется параметр, сообщающий ему о создании динамической библиотеки, а не исполняемого файла. Большинство компоновщиков используют параметр -shared, но Visual C++ и Intel для Windows используют -dll, Borland и Digital Mars используют -WD, а GGC для Mac OS X используется -dynamiclib.

Некоторые параметры в Таблице 1-11 помогают более эффективно использовать динамические библиотеки во время выполнения. Например, некоторым компоновщикам Unix следует указать генерировать независимый от позиции код с помощью параметра -fPIC (GCC и Intel для Linux). Этот параметр повышает вероятность того, что несколько процессов смогут совместно использовать одну копию кода динамической библиотеки; в некоторых системах отсутствие указания этой опции может вызвать ошибку компоновщика. Точно так же в Windows компоновщик GCC с опцией —enable-auto-image-base снижает вероятность того, что операционная система попытается загрузить две динамические библиотеки в одно и то же место; использование этого параметра помогает ускорить загрузку DLL.

Вы можете передать параметры компоновщику GCC через компилятор, используя параметр компилятора -Wl, в g++. (Буква, следующая за W, — строчная l.)

Большинство оставшихся параметров используются для указания вариантов библиотеки времени выполнения, как описано в рецепте 1.23.

Мне было интересно, как мне скомпилировать DLL с помощью gcc? Ну, я использую порт gcc для Windows. Как только я скомпилирую .dll, содержащую мои классы и функции, как мне связать ее с моей программой? Как использовать классы и функции в DLL? Может ли функция вызова DLL из связывающейся с ней программы? Если я сделаю класс < . >объект; в DLL, то когда DLL загружается программой, будет ли объект доступен для программы? Заранее спасибо, мне действительно нужно знать, как работать с DLL в C++, прежде чем я смогу продолжить этот проект.

"Можете ли вы добавить более подробную информацию о том, почему вы хотите, чтобы DLL вызывала функции в основной программе?"

Я думал, что схема объясняет это. программа, использующая DLL, передает фрагмент кода в DLL, которая анализирует код, и если в указанном коде обнаружены вызовы функций, то вызываются соответствующие функции в DLL. например, если бы я передал "a = sqrt(100)", то функция синтаксического анализатора DLL нашла бы вызов функции sqrt(), а внутри DLL была бы соответствующая функция sqrt(), которая вычисляла бы квадратный корень из аргумента передается ему, а затем он берет возвращаемое значение из этой функции и помещает его в переменную a. точно так же, как и любая другая программа, но если соответствующий обработчик для функции sqrt() не найден в DLL (был бы список изначально поддерживаемых функций), тогда она вызовет аналогичную функцию, которая будет находиться в программе с использованием DLL, чтобы увидеть, есть ли какие-либо пользовательские функции с таким именем.

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

Эта статья покажет вам основы создания библиотек DLL с помощью MinGW, а затем связывания с ними в ваших приложениях. Создаваемый здесь код можно будет использовать и в MSVC, поэтому вы или ваши конечные пользователи сможете использовать несколько компиляторов.

Основы DLL

DLL — это тип общей библиотеки, используемой в Microsoft Windows и OS/2, содержащей функции, которые можно повторно использовать в любом приложении, которое хочет их использовать (на самом деле библиотеки DLL могут содержать гораздо больше, чем просто функции, но эта статья о создании библиотек функций). Если у вас есть библиотека foo.dll, которая содержит функцию DoWork() , DLL должна экспортировать эту функцию, чтобы ее можно было использовать в приложениях. Аналогично, если приложение bar.exe хочет использовать функцию DoWork() , он должен импортировать эту функцию из foo.dll (также можно написать код, который загружает DLL, как только приложение запустилось, вызывает функции из этой DLL, а затем выгружает DLL, когда она больше не нужна — так обычно работают системы плагинов/аддонов). Экспорт и импорт функций довольно прост и требует небольшого количества кода, сопровождаемого правильной командной строкой компоновщика.

Негибкий и неэлегантный способ

Сначала мы создадим DLL, экспортирующую только очень простую функцию, которая складывает два целых числа и возвращает результат. Обратите внимание на атрибут « __declspec(dllexport) » в приведенном ниже коде — это ключ к экспорту функций из DLL, и каждая функция, которую вы хотите экспортировать из DLL, должна быть помечена этим атрибутом (любые внутренние функции, которые не составляют часть вашего API следует оставить как есть). Также обратите внимание на « __cdecl » перед именем функции, который объявляет соглашение о вызовах для функции (см. статью в Википедии о соглашениях о вызовах x86, если вы с ними не знакомы). Соглашение о вызовах по умолчанию для функций C в MinGW — cdecl, но рекомендуется всегда явно указывать соглашение о вызовах на случай, если какой-то другой компилятор использует другое значение по умолчанию — если это произойдет, ваше приложение, скорее всего, будет работать некорректно. или сбой в результате вызова одной из этих функций. Обратите внимание, что этот первый пример намеренно негибкий и неэлегантный, чтобы точно показать, что происходит за кулисами. В следующем разделе мы будем скрывать эти детали с помощью определений препроцессора.

Чтобы скомпилировать это, просто необходимо передать опцию «-shared» в обычную командную строку компоновщика:

Никаких ошибок не должно быть сообщено, и теперь у вас должна быть пригодная для использования библиотека DLL. В этом примере я разделил этапы компиляции и компоновки, хотя для такого небольшого проекта это можно было бы сделать просто с помощью « gcc -o add_basic.dll -s -shared add_basic.c -Wl, --subsystem,windows ». «-Wl,--subsystem,windows» на самом деле не является необходимым, но просто общепринято, что библиотеки DLL имеют подсистему графического интерфейса Windows, указанную в их PE-заголовке. Также обратите внимание, что переключатель «-s» используется для удаления символов из DLL — вы, вероятно, захотите сделать это только для выпускных сборок.

Теперь вы можете создать приложение, использующее эту библиотеку DLL. Все, что необходимо для использования функции Add(a, b), — это объявить функцию с атрибутом «__declspec(dllimport)» перед ее использованием (обратите внимание, что это dllimport для клиентского приложения, в отличие от dllexport, используемого в нашей DLL).

Теперь вы можете скомпилировать и связать это приложение, просто сославшись на нашу DLL в командной строке компоновщика.

Параметр «-L» указывает дополнительную папку, в которой компоновщик должен искать библиотеку DLL. В этом случае мы хотим, чтобы компоновщик выполнял поиск в текущем каталоге, поэтому мы можем просто использовать точку, но для настоящей DLL, которую мы развернули в системе, мы будем использовать полный путь к ее каталогу. Параметр «-l» указывает DLL, из которой мы хотим импортировать функции — это должно быть имя файла DLL без расширения файла. Опять же, для такого небольшого проекта приложение можно было бы скомпилировать и слинковать, выполнив команду gcc -o addtest_basic.exe -s addtest_basic.c -L. -ladd_basic ”

Гибкий и элегантный способ

Обратите внимание, что мы определили «ADDAPI» как «__declspec(dllexport)», если определено «ADD_EXPORTS», или «__declspec(dllimport)» в противном случае. Именно это позволяет нам использовать один и тот же заголовок для приложений и DLL. Также обратите внимание, что мы определили «ADDCALL» как «__cdecl», что позволяет легко переопределить соглашение о вызовах, которое мы используем для нашего API. Теперь в любом месте нашего кода, где нам нужно экспортировать функцию из нашей DLL, мы указываем «ADDAPI» вместо атрибута «__declspec(dllexport)» и «ADDCALL» вместо указания, т. е. «__cdecl». Принято называть эти определения препроцессора «[имя библиотеки]_EXPORTS», «[имя библиотеки]API» и «[имя библиотеки]». ЗВОНИТЕ», поэтому рекомендуется придерживаться этого правила, чтобы ваш код был удобочитаемым для вас и других.

Помните, что этот код нельзя переносить, так как в нем используются атрибуты Microsoft. Здесь это нормально, потому что это руководство по созданию Windows DLL, но если вам нужна межплатформенная совместимость, то ADDAPI и ADDCALL можно определить условно. Обычно это делается так:

Код для DLL теперь может включать только что созданный заголовок, и единственное изменение, которое нам нужно сделать, это включить «ADDCALL» перед именем функции (здесь необходимо указать соглашение о вызовах, чтобы предотвратить возможные конфликты между объявление и определение функции).

Чтобы скомпилировать эту DLL, необходимо определить «ADD_EXPORTS» при компиляции объектного кода, чтобы обеспечить правильное определение «ADDAPI» в заголовке.Проще всего это сделать, передав «-D ADD_EXPORTS» в командной строке:

Для компиляции приложения не требуется никаких изменений в используемой нами командной строке:

Этот процесс сборки используется во многих реальных библиотеках. Если неясно, что происходит с определениями препроцессора, передайте «-save-temps» в командной строке gcc на этапе компиляции и просмотрите файлы с расширением «.i» — вы заметите, что в конечном итоге сгенерированный код как в элегантных, так и в неэлегантных примерах почти одинаково.

Экспорт и импорт переменных

В дополнение к функциям также можно экспортировать и импортировать переменные. Эти переменные должны быть объявлены как «extern __declspec(dllexport)» или «extern __declspec(dllimport)», в зависимости от того, строим ли мы DLL или клиентское приложение с использованием этих переменных. Как и в случае с функциями, мы можем использовать определения нашего препроцессора и просто выделить переменные «extern ADDAPI». Для переменных не следует указывать соглашение о вызовах.

Здесь мы добавили две экспортированные переменные в наш заголовочный файл, foo и bar.

Код библиотеки DLL теперь включает присвоение значений нашим экспортируемым переменным:

Приложение было изменено таким образом, что теперь оно добавляет импортированные переменные foo и bar вместе, выводя результат:

Если вы измените значения foo и bar, перекомпилируете DLL и снова запустите приложение, вы увидите, что переменные действительно импортированы из DLL.

Импорт библиотек

Хотя обычно можно связать DLL, просто имея DLL в вашей системе и добавив некоторые параметры командной строки компоновщика, это не всегда желательно. В этом случае вместо этого следует использовать библиотеку импорта. Библиотека импорта не содержит кода, но содержит всю необходимую информацию, необходимую вашему приложению для поиска функций, экспортируемых DLL. При создании библиотек для использования третьими лицами я бы рекомендовал всегда создавать библиотеку импорта и распространять ее вместе с вашими файлами DLL и заголовочными файлами, поскольку вашим пользователям может потребоваться создавать свои приложения с использованием библиотек импорта.< /p>

Если вы изменили имена функций, которые экспортирует DLL (см. «Предупреждение об экспорте функций stdcall» ниже), библиотека импорта — единственный вариант. Это относится к API Windows, где вы должны связать свои приложения с помощью библиотек импорта, т. е. передать «-lgdi32 -luser32» и т. д. в командной строке компоновщика при компиляции программ с графическим интерфейсом Windows.

Создание и использование библиотеки импорта

Если вы не изменили ни одно из имен функций, экспортируемых DLL, то создание библиотеки импорта — это просто случай передачи дополнительного параметра «-Wl,--out-implib,lib[имя библиотеки ].a ” в командную строку компоновщика.

Принято использовать имя библиотеки в имени библиотеки импорта. Обязательно, чтобы имя файла начиналось с «lib» и заканчивалось расширением «.a», чтобы его можно было использовать (на самом деле есть несколько поддерживаемых соглашений об именах, которые вы можете использовать, которые описаны в руководстве, но все остальное не подходит). не работает).

Сборка приложения такая же, как и раньше, но вместо того, чтобы передавать каталог, содержащий DLL, компоновщику, вместо этого вы передаете каталог, содержащий библиотеку импорта (на самом деле мы все еще делаем все из текущего каталога, но это, вероятно, не было бы случаем для реального приложения). Чтобы связать DLL, вы передаете имя библиотеки импорта без префикса lib и расширения файла.

Обычно библиотеки DLL помещаются в папку с именем «bin», а библиотеки импорта — в папку с именем «lib», например, «C:\Program Files\Add\bin\» и «C:\Program Files\Add\». библиотека\ ".

Предупреждение об экспорте функций стандартного вызова

Одна вещь, о которой вы должны помнить при объявлении функций «__stdcall», заключается в том, что MinGW и MSVC экспортируют свои функции с немного разными именами (декорациями функций). Для нашего примера с добавлением выше MSVC экспортирует функцию «Add(int a, int b)» с именем «_Add@8» (подчеркивание, имя функции, знак at, размер аргументов в байтах), тогда как MinGW экспортирует ее. как «Add@8» (то же, что и MSVC, но без подчеркивания). Поэтому, если вы хотите иметь бинарную совместимость между сборками MinGW и MSVC вашей DLL, необходимо изменить имена экспортируемых функций. Это немного сложнее, чем я собираюсь охватить в этой статье, но я написал статью Advanced MinGW DLL Topics, в которой объясняется, как это сделать, а также некоторые подводные камни, на которые следует обратить внимание.

Добавление информации о версии и комментариев к вашей DLL

Если вы просмотрите свойства DLL с помощью проводника Windows и перейдете на вкладку «Подробности», вы, скорее всего, увидите информацию о DLL, такую ​​как версия, автор, авторские права и описание библиотеки.Это приятное прикосновение к DLL (особенно полезно, когда вы хотите знать, какая версия DLL установлена ​​на вашем ПК), и это довольно просто добавить в вашу DLL. Все, что вам нужно сделать, это создать ресурс версии, например следующий:

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

Идея состоит в том, чтобы скомпилировать сценарий ресурсов с помощью windres.exe, а затем передать его компоновщику при связывании библиотеки DLL. Например, если ваш ресурсный скрипт называется «resource.rc»:

Это все, что нужно сделать, чтобы добавить информацию о версии в DLL. Стоит потратить время на то, чтобы сделать это для любых DLL, которые вы выпускаете, так как это полезно и придает вашему приложению профессиональный вид.

Собираем все вместе

Используя информацию из этой статьи, вы теперь сможете собрать все воедино и создать библиотеки DLL с помощью MinGW, а также исполняемые файлы, использующие функции, экспортированные из библиотек DLL. Чтобы помочь вам, я создал небольшой проект, который создает DLL со следующими функциями:

  • Экспортировано “ int Add(int a, int b); ”, используя соглашение о вызовах cdecl.
  • Экспортированные переменные «foo» и «bar».
  • Ресурс информации о версии, встроенный в библиотеку DLL.

Кроме того, будет создан исполняемый файл, который выводит результат сложения, используя экспортированную функцию и экспортированные переменные. Проект можно собрать с помощью утилиты mingw32-make.

Вы можете загрузить пример DLL MinGW с GitHub, чтобы создать пример DLL и приложения из этой статьи. Вы можете либо клонировать репозиторий с помощью git, либо загрузить пример выпуска MinGW DLL. Вы можете использовать его для любых целей, которые посчитаете нужными (полные условия использования см. в «License.txt»). Используя файлы проекта, вы можете легко создать пример из этой статьи и использовать его в качестве основы для своих собственных библиотек DLL и приложений.

Что нужно знать

Еще один момент, о котором следует помнить, это то, что если вы передаете структуры в функцию, экспортированную из библиотеки DLL, или возвращаете структуру из этой функции, вы не сможете позже изменить определение этой структуры. Внесение таких изменений, скорее всего, приведет к сбою старых приложений или к непредусмотренному поведению, поскольку расположение памяти структуры изменилось с момента компиляции приложения. Невозможно изменить определение структуры, не нарушая обратной совместимости, но это тоже не элегантно. Например, в Windows API часто бывает, что структуры имеют член (на первый взгляд бессмысленный), который указывает размер структуры, новые элементы добавляются в конце определения структуры и передаются в функции по адресу, а не по значению (для пример структуры OPENFILENAME). Это те вещи, которые позволяют Windows API изменяться между выпусками Windows, не нарушая обратной совместимости (я знаю, что люди могут сказать, что каждая версия Windows вызывает много проблем с обратной совместимостью, но факт в том, что хорошо написанное приложение, созданное для Windows 95 имеет хорошие шансы работать на последних версиях Windows, что, на мой взгляд, весьма впечатляет).

Похожие страницы


    Дополнительные темы по созданию библиотек DLL с помощью MinGW, включая классы JNI, P/Invoke и C++.
    Как создать полнофункциональное приложение Windows с графическим интерфейсом с помощью компилятора MinGW.
    Как создавать приложения OpenGL для Windows с помощью библиотек freeglut или GLUT для Win32 и компилятора MinGW.

©2001–2018 Transmission Zero. Все права защищены. Конфиденциальность и файлы cookie.

Автопредложение помогает быстро сузить результаты поиска, предлагая возможные совпадения по мере ввода.

создать dll для windows с помощью gcc

создать dll для windows с помощью gcc

‎02.03.2006 11:19

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Я написал программу на C под Linux и хочу превратить ее в dll с помощью gcc, чтобы использовать ее в labview под Windows.

Кто-нибудь уже делал подобное? так какой волшебный вариант передать gcc, чтобы сделать это правильно?

Рольфк

‎03-03-2006 09:54

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Я написал программу на C под Linux и хочу превратить ее в dll с помощью gcc, чтобы использовать ее в labview под Windows.

Кто-нибудь уже делал подобное? так какой волшебный вариант передать gcc, чтобы сделать это правильно?

‎03-03-2006 10:15

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Приветствую,
Я не использовал MinGW для создания dll, но вы должны быть в состоянии создать dll. Ниже приведен небольшой пример из документации MinGW.

Джастин

Как создать dll

Вот пример. Вырежьте и вставьте следующее в файл с именем dllfct.h: Вырежьте и вставьте следующее в файл с именем dllfct.c: Вырежьте и вставьте следующее в файл с именем hello.c: Чтобы создать dll и исполняемый файл, который ее использует, попробуйте следующее:

‎07.03.2006 09:32

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Рольфк

‎07.03.2006 11:17

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

мы также пробовали ключевое слово '__attribute ((dllexport))__', но оно больше не работало.

Я думаю, что стандартный атрибут gcc больше похож на атрибут (экспорт). В конце концов, среды gcc обычно используют общие библиотеки, а не библиотеки DLL. Но опять же, не каждый компилятор gcc сможет просто создать правильные исполняемые файлы Win32. Как уже объяснялось, в цепочке инструментов должно быть довольно много инструментов для намеченной цели.
Например, компилятор/компоновщик должен поддерживать формат библиотеки COFF, чтобы создать допустимый исполняемый файл Win32, если только у вас нет своего собственного формата Win32-совместимого загрузчика, который подключается и понимает формат вашей библиотеки. Также declspec() — это специальное расширение Microsoft, не поддерживаемое готовым gcc. И у gcc нет намерений добавлять что-либо, что они считают нестандартным, и большинство расширений Microsoft нестандартны.

MingW поставляется с исправленным gcc, который поддерживает самые важные вещи, чтобы иметь возможность создавать действительные исполняемые файлы Win32, но большинство других версий gcc этого не делают.

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Это определенно возможно. Было бы проще понять, если бы Labview выдавал более качественные сообщения об ошибках, когда функция загружается неправильно.

Мне удалось скомпилировать следующий пример в dll и использовать его в узле функций библиотеки вызовов Labview;

Я скомпилировал это с 32-битной версией msys2. Я думаю, вы также можете использовать 64-битную версию, если укажете другой компилятор. Чтобы скомпилировать, я запустил:

Флаг -static-libgcc не позволяет labview жаловаться на libgcc_s_dw2-1.dll.

Надеюсь, это кому-нибудь поможет.

Рольфк

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу
написал:

Это определенно возможно. Было бы легче понять, если бы Labview выдавал более качественные сообщения об ошибках, когда функция загружается неправильно.

LabVIEW использует Windows API LoadLibrary(), чтобы попытаться загрузить DLL в память. LoadLibrary() довольно лаконична с точки зрения обработки ошибок. Это либо удается, либо нет, и на самом деле он не возвращает гораздо более подробную информацию о причине сбоя. GetLastError() можно вызвать, чтобы получить дополнительную информацию, но возвращаемые коды ошибок на самом деле не задокументированы и различаются в разных версиях Windows, поэтому немного сложно использовать это для чего-то большего, чем просто индикация ошибок.

Чтобы выдавать более содержательные сообщения об ошибках, связанных с отказавшими зависимостями, в LabVIEW потребуется реализовать собственный фиктивный синтаксический анализатор PE-файлов, чтобы попытаться определить все зависимости, а затем попытаться определить их местонахождение для правил поиска, которые могут различаться в разных версиях Windows и в зависимости от ограничения безопасности, наложенные в текущем сеансе Windows ИТ-отделом или аналогичным органом.

Надеюсь, вы понимаете, что это довольно бесполезное и затратное занятие.

Мне удалось скомпилировать следующий пример в dll и использовать его в узле функций библиотеки вызовов Labview;

aMultb.c:

aMultb.h:

Я скомпилировал это с 32-битной версией msys2. Я думаю, вы также можете использовать 64-битную версию, если укажете другой компилятор. Для компиляции я выполнил:

Флаг -static-libgcc не позволяет labview жаловаться на libgcc_s_dw2-1.dll.

Надеюсь, это кому-нибудь поможет.

Моему последнему сообщению исполнилось 12 лет. Это почти бесконечный период времени в компьютерных терминах. Положение дел с GCC, безусловно, изменилось за это время, и последние версии GCC поддерживают многие Microsoftisms, хотя может быть сложно найти правильный параметр командной строки для передачи в GCC для включения такой функции.

Тем не менее, компиляция кода — это первый шаг. Связывание его с окончательным исполняемым файлом — это совершенно другое дело, и хотя GCC также поддерживает множество различных вариантов и целей, в настоящее время вам обычно требуется дополнительная поддержка с точки зрения целевых скриптов компоновщика.Определенные дистрибутивы GCC, такие как MSys или MingW, поставляются с правильными сценариями поддержки, которые преобразуют параметры командной строки в форму, чтобы компилятор и компоновщик GCC выдавали правильные форматы.

И если вы установите libgcc_s_dw2-1.dll в своей системе Windows, я не вижу причин, по которым DLL, скомпилированная для зависимости от этой библиотеки времени выполнения, не сможет загрузиться в LabVIEW.

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

LabVIEW использует Windows API LoadLibrary(), чтобы попытаться загрузить DLL в память. LoadLibrary() довольно лаконична с точки зрения обработки ошибок. Это либо удается, либо нет, и на самом деле он не возвращает гораздо более подробную информацию о причине сбоя. GetLastError() можно вызвать для получения дополнительной информации, но возвращаемые коды ошибок на самом деле не задокументированы и различаются в разных версиях Windows, поэтому немного сложно использовать это для чего-то большего, чем просто индикация ошибки


Непонятный, плохо задокументированный код ошибки, на мой взгляд, предпочтительнее, чем вообще никакого.

Моему последнему сообщению исполнилось 12 лет. Это почти бесконечный период времени в компьютерных терминах. Положение дел с GCC, безусловно, изменилось за это время, и последние версии GCC поддерживают многие Microsoftismes, хотя может быть сложно найти правильный параметр командной строки для передачи GCC для включения такой функции.

Тем не менее, компиляция кода — это первый шаг. Связывание его с окончательным исполняемым файлом — это совершенно другое дело, и хотя GCC также поддерживает множество различных вариантов и целей, в настоящее время вам обычно требуется дополнительная поддержка с точки зрения целевых скриптов компоновщика. Определенные дистрибутивы GCC, такие как MSys или MingW, поставляются с правильными сценариями поддержки, которые преобразуют параметры командной строки в форму, чтобы заставить компилятор и компоновщик GCC выдавать правильные форматы.

И если вы получите libgcc_s_dw2-1 .dll, установленной в вашей системе Windows, я не вижу причин, по которым DLL, скомпилированная для зависимости от этой библиотеки времени выполнения, не сможет загрузиться в LabVIEW.

Я ответил на этот пост, несмотря на то, что ему уже 12 лет, потому что он до сих пор является первым хитом (по крайней мере, для меня) в Google.

Смысл был не в том, чтобы сказать: "Я могу скомпилировать эту тривиальную функцию с помощью gcc в Windows". То, что я опубликовал, было шагами, которые я использовал для компиляции и связывания функции с dll, которую я мог (и действительно) успешно использовать с узлом функций библиотеки вызовов labview.

Рольфк

‎09-02-2018, 01:38 — отредактировано ‎09-02-2018, 02:01

  • Отметить как новое
  • Добавить в закладки
  • Подписаться
  • Отключить звук
  • Отправить сообщение другу

Неясный, плохо задокументированный код ошибки, на мой взгляд, предпочтительнее, чем вообще никакого.

Я не уверен, что согласен здесь. Если вы введете «код ошибки LoadLibrary» в Google, вы получите дюжину или более рекомендаций по конкретным кодам ошибок, каждая из которых дает вам несколько десятков тысяч или более совпадений с часто очень неясными и даже совершенно неправильными решениями, включая множество поддельные сайты, пытающиеся очернить вас своим приложением для решения проблем с DLL, которое затем сообщает вам целую кучу проблем на вашем компьютере, которые они рады решить для вас, когда вы загружаете их оплаченное «настоящее» приложение, которое в лучшем случае не делает ничего, кроме показывает вам очищенное состояние вашей машины после длительного периода предположительной очистки вашей машины и, в не очень лучшем случае, содержит какую-то другую полезную нагрузку в виде вируса, рекламного ПО, трояна или какой-либо другой махинации.

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

LabVIEW не является средой для отладки проблем с зависимостями DLL и других проблем с DLL. Вполне допустимо предположить, что когда вы хотите использовать DLL в LabVIEW, эта DLL уже полностью отлажена и правильно установлена ​​в системе. Правильная установка — это не копирование ее куда-то на ваш компьютер и ожидание, что она будет работать, а установка DLL и ВСЕХ ее зависимостей в соответствии с правилами, которые Windows налагает на DLL. Если вы разработали DLL, вы несете ответственность за отладку DLL и создание такого установщика, а также тестирование установщика на различных компьютерах с помощью ваших собственных тестовых приложений и инструментов, таких как обходчик зависимостей DLL и Sysinternal Process Explorer.

Если вы не разрабатывали DLL самостоятельно, первоначальный разработчик должен был создать такой установщик, а если он этого не сделал, то это не вина LabVIEW, когда эта DLL не может быть загружена.Ошибка LoadLibrary(), и состояние дел вокруг этой функции таково, что перенастроенный код ошибки сильно различается между версиями Windows, включая иногда даже вводящие в заблуждение подсказки.

Майкрософт без каких-либо причин не удалила упоминания конкретных кодов ошибок со страницы MSDN для этой функции!

В текущей документации указано:

Если функция завершается ошибкой, возвращается значение NULL. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.

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

Если загрузка библиотеки DLL в узле библиотеки вызовов LabVIEW не удалась, это означает, что библиотека DLL повреждена или она или одна из ее зависимостей неправильно установлена ​​на компьютере! На данный момент LabVIEW не может сообщить вам больше, чем то, что Windows отказалась загружать DLL.

DLL — это библиотеки с динамическим подключением, что означает, что они связаны с вашей программой во время выполнения, а не во время сборки. DLL состоит из трех частей:

код и данные

библиотека импорта

Код и данные — это те части, которые вы пишете: функции, переменные и т. д. Все они объединяются вместе, как если бы вы создавали один большой объектный файл, и помещаются в dll. Они вообще не помещаются в ваш .exe.

Экспорт содержит список функций и переменных, которые dll делает доступными для других программ. Думайте об этом как о списке «глобальных» символов, остальные скрыты. Обычно вы создаете этот список вручную с помощью текстового редактора, но это можно сделать автоматически из списка функций в вашем коде. Программа dlltool создает раздел экспорта dll из вашего текстового файла экспортированных символов.

Библиотека импорта представляет собой обычную UNIX-подобную библиотеку .a, но она содержит только крошечную часть информации, необходимой для того, чтобы сообщить ОС, как ваша программа взаимодействует с ("импортирует") dll. Эта информация связана с вашим .exe . Это также генерируется dlltool .

Создание библиотек DLL

Давайте рассмотрим простой пример создания dll. В этом примере мы будем использовать один файл myprog.c для программы ( myprog.exe ) и один файл mydll.c для содержимого dll ( mydll.dll ).

К счастью, с последними версиями gcc и binutils процесс сборки dll теперь довольно прост. Допустим, вы хотите создать эту минимальную функцию в mydll.c:

Сначала скомпилируйте mydll.c в объектный код:

Затем сообщите gcc, что он создает общую библиотеку:

Вот оно! Чтобы закончить пример, теперь вы можете связать dll с помощью простой программы:

Затем свяжите свою dll с помощью команды, например:

Однако, если вы создаете dll в качестве библиотеки экспорта, вам, вероятно, потребуется использовать полный синтаксис:

Имя вашей библиотеки — $ с префиксом cyg для DLL и lib для библиотеки импорта. Библиотеки Cygwin используют префикс cyg, чтобы отличить их от собственных библиотек DLL MinGW для Windows. Дополнительные сведения см. на веб-сайте MinGW. $ — это все ваши объектные файлы, объединенные в статические библиотеки или отдельные объектные файлы, а $ — это импортные библиотеки, с которыми вам нужно связать, например, '-lpng -lz -L/usr/local/special -lmyspeciallib' .

Связывание с DLL

Если у вас уже есть DLL, вам нужно создать библиотеку импорта, совместимую с Cygwin. Если у вас есть исходный код для компиляции DLL, см. раздел «Создание DLL» для получения подробной информации о сборке gcc для вас. Если у вас нет исходного кода или поставляемой рабочей библиотеки импорта, вы можете получить большую часть пути, создав файл .def с помощью этих команд (вам может потребоваться сделать это в bash, чтобы цитирование работало правильно):

Обратите внимание, что это будет работать, только если DLL не удалена. В противном случае вы получите сообщение об ошибке: «Нет символов в foo.dll».

Получив файл .def, вы можете создать из него библиотеку импорта следующим образом:

Читайте также: