Экспорт класса C ++ из DLL - PullRequest
       39

Экспорт класса C ++ из DLL

26 голосов
/ 26 августа 2008

Большая часть моей разработки на C / C ++ включает монолитные файлы модулей и абсолютно никаких классов, поэтому обычно, когда мне нужно создать DLL с доступными функциями, я просто экспортирую их, используя стандартную директиву __declspec(dllexport). Затем получите к ним динамический доступ через LoadLibrary() или во время компиляции с заголовком и файлом lib.

Как это сделать, если вы хотите экспортировать весь класс (и все его публичные методы и свойства)?

Возможно ли динамически загрузить этот класс во время выполнения и, если да, то как?

Как бы вы сделали это с заголовком и библиотекой lib для компиляции во время компиляции?

Ответы [ 6 ]

17 голосов
/ 26 августа 2008

А как насчет позднего связывания? Как при загрузке это с LoadLibrary () и GetProcAddress ()? Я привык быть в состоянии загрузить библиотеку во время выполнения и было бы здорово, если бы ты мог сделать это здесь.

Таким образом, есть два способа загрузки DLL. Первый - ссылаться на один или несколько символов из DLL (например, имя вашего класса), предоставлять соответствующий импорт .LIB и позволить компоновщику разобраться во всем.

Второй - явная загрузка DLL через LoadLibrary.

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

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

Если вы хотите создать ссылку на класс, определенный в DLL, и хотите, чтобы эта DLL загружалась динамически, через некоторое время после запуска программы, у вас есть два варианта:

  1. Создайте объекты класса, используя специальную фабричную функцию, которая внутренне должна будет использовать (совсем немного) ассемблер, чтобы «подключить» вновь созданные объекты к соответствующим смещениям. Это должно быть сделано во время выполнения ПОСЛЕ того, как DLL была загружена, очевидно. Хорошее объяснение этого подхода можно найти здесь .

  2. Использовать DLL с задержкой загрузки .

Учитывая все обстоятельства ... вероятно, лучше просто пойти с неявным связыванием, и в этом случае вы определенно захотите использовать технику препроцессора, показанную выше. Фактически, если вы создадите новую DLL в Visual Studio и выберете опцию «экспортировать символы», эти макросы будут созданы для вас.

Удачи ...

17 голосов
/ 26 августа 2008

Когда вы создаете библиотеку DLL и модуль, который будет использовать библиотеку DLL, у вас есть #define, который вы можете использовать для различения одного и другого, тогда вы можете сделать что-то подобное в заголовочном файле вашего класса:

#if defined( BUILD_DLL )
    #define IMPORT_EXPORT __declspec(dllexport)
#else
    #define IMPORT_EXPORT __declspec(dllimport)
#endif
class IMPORT_EXPORT MyClass {
    ...
};

Редактировать: crashmstr победил меня!

12 голосов
/ 24 июня 2014

Добавление простого рабочего примера для экспорта класса C ++ из DLL:

Приведенный ниже пример дает вам только краткий обзор того, как dll и exe могут взаимодействовать друг с другом (не требует пояснений), но для добавления в рабочий код нужно добавить больше вещей.

Пример полной выборки разделен на две части

A. Создание библиотеки .dll (MyDLL.dll)

B. Создание приложения, использующего библиотеку .dll (Приложение).

A. Файл проекта .dll (MyDLL.dll):

1. dllHeader.h

#ifdef  MYDLL_EXPORTS 
#define DLLCALL __declspec(dllexport)   /* Should be enabled before compiling 
                                           .dll project for creating .dll*/
#else
#define DLLCALL __declspec(dllimport)  /* Should be enabled in Application side
                                          for using already created .dll*/
#endif

// Interface Class
class ImyMath {
public:
    virtual ~ImyMath() {;}
    virtual int Add(int a, int b) = 0;
    virtual int Subtract(int a, int b) = 0;
};

// Concrete Class
class MyMath: public ImyMath {
public:
    MyMath() {}
    int Add(int a, int b);
    int Subtract(int a, int b);
    int a,b;
};

//  Factory function that will return the new object instance. (Only function
//  should be declared with DLLCALL)
extern "C" /*Important for avoiding Name decoration*/
{
    DLLCALL ImyMath* _cdecl CreateMathObject();
};

// Function Pointer Declaration of CreateMathObject() [Entry Point Function]
typedef ImyMath* (*CREATE_MATH) ();

2. dllSrc.cpp

#include "dllHeader.h"

// Create Object
DLLCALL ImyMath* _cdecl CreateMathObject() {
    return new MyMath();
}

int MyMath::Add(int a, int b) {
    return a+b;
}

int MyMath::Subtract(int a, int b) {
    return a-b;
}

B. Проект приложения, который загружает и связывает уже созданный DLL-файл:

 #include <iostream>
#include <windows.h>
#include "dllHeader.h"

int main()
{
    HINSTANCE hDLL = LoadLibrary(L"MyDLL.dll"); // L".\Debug\MyDLL.dll"

    if (hDLL == NULL) {
        std::cout << "Failed to load library.\n";
    }
    else {
        CREATE_MATH pEntryFunction = (CREATE_MATH)GetProcAddress(hDLL,"CreateMathObject");
        ImyMath* pMath = pEntryFunction();
        if (pMath) {
            std::cout << "10+10=" << pMath->Add(10, 10) << std::endl;
            std::cout << "50-10=" << pMath->Subtract(50, 10) << std::endl;
        }
        FreeLibrary(hDLL);
    }
    std::cin.get();
    return 0;
}
12 голосов
/ 26 августа 2008

Я использую некоторые макросы для пометки кода для импорта или экспорта

#ifdef ISDLL
#define DLL __declspec(dllexport)
#endif

#ifdef USEDLL
#define DLL __declspec(dllimport)
#endif

Затем объявите класс в заголовочном файле:

class DLL MyClassToExport { ... }

Затем #define ISDLL в библиотеке и USEDLL перед включением файла заголовка в место, где вы хотите использовать класс.

Не знаю, нужно ли вам что-то делать по-другому для работы с LoadLibrary

7 голосов
/ 17 сентября 2011

Недавно я задал себе точно такой же вопрос и резюмировал свои выводы в блоге . Вы можете найти это полезным.

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

0 голосов
/ 27 августа 2008

Если вы хотите поместить vtable в экспортируемый класс, вы можете экспортировать функцию, которая возвращает интерфейс, и реализовать класс в .dll, а затем поместить его в файл .def. Возможно, вам придется сделать некоторую хитрость объявления, но это не должно быть слишком сложно.

Так же, как COM. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...