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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Удачи...

Ответ 2

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

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

Изменить: crashmstr победил меня!

Ответ 3

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

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

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

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

class DLL MyClassToExport { ... }

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

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

Ответ 4

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

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

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

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

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

а..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;
}

В. Application Project, который загружает и связывает уже созданный 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;
}

Ответ 5

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

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

Ответ 6

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

Как и COM.:)