Che cos'è una DLL

Questo articolo descrive che cos'è una libreria di collegamento dinamico (DLL) e quali sono i problemi che possono verificarsi quando si utilizzano le DLL. Descrive anche alcuni problemi più complessi da tenere in considerazione quando si sviluppano le DLL.

Si applica a: Windows 10 - tutte le edizioni
Numero originale della Knowledge Base: 815065

Riepilogo

Durante la descrizione delle DLL, vengono illustrati i metodi di collegamento dinamico, le dipendenze delle DLL, i punti di ingresso delle DLL, le funzioni DLL di esportazione e gli strumenti per la risoluzione dei problemi delle DLL.

L'articolo si conclude con un confronto di carattere generale tra le DLL e gli assembly di Microsoft .NET Framework.

Per Windows, gran parte delle funzionalità del sistema operativo è fornita dalla DLL. Inoltre, quando si esegue un programma in uno di questi sistemi operativi Windows, molte delle funzionalità del programma possono essere fornite tramite DLL. Alcuni programmi possono ad esempio contenere numerosi moduli diversi e ogni modulo del programma è contenuto e distribuito in DLL.

L'utilizzo delle DLL favorisce la modularizzazione del codice, il riutilizzo del codice, l'utilizzo efficiente della memoria e la riduzione dello spazio su disco. Quindi, il sistema operativo e i programmi vengono caricati ed eseguiti più velocemente e richiedono meno spazio su disco nel computer.

Quando un programma utilizza una DLL, un problema denominato dipendenza può impedire l'esecuzione del programma. Quando un programma utilizza una DLL, viene creata una dipendenza. Se un altro programma sovrascrive e interrompe questa dipendenza, potrebbe risultare impossibile eseguire il programma originale.

Con l'introduzione di Microsoft .NET Framework, la maggior parte dei problemi di dipendenza sono stati eliminati grazie all'utilizzo degli assembly.

Ulteriori informazioni

Una DLL è una libreria che contiene codice e dati utilizzabili da più di un programma contemporaneamente. Ad esempio, nei sistemi operativi Windows, la DLL Comdlg32 esegue funzioni comuni correlate alle finestre di dialogo. Ogni programma può utilizzare le funzionalità contenute in questa DLL per implementare una finestra di dialogo Apri. Questo favorisce il riutilizzo del codice e l'utilizzo efficiente della memoria.

Utilizzando una DLL, un programma può essere modularizzato in componenti distinti. Un programma di contabilità può ad esempio essere venduto in diversi moduli. Ogni modulo può essere caricato nel programma principale in fase di esecuzione, se il modulo è installato. Poiché i moduli sono separati, il tempo di caricamento del programma è più veloce. E un modulo viene caricato solo quando tale funzionalità viene richiesta.

Gli aggiornamenti possono inoltre essere applicati in modo più semplice a ogni modulo senza influire sulle altre parti del programma. Ad esempio, si potrebbe avere un programma per le retribuzioni in cui le aliquote fiscali cambiano ogni anno. Quando queste modifiche vengono isolate in una DLL, è possibile applicare un aggiornamento senza che sia necessario compilare o installare di nuovo l'intero programma.

Nell'elenco seguente sono descritti alcuni dei file implementati come DLL nei sistemi operativi Windows:

  • File di controlli ActiveX (con estensione ocx)

    Un esempio di controllo ActiveX è un controllo calendario che consente di selezionare una data dal calendario.

  • File del Pannello di controllo (con estensione cpl)

    Un esempio di file con estensione cpl è un elemento disponibile nel Pannello di controllo. Ogni elemento è una DLL specializzata.

  • File di driver di dispositivo (con estensione drv)

    Un esempio di driver di dispositivo è un driver di stampante che controlla la stampa su una stampante.

Vantaggi delle DLL

Nell'elenco seguente sono descritti alcuni dei vantaggi offerti dall'utilizzo di una DLL in un programma:

  • Riduzione dell'utilizzo delle risorse

    Quando più programmi utilizzano la stessa libreria di funzioni, una DLL può ridurre la duplicazione del codice caricato sul disco e nella memoria fisica. Questo può influire notevolmente sulle prestazioni non solo del programma eseguito in primo piano, ma anche di altri programmi in esecuzione nel sistema operativo Windows.

  • Promozione dell'architettura modulare

    Una DLL promuove lo sviluppo di programmi modulari. In questo modo, è possibile creare programmi di grandi dimensioni che richiedono versioni in più lingue oppure un programma che richiede un'architettura modulare. Un esempio di programma modulare è un programma di contabilità che dispone di molti moduli che possono essere caricati in modo dinamico in fase di esecuzione.

  • Semplicità di distribuzione e installazione

    Quando una funzione all'interno di una DLL richiede un aggiornamento o una correzione, per la distribuzione e l'installazione della DLL non è necessario ricollegare il programma alla DLL. Inoltre, se più programmi utilizzano la stessa DLL, tutti i programmi trarranno vantaggio dall'aggiornamento o dalla correzione. Questo problema può verificarsi più di frequente quando si utilizza una DLL di terze parti che viene aggiornata o corretta regolarmente.

