DLL - Come scrivere
Innanzitutto, discuteremo dei problemi e dei requisiti da considerare durante lo sviluppo delle tue DLL.
Tipi di DLL
Quando si carica una DLL in un'applicazione, due metodi di collegamento consentono di chiamare le funzioni DLL esportate. I due metodi di collegamento sono:
- collegamento dinamico in fase di caricamento e
- collegamento dinamico in fase di esecuzione.
Collegamento dinamico in fase di caricamento
Nel collegamento dinamico in fase di caricamento, un'applicazione effettua chiamate esplicite alle funzioni DLL esportate come le funzioni locali. Per utilizzare il collegamento dinamico in fase di caricamento, fornire un file di intestazione (.h) e un file di libreria di importazione (.lib), quando si compila e si collega l'applicazione. Quando si esegue l'operazione, il linker fornirà al sistema le informazioni necessarie per caricare la DLL e risolvere i percorsi di funzione DLL esportati al momento del caricamento.
Collegamento dinamico di runtime
Nel collegamento dinamico di runtime, un'applicazione chiama la funzione LoadLibrary o la funzione LoadLibraryEx per caricare la DLL in fase di esecuzione. Dopo che la DLL è stata caricata correttamente, si utilizza la funzione GetProcAddress per ottenere l'indirizzo della funzione DLL esportata che si desidera chiamare. Quando si utilizza il collegamento dinamico di runtime, non è necessario un file di libreria di importazione.
Il seguente elenco descrive i criteri dell'applicazione per la scelta tra collegamento dinamico in fase di caricamento e collegamento dinamico in fase di esecuzione -
Startup performance - Se le prestazioni iniziali di avvio dell'applicazione sono importanti, è necessario utilizzare il collegamento dinamico in fase di esecuzione.
Ease of use- Nel collegamento dinamico in fase di caricamento, le funzioni DLL esportate sono come funzioni locali. Ti aiuta a chiamare facilmente queste funzioni.
Application logic- Nel collegamento dinamico di runtime, un'applicazione può diramarsi per caricare diversi moduli come richiesto. Questo è importante quando si sviluppano versioni multilingue.
Il punto di ingresso della DLL
Quando si crea una DLL, è possibile specificare facoltativamente una funzione del punto di ingresso. La funzione del punto di ingresso viene chiamata quando processi o thread si collegano alla DLL o si scollegano dalla DLL. È possibile utilizzare la funzione del punto di ingresso per inizializzare o distruggere le strutture di dati come richiesto dalla DLL.
Inoltre, se l'applicazione è multithread, è possibile utilizzare l'archiviazione locale del thread (TLS) per allocare la memoria privata a ogni thread nella funzione del punto di ingresso. Il codice seguente è un esempio della funzione del punto di ingresso della DLL.
BOOL APIENTRY DllMain(
HANDLE hModule, // Handle to DLL module
DWORD ul_reason_for_call,
LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACHED:
// A process is loading the DLL.
break;
case DLL_THREAD_ATTACHED:
// A process is creating a new thread.
break;
case DLL_THREAD_DETACH:
// A thread exits normally.
break;
case DLL_PROCESS_DETACH:
// A process unloads the DLL.
break;
}
return TRUE;
}
Quando la funzione del punto di ingresso restituisce un valore FALSE, l'applicazione non verrà avviata se si utilizza il collegamento dinamico in fase di caricamento. Se si utilizza il collegamento dinamico di runtime, non verrà caricata solo la singola DLL.
La funzione del punto di ingresso dovrebbe eseguire solo semplici attività di inizializzazione e non dovrebbe chiamare altre funzioni di caricamento o terminazione della DLL. Ad esempio, nella funzione punto di ingresso, non dovresti chiamare direttamente o indirettamente il fileLoadLibrary funzione o il LoadLibraryExfunzione. Inoltre, non dovresti chiamare ilFreeLibrary funzione quando il processo sta terminando.
WARNING- Nelle applicazioni multithread, assicurarsi che l'accesso ai dati globali della DLL sia sincronizzato (thread-safe) per evitare il possibile danneggiamento dei dati. A tale scopo, utilizzare TLS per fornire dati univoci per ogni thread.
Esportazione di funzioni DLL
Per esportare le funzioni DLL, è possibile aggiungere una parola chiave di funzione alle funzioni DLL esportate o creare un file di definizione del modulo (.def) che elenca le funzioni DLL esportate.
Per utilizzare una parola chiave di funzione, è necessario dichiarare ciascuna funzione che si desidera esportare con la seguente parola chiave:
__declspec(dllexport)
Per utilizzare le funzioni DLL esportate nell'applicazione, è necessario dichiarare ogni funzione che si desidera importare con la seguente parola chiave:
__declspec(dllimport)
In genere, useresti un file di intestazione con estensione define dichiarazione e un ifdef istruzione per separare l'istruzione export e l'istruzione import.
È inoltre possibile utilizzare un file di definizione del modulo per dichiarare le funzioni DLL esportate. Quando si utilizza un file di definizione del modulo, non è necessario aggiungere la parola chiave function alle funzioni DLL esportate. Nel file di definizione del modulo, dichiari il fileLIBRARY dichiarazione e il EXPORTSistruzione per la DLL. Il codice seguente è un esempio di un file di definizione.
// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS
HelloWorld
Scrivi una DLL di esempio
In Microsoft Visual C ++ 6.0 è possibile creare una DLL selezionando il file Win32 Dynamic-Link Library tipo di progetto o il file MFC AppWizard (dll) Tipo di progetto.
Il codice seguente è un esempio di una DLL creata in Visual C ++ utilizzando il tipo di progetto Win32 Dynamic-Link Library.
// SampleDLL.cpp
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"),
TEXT("In a DLL"), MB_OK);
}
// File: SampleDLL.h
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld() ;
#else
extern __declspec(dllimport) void HelloWorld() ;
#endif
#endif
Chiamare una DLL di esempio
Il codice seguente è un esempio di un progetto di applicazione Win32 che chiama la funzione DLL esportata nella DLL SampleDLL.
// SampleApp.cpp
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}
NOTE - Nel collegamento dinamico in fase di caricamento, è necessario collegare la libreria di importazione SampleDLL.lib creata quando si compila il progetto SampleDLL.
Nel collegamento dinamico di runtime, si utilizza il codice simile al codice seguente per chiamare la funzione DLL esportata SampleDLL.dll.
...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;
hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
if (HelloWorld != NULL)
(HelloWorld);
fFreeDLL = FreeLibrary(hinstDLL);
}
...
Quando si compila e si collega l'applicazione SampleDLL, il sistema operativo Windows cerca la DLL SampleDLL nelle seguenti posizioni in questo ordine:
La cartella dell'applicazione
La cartella corrente
La cartella di sistema di Windows (The GetSystemDirectory funzione restituisce il percorso della cartella di sistema di Windows).
La cartella Windows (The GetWindowsDirectory funzione restituisce il percorso della cartella Windows).