Неуправляемые библиотеки DLL в C++

Я читал много учебников / статей о неуправляемых библиотеках DLL в C++. Однако, хоть убей, я не могу понять эту концепцию. Меня легко сбивает с толку кажущееся разногласие по поводу того, нужен ли ему файл заголовка, как его экспортировать, нужен ли мне файл .lib и что есть у вас.

Итак, предположим, что у меня есть только такая функция:

public int calculateSquare(int num)
{
    return num*num;
}

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
5 824
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

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

Обычно это делается с помощью комбинации макросов и файлов заголовков. Вы создаете макрос с именем MYIMPORTEXPORT и с помощью условных операторов макроса заставляете его работать как __declspec (dllexport) в dll и __declspec (dllimport) в клиентском коде.

в файле MYIMPORTEXPORT.h

#ifdef SOME_CONDITION
#define MYIMPORTEXPORT __declspec( dllexport )
#else
#define MYIMPORTEXPORT __declspec( dllimport )
#endif

в файле MyHeader.h

#include <MyImportExport.h>

MYIMPORTEXPORT public int calculateSquare(int num)
{
    return num*num;
}

в файле dll .cpp

#define SOME_CONDITION

#include <MyHeader.h>

в файле клиентского кода .cpp

#include <MyHeader.h>

Конечно, вам также необходимо сообщить компоновщику, что вы создаете dll с Параметр / DLL.

В процессе сборки также создается файл .lib, это статическая библиотека, в данном случае называемая заглушкой, на которую клиентский код должен ссылаться, как если бы он ссылался на настоящую статическую библиотеку. Автоматически dll загружается при запуске клиентского кода. Конечно, dll должна быть найдена ОС через механизм поиска, что означает, что вы не можете разместить dll где угодно, а только в определенном месте. Здесь больше об этом.

мусорная корзина - очень удобный инструмент для проверки того, правильно ли вы экспортировали функцию из dll и правильно ли импортирует код клиента. Запустите его с помощью / EXPORTS и / IMPORTS соответственно.

Вам необходимо экспортировать функцию, используя __declspec( dllexport ) или добавив функцию в файл определения модуля (.def). Затем скомпилируйте проект как DLL.

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

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

Ответ QBziZ достаточно правильный. См. Неуправляемые библиотеки DLL в C++

Для его завершения: В C++, если вам нужно использовать символ, вы должны сообщить компилятору, что он существует, и часто его прототип.

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

В C++ вы должны сообщить об этом компилятору.

См. Заголовок C / C++ как оглавление книги.

Лучше всего разместить необходимый код в каком-нибудь общем месте. «Интерфейс», если хотите. Обычно это делается в файле заголовка, который называется заголовком, потому что обычно это не независимый исходный файл. Заголовок - это только файл, цель которого - включить (т.е. скопировать / вставить препроцессором) в настоящие исходные файлы.

По сути, кажется, что вам нужно дважды объявлять символ (функцию, класс и т. д.). Это почти ересь по сравнению с другими языками.

Вы должны увидеть его как книгу со сводной таблицей или указателем. В таблице у вас есть все главы. В тексте у вас есть главы и их содержание.

А иногда вы просто счастливы, что у вас есть список глав.

В C++ это заголовок.

А как насчет DLL?

Итак, вернемся к проблеме DLL: цель DLL - экспортировать символы, которые будет использовать ваш код.

Таким образом, для C++ вы должны как экспортировать код при компиляции (например, в Windows, использовать __declspec), так и «публиковать» таблицу экспортируемых данных (т.е. иметь «общедоступные» заголовки, содержащие экспортированные объявления). .

Контрольный список для экспорта функций:

  • Подходит ли соглашение о вызове для вызывающего абонента? (это определяет, как передаются параметры и результаты, и кто отвечает за очистку стека). Вы должны четко указать свое соглашение о вызовах.
  • Под каким именем будет экспортироваться символ? В C++ обычно требуется украшать ("искажать") имена символов, например чтобы различать разные перегрузки.
  • Сообщите компоновщику, чтобы функция отображалась как экспорт DLL.

На MSVC:

  • __stdcall (который является паскальским соглашением о вызовах) - это типичное соглашение о вызовах для экспортируемых символов, которое, я думаю, поддерживается большинством клиентов.
  • extern "C" позволяет экспортировать символ в стиле C без изменения имени.
  • используйте __declspec(dllexport), чтобы отметить символ, который нужно экспортировать, или свяжите отдельный файл .def, в котором перечислены экспортируемые символы. С файлом .def вы также можете экспортировать только по порядковому номеру (не по имени) и изменить имя экспортируемого символа.

Другие вопросы по теме