Dipendenze delle DLL

Quando un programma o una DLL utilizza una funzione DLL in un'altra DLL, viene creata una dipendenza. Di conseguenza, il programma non è più indipendente e potrebbero verificarsi problemi se la dipendenza si interrompe. Potrebbe ad esempio risultare impossibile eseguire il programma in seguito a una delle azioni seguenti:

  • Una DLL dipendente viene aggiornata a una nuova versione.
  • Una DLL dipendente viene corretta.
  • Una DLL dipendente viene sovrascritta con una versione precedente.
  • Una DLL dipendente viene rimossa dal computer.

Queste azioni sono in genere definite conflitti di DLL. Se non si garantisce la compatibilità con le versioni precedenti, il programma potrebbe non essere eseguito correttamente.

Di seguito sono elencate le modifiche che sono state introdotte in Windows 2000 e nelle versioni successive dei sistemi operativi Windows per ridurre al minimo i problemi di dipendenza:

  • Protezione file Windows

    Con Protezione file Windows, il sistema operativo impedisce che le DLL di sistema vengano aggiornate o eliminate da un agente non autorizzato. Quando l'installazione di un programma tenta di rimuovere o aggiornare una DLL definita come DLL di sistema, Protezione file Windows richiederà una firma digitale valida.

  • DLL private

    Le DLL private consentono di isolare un programma dalle modifiche apportate alle DLL condivise. Le DLL private utilizzano informazioni specifiche della versione o un file con estensione .local vuoto per applicare la versione della DLL che viene utilizzata dal programma. Per utilizzare DLL private, individuare le DLL nella cartella radice del programma. Per i nuovi programmi, aggiungere informazioni specifiche della versione alla DLL. Per i vecchi programmi, utilizzare un file con estensione .local vuoto. Ognuno di questi metodi indica al sistema operativo di utilizzare le DLL private disponibili nella cartella radice del programma.

Strumenti di risoluzione dei problemi delle DLL

Sono disponibili diversi strumenti per la risoluzione dei problemi delle DLL. Alcuni di questi strumenti sono descritti di seguito.

Dependency Walker

Lo strumento Dependency Walker analizza in modo ricorsivo tutte le DLL dipendenti che sono utilizzate da un programma. Quando si apre un programma in Dependency Walker, questo effettua i controlli seguenti:

  • Controlla le DLL mancanti.
  • Controlla i file di programma o le DLL non validi.
  • Controlla la corrispondenza tra le funzioni di importazione ed esportazione.
  • Controlla gli errori di dipendenza circolare.
  • Controlla i moduli non validi perché destinati a un diverso sistema operativo.

Utilizzando Dependency Walker, è possibile documentare tutte le DLL utilizzate da un programma. Ciò consente di prevenire e correggere i problemi delle DLL che possono verificarsi in futuro. Dopo avere installato Microsoft Visual Studio 6.0, Dependency Walker è disponibile nella seguente directory:

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLL Universal Problem Solver

Lo strumento DLL Universal Problem Solver (DUPS) viene utilizzato per controllare, confrontare, documentare e visualizzare le informazioni sulle DLL. Di seguito sono elencate le utilità che costituiscono lo strumento DUPS:

  • Dlister.exe

    Questa utilità enumera tutte le DLL nel computer e registra le informazioni in un file di testo o in un file di database.

  • Dcomp.exe

    Questa utilità confronta le DLL elencate in due file di testo e produce un terzo file di testo che contiene le differenze.

  • Dtxt2DB.exe

    Questa utilità carica i file di testo creati tramite le utilità Dlister.exe e Dcomp.exe nel database dllHell.

  • DlgDtxt2DB.exe

    Questa utilità fornisce una versione con interfaccia grafica dell'utilità Dtxt2DB.exe.

Database DLL Help

Il database DLL Help consente di individuare le versioni specifiche delle DLL installate dai prodotti software Microsoft.

Sviluppo di DLL

In questa sezione vengono descritti i problemi e i requisiti da tenere presente durante lo sviluppo di 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 il collegamento dinamico in fase di caricamento e il 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 funzioni locali. Per utilizzare il collegamento dinamico in fase di caricamento, fornire un file di intestazione (con estensione h) e un file di libreria di importazione (con estensione lib) durante la compilazione e il collegamento dell'applicazione. Quando si esegue questa operazione, il linker fornirà al sistema le informazioni necessarie per caricare la DLL e risolvere i percorsi delle funzioni DLL esportate in fase di caricamento.

Collegamento dinamico in fase di esecuzione

Nel collegamento dinamico in fase di esecuzione, un'applicazione chiama la funzione LoadLibrary o la funzione LoadLibraryEx per caricare la DLL in fase di esecuzione. Dopo avere caricato la DLL, utilizzare la funzione GetProcAddress per ottenere l'indirizzo della funzione DLL esportata che si desidera chiamare. Quando si utilizza il collegamento dinamico in fase di esecuzione, non è necessario un file di libreria di importazione.

Di seguito sono elencati i criteri relativi alle applicazioni per determinare quando utilizzare il collegamento dinamico in fase di caricamento e quando utilizzare il collegamento dinamico in fase di esecuzione:

  • Prestazioni di avvio

    Se le prestazioni di avvio iniziale dell'applicazione sono importanti, è necessario utilizzare il collegamento dinamico in fase di esecuzione.

  • Facilità di utilizzo

    Nel collegamento dinamico in fase di caricamento, le funzioni DLL esportate sono come funzioni locali. Questo semplifica la chiamata di tali funzioni.

  • Logica dell'applicazione

    Nel collegamento dinamico in fase di esecuzione, un'applicazione può caricare differenti moduli in base alle esigenze. È un aspetto importante quando si sviluppano versioni multilingue.

Punto di ingresso della DLL

Quando si crea una DLL, è possibile specificare 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 strutture di dati o eliminare definitivamente strutture di dati come richiesto dalla DLL. Inoltre, se l'applicazione è multithread, è possibile utilizzare l'archiviazione locale dei thread per allocare memoria privata a ciascun thread nella funzione del punto di ingresso. Il codice riportato di seguito è un esempio di funzione del punto di ingresso DLL.

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
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 il valore FALSE, se si utilizza il collegamento dinamico in fase di caricamento, l'applicazione non verrà avviata. Se si utilizza il collegamento dinamico in fase di esecuzione, non verrà caricata solo la singola DLL.

La funzione del punto di ingresso deve eseguire solo semplici attività di inizializzazione e non deve chiamare altre funzioni di caricamento o di terminazione di DLL. Nella funzione del punto di ingresso, ad esempio, non è possibile chiamare direttamente o indirettamente la funzione LoadLibrary o la funzione LoadLibraryEx. Inoltre, non è necessario chiamare la funzione FreeLibrary quando il processo è terminato.

Nota

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 l'archiviazione locale dei thread per fornire dati univoci per ciascun thread.

Esportare le funzioni DLL

Per esportare le funzioni DLL, è possibile aggiungere una parola chiave function alle funzioni DLL esportate o creare un file di definizione di modulo (con estensione def) in cui sono elencate le funzioni DLL esportate.

Per utilizzare una parola chiave function, è necessario dichiarare ogni funzione che si desidera esportare con la parola chiave seguente:
__declspec(dllexport)

Per utilizzare le funzioni DLL esportate nell'applicazione, è necessario indicare ogni funzione che si desidera importare con la seguente parola chiave: __declspec(dllimport)

In genere si utilizza un file di intestazione con un'istruzione define e un'istruzione ifdef per separare l'istruzione export e l'istruzione import.

È anche possibile utilizzare un file di definizione di modulo per dichiarare le funzioni DLL esportate. Quando si utilizza un file di definizione di modulo, non è necessario aggiungere la parola chiave function alle funzioni DLL esportate. Nel file di definizione del modulo vengono dichiarate l'istruzione LIBRARY e l'istruzione EXPORTS per la DLL. Il codice riportato di seguito è un esempio di file di definizione.

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

DLL e applicazioni di esempio

In Microsoft Visual C++ 6.0 è possibile creare una DLL selezionando il tipo di progetto Win32 Dynamic-Link Library o il tipo di progetto MFC AppWizard (dll).

Il codice riportato di seguito è un esempio di 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

Il codice riportato di seguito è un esempio di progetto Win32 Application 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;
}

Nota

