Сведения о вопросе

MAT

14:15, 5th August, 2020

Теги

c++   windows   dll    

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

Просмотров: 696   Ответов: 6

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

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

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

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



  Сведения об ответе

VCe znayu

00:30, 23rd August, 2020

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

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

Edit: crashmstr опередил меня!


  Сведения об ответе

PIRLO

09:52, 25th August, 2020

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

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

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

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

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

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

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

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

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

Удачи...


  Сведения об ответе

PIRLO

11:21, 15th August, 2020

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

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

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

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

class DLL MyClassToExport { ... }

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

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


  Сведения об ответе

SSESION

22:41, 13th August, 2020

Добавление простого рабочего примера для экспорта класса 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;
}

Б. Проект приложения, который загружает и связывает уже созданный файл .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;
}


  Сведения об ответе

lesha

17:26, 12th August, 2020

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

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


  Сведения об ответе

#hash

21:06, 25th August, 2020

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

Совсем как COM. :)


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться