Ответ 1
Существует по существу три правильных способа:
- Используйте С++/CLI. Это оптимальный способ, если эта DLL будет использоваться только .NET.
- Используйте API-интерфейс, совместимый с
extern "C"
, как и сам API Windows. Это самый портативный, но не так удобен для ваших абонентов, как использование модели класса для представления ваших объектов.- Это лучший вариант, если вы действительно собираетесь писать в ANSI C (не С++).
- Для этого пути вы записываете свои функции как
extern "C" returntype __stdcall __declspec(dllexport) func(params) { ... }
- Вы также должны использовать модель памяти "вызывающий-предоставить-буфер", а не возвращать буфер, выделенный внутри вашей библиотеки. В случаях, когда вам нужно выделить память для внутреннего состояния, вызывающий должен видеть в ней непрозрачный дескриптор, и вы должны предоставить функции доступа для вызывающего пользователя для извлечения данных. Ни при каких обстоятельствах не следует ожидать, что вызывающий абонент выделит память, выделенную внутри вашей библиотеки, однако это нормально, когда вызывающий абонент попросит библиотеку выполнить освобождение.
- Используйте COM или COM-API. Здесь вы возвращаете (часто через параметр out) указатель на интерфейс, который является классом с чистыми виртуальными функциями, невалютными функциями и данными.
- Реализация в конкретных классах, полученных из этого абстрактного интерфейса, они могут иметь данные и вспомогательные функции в изобилии, поскольку это не влияет на двоичный интерфейс.
- Это намного больше работы в библиотеке, но чрезвычайно переносимо и легко для потребителя.
И есть одна вещь абсолютно НЕ делать:
- используйте __declspec (dllexport) для классов С++.
EDIT: Я хочу также объяснить некоторые хорошие практики для опции № 2, которая позволит максимально повысить переносимость и сделать части C/С++ пригодными для использования из неуправляемых приложений.
Вы можете сделать это проще с помощью макроса, обычный способ сделать это:
В вашем файле заголовка все объявления функций выглядят как
MYPROJECTAPI(returntype) PublicFunc(params);
В вашем проекте определение
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
В потребительских проектах
#define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
а затем вы можете определить макрос по-разному для других компиляторов, таких как gcc, которые не используют __declspec
.
Полное решение будет выглядеть (в публичном файле заголовка myproject.h
):
#if _WIN32
# if BUILDMYPROJECT
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllexport)
# else
# define MYPROJECTAPI(returntype) \
extern "C" returntype __stdcall __declspec(dllimport)
# endif
#else
# define MYPROJECTAPI(returntype) extern "C" returntype
#endif
а затем ваш проект Visual С++ приведет к определению BUILDMYPROJECT
при создании myproject.dll