Nel collegamento dinamico in fase di caricamento è necessario collegare la libreria di importazione SampleDLL.lib che viene creata quando si compila il progetto SampleDLL.

Nel collegamento dinamico in fase di esecuzione utilizzare codice simile al 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 nei percorsi seguenti nell'ordine indicato:

  1. La cartella dell'applicazione

  2. La cartella corrente

  3. La cartella di sistema di Windows

    Nota

    La funzione GetSystemDirectory restituisce il percorso della cartella di sistema di Windows.

  4. La cartella di Windows

    Nota

    La funzione GetWindowsDirectory restituisce il percorso della cartella di Windows.

Assembly di .NET Framework

Con l'introduzione di Microsoft .NET e .NET Framework, la maggior parte dei problemi associati alle DLL è stata eliminata utilizzando gli assembly. Un assembly è un'unità logica di funzionalità che viene eseguita sotto il controllo di .NET Common Language Runtime (CLR). Un assembly si presenta fisicamente come un file con estensione dll o exe. Tuttavia, internamente un assembly è diverso da una DLL Microsoft Win32.

Un file di assembly contiene un manifesto dell'assembly, metadati di tipo, codice Microsoft Intermediate Language (MSIL) e altre risorse. Il manifesto dell'assembly contiene i metadati dell'assembly che forniscono tutte le informazioni necessarie perché un assembly sia autodescrittivo. Le informazioni seguenti sono incluse nel manifesto dell'assembly:

  • Nome dell'assembly
  • Informazioni sulla versione
  • Informazioni sulle impostazioni cultura
  • Informazioni sul nome sicuro
  • Elenco dei file dell'assembly
  • Informazioni sul tipo di riferimento
  • Informazioni sugli assembly a cui viene fatto riferimento e dipendenti

Il codice MSIL contenuto nell'assembly non può essere eseguito direttamente. L'esecuzione del codice MSIL viene invece gestita tramite CLR. Per impostazione predefinita, quando si crea un assembly, l'assembly è privato per l'applicazione. Per creare un assembly condiviso, è necessario assegnare un nome sicuro all'assembly e quindi pubblicare l'assembly nella Global Assembly Cache.

Di seguito sono descritte alcune delle caratteristiche degli assembly, confrontandole con quelle delle DLL Win32:

  • Natura autodescrittiva

    Quando si crea un assembly, tutte le informazioni necessarie a CLR per eseguire l'assembly sono contenute nel manifesto dell'assembly. Il manifesto dell'assembly contiene un elenco degli assembly dipendenti. CLR può pertanto gestire un set coerente degli assembly utilizzati nell'applicazione. Nelle DLL Win32 non è possibile mantenere la coerenza tra un set di DLL che vengono utilizzate in un'applicazione quando si utilizzano DLL condivise.

  • Controllo delle versioni

    Nel manifesto di un assembly le informazioni sulla versione vengono registrate e applicate da CLR. I criteri di versione, inoltre, consentono di imporre l'utilizzo di specifiche versioni. Nelle DLL Win32 il controllo delle versioni non può essere applicato dal sistema operativo. È necessario assicurarsi che le DLL siano compatibili con le versioni precedenti.

  • Distribuzione side-by-side

    Gli assembly supportano la distribuzione side-by-side. Un'applicazione può utilizzare una versione di un assembly, mentre un'altra applicazione può utilizzare una versione diversa di un assembly. A partire da Windows 2000, la distribuzione side-by-side è supportata individuando le DLL nella cartella dell'applicazione. Protezione file Windows, inoltre, impedisce che le DLL di sistema vengano sovrascritte o sostituite da un agente non autorizzato.

  • Indipendenza e isolamento

    Un'applicazione sviluppata tramite un assembly può essere indipendente e isolata dalle altre applicazioni in esecuzione nel computer Questa funzionalità consente di creare installazioni a impatto zero.

  • Esecuzione

    Un assembly viene eseguito con le autorizzazioni di sicurezza che vengono fornite nel manifesto dell'assembly e che sono controllate da CLR.

  • Indipendenza dal linguaggio

    Un assembly può essere sviluppato utilizzando uno qualsiasi dei linguaggi .NET supportati. È ad esempio possibile sviluppare un assembly in Microsoft Visual C# e quindi utilizzare l'assembly in un progetto Visual Basic .NET

Raccolta dei dati

Se è necessaria l'assistenza del supporto tecnico Microsoft, si consiglia di raccogliere le informazioni seguendo i passaggi indicati in Raccogliere informazioni utilizzando TSS per le questioni relative alla distribuzione.

Riferimenti