Domanda

Ho letto molti tutorial / articoli sulle DLL non gestite in C ++. Per la mia vita, tuttavia, non riesco a capire il concetto. Sono facilmente confuso dall'apparente disaccordo sul fatto che abbia bisogno di un file di intestazione, come esportarlo, se ho bisogno di un file .lib e cosa hai.

Quindi, supponiamo che io abbia una funzione come questa:

public int calculateSquare(int num)
{
    return num*num;
}

Ignorando il codice attuale, cosa devo fare per rendere questa semplice funzione, da sola, in una DLL che posso quindi chiamare? Devo solo aggiungere __dllexport o qualunque cosa sia alla prima riga o ho bisogno di un'intestazione? Sono perplesso per tutto questo.

È stato utile?

Soluzione

Non posso sottolinearlo abbastanza, il compilatore C ++ non vede i file di intestazione, dopo che il preprocessore è stato fatto, c'è solo un grande file sorgente (chiamato anche unità di compilazione). Quindi rigorosamente non è necessaria un'intestazione per esportare questa funzione da una dll. Ciò di cui hai bisogno è una forma di compilazione condizionale per esportare la funzione nella dll che stai compilando e importarla nel codice client.

In genere questo viene fatto con una combinazione di macro e file di intestazione. Si crea una macro denominata MYIMPORTEXPORT e mediante l'uso di istruzioni condizionali macro si fa funzionare come __declspec (dllexport) nella dll e __declspec (dllimport) nel codice client.

nel file MYIMPORTEXPORT.h

#ifdef SOME_CONDITION
#define MYIMPORTEXPORT __declspec( dllexport )
#else
#define MYIMPORTEXPORT __declspec( dllimport )
#endif

nel file MyHeader.h

#include <MyImportExport.h>

MYIMPORTEXPORT public int calculateSquare(int num)
{
    return num*num;
}

nel file dll .cpp

#define SOME_CONDITION

#include <MyHeader.h>

nel file .cpp del codice client

#include <MyHeader.h>

Ovviamente devi anche segnalare al linker che stai creando una dll con / opzione DLL .

Il processo di compilazione creerà anche un file .lib, questa è una lib statica - in questo caso chiamata stub - a cui il codice client deve collegarsi come se si stesse collegando a una vera lib statica. Automaticamente, la dll verrà caricata quando viene eseguito il codice client. Ovviamente la DLL deve essere trovata dal sistema operativo attraverso il suo meccanismo di ricerca, il che significa che non è possibile collocare la DLL solo da nessuna parte, ma in una posizione specifica. Qui c'è di più.

Uno strumento molto utile per vedere se hai esportato la funzione corretta dalla dll e se il codice client sta importando correttamente è dumpbin . Eseguilo rispettivamente con / EXPORTS e / IMPORTS.

Altri suggerimenti

La risposta di QBziZ è abbastanza giusta. Vedi DLL non gestite in C ++

Per completarlo: In C ++, se devi usare un simbolo, devi dire al compilatore che esiste, e spesso, il suo prototipo .

In altre lingue, il compilatore esplorerà la libreria da solo e troverà il simbolo et voilà .

In C ++, devi dirlo al compilatore.

Vedi un'intestazione C / C ++ come sommario di un libro

Il modo migliore è mettere in un luogo comune il codice necessario. L '"interfaccia", se lo desideri. Questo di solito viene fatto in un file di intestazione, chiamato intestazione perché di solito non è un file di origine indipendente. L'intestazione è solo un file il cui scopo deve essere incluso (ovvero copia / incolla dal preprocessore) in file sorgente reali.

In sostanza, sembra che tu debba dichiarare due volte un simbolo (funzione, classe, qualunque cosa). Il che è quasi un'eresia rispetto ad altre lingue.

Dovresti vederlo come un libro, con una tabella riassuntiva o un indice. Nella tabella, hai tutti i capitoli. Nel testo, hai i capitoli e il loro contenuto.

E a volte, sei solo felice di avere l'elenco dei capitoli.

In C ++, questa è l'intestazione.

E la DLL?

Quindi, tornando al problema DLL: lo scopo di una DLL è esportare simboli che verranno utilizzati dal codice.

Quindi, in modo C ++, devi entrambi esportare il codice durante la compilazione (ad esempio, in Windows, utilizzare __declspec, per esempio) e " pubblicare " una tabella di ciò che viene esportato (ovvero hanno intestazioni "pubbliche" contenenti le dichiarazioni esportate).

Elenco di controllo per le funzioni di esportazione:

  • La convenzione di chiamata è adatta al chiamante? (questo determina come vengono passati parametri e risultati e chi è responsabile della pulizia dello stack). Dovresti dichiarare esplicitamente la tua convenzione di chiamata.
  • Con quale nome verrà esportato il simbolo? Il C ++ di solito deve decorare (" mangle ") i nomi dei simboli, ad es. distinguere tra diversi sovraccarichi.
  • Indica al linker di rendere visibile la funzione come esportazione DLL

Su MSVC:

  • __stdcall (che è la convenzione di chiamata pascal) è la tipica convenzione di chiamata per i simboli esportati - supportata dalla maggior parte dei client immagino.
  • esterno "C" ti consente di esportare il simbolo in stile C senza alterare il nome
  • usa __declspec (dllexport) per contrassegnare un simbolo da esportare o collega un file .def separato in cui sono elencati i simboli da esportare. Con un file .def puoi anche esportare solo in modo ordinale (non in base al nome) e modificare il nome del simbolo che viene esportato.

È necessario esportare la funzione utilizzando __declspec (dllexport) o aggiungendo la funzione a un file di definizione del modulo (.def). Quindi compilare il progetto come una DLL.

Sul lato client, hai due opzioni. Utilizzare una libreria di importazione (.lib) generata durante la compilazione della DLL. Il semplice collegamento al progetto client con questa libreria consente di accedere alle funzioni esportate dalla DLL. E hai bisogno di un file di intestazione, perché il compilatore deve conoscere la firma della tua funzione - che restituisce int e prende un int. Per ricapitolare, devi collegare una libreria di importazione (.lib) e un file di intestazione che contiene l'intestazione della tua funzione.

Un altro modo è caricare la DLL in modo dinamico usando WinAPI chiama LoadLibrary e quindi GetProcAddress per ottenere un puntatore alla funzione. Il puntatore per funzionare deve avere il tipo corretto, in modo che il compilatore possa assegnargli i parametri corretti e venga utilizzata la convenzione di chiamata corretta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top