Подтвердить что ты не робот

Написание DLL в C/С++ для .Net-совместимости

В моем приложении С# я хотел бы написать часть кода в C. Я планирую написать библиотеку DLL, которая будет взаимодействовать с .Net. Как я могу это сделать?

4b9b3361

Ответ 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

Ответ 2

В двух словах:

(1) Создайте новый проект библиотеки С++/CLI.

(2) Напишите свой код. Для классов, которые должны быть доступны из вашего проекта С#, обязательно создайте их как классы CLR:

public ref class R {/*...*/};       // CLR class
public value class V {/*...*/};     // CLR struct
public interface class I {/*...*/}; // CLR interface

(3) Скомпилируйте проект и добавьте ссылку на него в проект С#.

Ответ 4

Ниже приведен пример приложения, в котором я должен был это сделать. В моем случае мне понадобилась DLL для переноса вызовов на функции, которые были доступны только в .lib. Ключевой частью является extern "C" __declspec (dllexport) в объявлении. Это в основном все, что вам нужно. Остальное просто использовало dllimport в приложении С# и получало право сортировки.

extern "C" __declspec (dllexport) LONG EstablishContext(DWORD dwScope, 
                                                    LPCVOID pvReserved1, 
                                                    LPCVOID pvReserved2, 
                                                    LPSCARDCONTEXT phContext)
{
    return SCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext);
}