Exportando uma classe C++ de uma DLL
Pergunta
A maior parte do meu desenvolvimento em C/C++ envolve arquivos de módulos monolíticos e absolutamente nenhuma classe, então geralmente quando preciso fazer um DLL com funções acessíveis, acabei de exportá-las usando o padrão __declspec(dllexport)
diretiva.Em seguida, acesse-os dinamicamente via LoadLibrary()
ou em tempo de compilação com um cabeçalho e um arquivo lib.
Como você faz isso quando deseja exportar uma classe inteira (e todos os seus métodos e propriedades públicas)?
É possível carregar dinamicamente essa classe em tempo de execução e, em caso afirmativo, como?
Como você faria isso com um cabeçalho e uma biblioteca para vinculação em tempo de compilação?
Solução
E quanto à vinculação tardia?Como no carregamento com loadlibrary () e getProcaddress ()?Estou acostumado a carregar a biblioteca no tempo de execução e seria ótimo se você pudesse fazer isso aqui.
Portanto, existem duas maneiras de carregar a DLL.A primeira é fazer referência a um ou mais símbolos da DLL (seu nome de classe, por exemplo), fornecer um import .LIB apropriado e deixar o vinculador descobrir tudo.
A segunda é carregar explicitamente a DLL via LoadLibrary.
Qualquer abordagem funciona bem para exportações de funções de nível C.Você pode deixar o vinculador cuidar disso ou chamar GetProcAddress conforme observado.
Mas quando se trata de exportação Aulas, normalmente apenas a primeira abordagem é usada, ou seja, vincular implicitamente à DLL.Nesse caso, a DLL é carregada no momento de início do aplicativo e o aplicativo falha ao carregar se a DLL não for encontrada.
Se você deseja vincular a uma classe definida em uma DLL e deseja que essa DLL seja carregada dinamicamente, algum tempo após o início do programa, você tem duas opções:
Crie objetos da classe usando uma função especial de fábrica, que internamente terá que usar (um pouquinho de) assembler para "conectar" objetos recém-criados aos seus deslocamentos apropriados.Isso deve ser feito em tempo de execução APÓS o carregamento da DLL, obviamente.Uma boa explicação desta abordagem pode ser encontrada aqui.
Use um DLL de carregamento atrasado.
Considerando todas as coisas...provavelmente é melhor usar a vinculação implícita; nesse caso, você definitivamente deseja usar a técnica de pré-processador mostrada acima.Na verdade, se você criar uma nova DLL no Visual Studio e escolher a opção “exportar símbolos” essas macros serão criadas para você.
Boa sorte...
Outras dicas
Quando você construir a DLL e o módulo que usará a DLL, tenha algum tipo de #define que você possa usar para distinguir entre um e outro, então você pode fazer algo assim no arquivo de cabeçalho da sua classe:
#if defined( BUILD_DLL )
#define IMPORT_EXPORT __declspec(dllexport)
#else
#define IMPORT_EXPORT __declspec(dllimport)
#endif
class IMPORT_EXPORT MyClass {
...
};
Editar:crashmstr me venceu!
Utilizo algumas macros para marcar o código para importação ou exportação
#ifdef ISDLL #define DLL __declspec(dllexport) #endif #ifdef USEDLL #define DLL __declspec(dllimport) #endif
Em seguida, declare a classe em um arquivo de cabeçalho:
class DLL MyClassToExport { ... }
Então #define ISDLL
na biblioteca e USEDLL
antes de incluir o arquivo de cabeçalho no local onde deseja usar a classe.
Não sei se você precisará fazer algo diferente para trabalhar com LoadLibrary
Adicionando um exemplo simples de trabalho para exportar uma classe C++ de uma DLL:
O exemplo abaixo fornece apenas uma breve visão geral de como dll e exe podem interagir entre si (autoexplicativo), mas precisa de mais coisas para adicionar para mudar para um código de produção.
O exemplo completo da amostra está dividido em duas partes
A.Criando uma biblioteca .dll (MyDLL.dll)
B.Criando um aplicativo que usa a biblioteca .dll (aplicativo).
A.Arquivo de projeto .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.Projeto de aplicação que carrega e vincula o arquivo .dll já criado:
#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;
}
Recentemente me fiz exatamente a mesma pergunta e resumi minhas descobertas em uma postagem de blog.Você pode achar útil.
Abrange a exportação de classes C++ de uma DLL, bem como o carregamento delas dinamicamente com LoadLibrary
, e discute algumas das questões relacionadas a isso, como gerenciamento de memória, manipulação de nomes e convenções de chamada.
Se você deseja colocar uma vtable na classe que está exportando, você pode exportar uma função que retorna uma interface e implementar a classe no .dll e, em seguida, colocá-la no arquivo .def.Talvez você precise fazer alguns truques de declaração, mas não deve ser muito difícil.
Assim como COM.:)