Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Второй — явно загрузить DLL через LoadLibrary.

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

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

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

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

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

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

Удачи...

Другие советы

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

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

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

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

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

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

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

class DLL MyClassToExport { ... }

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

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

Добавляем простой рабочий пример экспорта класса C++ из 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;
}

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

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

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

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

Точно так же, как COM.:)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top