Dela via


Länka en körbar fil till en DLL

En körbar fil länkar till (eller läser in) en DLL på något av två sätt:

  • Implicit länkning, där operativsystemet läser in DLL-filen samtidigt som den körbara filen används. Den körbara klienten anropar de exporterade funktionerna i DLL på samma sätt som om funktionerna var statiskt länkade och inneslutna i den körbara filen. Implicit länkning kallas ibland för statisk belastning eller dynamisk inläsningstidslänkning.

  • Explicit länkning, där operativsystemet läser in DLL på begäran vid körning. En körbar fil som använder en DLL genom explicit länkning måste uttryckligen läsa in och ta bort DLL:en. Den måste också konfigurera en funktionspekare för att komma åt varje funktion som den använder från DLL:en. Till skillnad från anrop till funktioner i ett statiskt länkat bibliotek eller en implicit länkad DLL måste den körbara klienten anropa de exporterade funktionerna i en explicit länkad DLL via funktionspekare. Explicit länkning kallas ibland dynamisk belastning eller dynamisk körningslänkning.

En körbar fil kan använda endera länkningsmetoden för att länka till samma DLL. Dessutom är dessa metoder inte ömsesidigt uteslutande; en körbar fil kan implicit länka till en DLL och en annan kan kopplas till den explicit.

Avgöra vilken länkningsmetod som ska användas

Om du vill använda implicit länkning eller explicit länkning är ett arkitektoniskt beslut som du måste fatta för ditt program. Det finns fördelar och nackdelar med varje metod.

Implicit länkning

Implicit länkning sker när ett programs kod anropar en exporterad DLL-funktion. När källkoden för den körbara anropande filen kompileras eller monteras genererar DLL-funktionsanropet en extern funktionsreferens i objektkoden. För att lösa den här externa referensen måste programmet länka till importbiblioteket (.lib-filen) som tillhandahålls av DLL-tillverkaren.

Importbiblioteket innehåller bara kod för att läsa in DLL och implementera anrop till funktioner i DLL-filen. Att hitta en extern funktion i ett importbibliotek informerar länkaren om att koden för den funktionen finns i en DLL. För att lösa externa referenser till DLL:er lägger länkaren helt enkelt till information i den körbara filen som talar om för systemet var DLL-koden ska hittas när processen startas.

När systemet startar ett program som innehåller dynamiskt länkade referenser använder det informationen i programmets körbara fil för att hitta nödvändiga DLL:er. Om den inte kan hitta DLL-filen avslutar systemet processen och visar en dialogruta som rapporterar felet. Annars mappar systemet DLL-modulerna till processadressutrymmet.

Om någon av DLL:erna har en startpunktsfunktion för initierings- och avslutningskod, till exempel DllMain, anropar operativsystemet funktionen. En av parametrarna som skickas till startpunktsfunktionen anger en kod som anger att DLL:en ansluter till processen. Om startpunktsfunktionen inte returnerar TRUE avslutar systemet processen och rapporterar felet.

Slutligen ändrar systemet den körbara koden för processen för att tillhandahålla startadresser för DLL-funktionerna.

Precis som i resten av ett programs kod mappar inläsaren DLL-kod till processens adressutrymme när processen startas. Operativsystemet läser bara in det i minnet när det behövs. Därför har kodattributen PRELOAD och LOADONCALL som används av .def-filer för att styra inläsning i tidigare versioner av Windows inte längre betydelse.

Explicit länkning

De flesta program använder implicit länkning eftersom det är den enklaste länkningsmetoden att använda. Det finns dock tillfällen då explicit länkning krävs. Här är några vanliga orsaker till att använda explicit länkning:

  • Programmet vet inte namnet på en DLL som det läser in förrän körningstiden. Programmet kan till exempel hämta namnet på DLL och de exporterade funktionerna från en konfigurationsfil vid start.

  • En process som använder implicit länkning avslutas av operativsystemet om DLL-filen inte hittas vid processens start. En process som använder explicit länkning avslutas inte i den här situationen och kan försöka återställa från felet. Processen kan till exempel meddela användaren om felet och låta användaren ange en annan sökväg till DLL-filen.

  • En process som använder implicit länkning avslutas också om någon av de DLL:er som den är länkad till har en DllMain funktion som misslyckas. En process som använder explicit länkning avslutas inte i den här situationen.

  • Ett program som implicit länkar till många DLL:er kan vara långsamt att starta eftersom Windows läser in alla DLL:er när programmet läses in. För att förbättra startprestanda kan ett program bara använda implicit länkning för DLL:er som krävs omedelbart efter inläsningen. Den kan använda explicit länkning för att endast läsa in andra DLL:er när de behövs.

  • Explicit länkning eliminerar behovet av att länka programmet med hjälp av ett importbibliotek. Om ändringar i DLL:en gör att exportordningarna ändras behöver programmen inte länka om om de anropar GetProcAddress med namnet på en funktion och inte ett ordningstal. Program som använder implicit länkning måste fortfarande länkas om till det ändrade importbiblioteket.

Här är två faror med explicit länkning att vara medveten om:

  • Om DLL:en har en DllMain startpunktsfunktion anropar operativsystemet funktionen i kontexten för tråden som heter LoadLibrary. Startpunktsfunktionen anropas inte om DLL-filen redan är kopplad till processen på grund av ett tidigare anrop till LoadLibrary som inte har haft något motsvarande anrop till FreeLibrary funktionen. Explicit länkning kan orsaka problem om DLL:en använder en DllMain funktion för att initiera varje tråd i en process, eftersom alla trådar som redan finns när LoadLibrary (eller AfxLoadLibrary) anropas inte initieras.

  • Om en DLL deklarerar statiska data som __declspec(thread)kan det orsaka ett skyddsfel om de uttryckligen länkas. När DLL:en har lästs in av ett anrop till LoadLibraryorsakar den ett skyddsfel när koden refererar till dessa data. (Statiska data omfattar både globala och lokala statiska objekt.) Därför bör du undvika att använda trådlokal lagring när du skapar en DLL. Om du inte kan kan du informera dina DLL-användare om de potentiella fallgroparna med dynamisk inläsning av din DLL. Mer information finns i Using thread local storage in a dynamic-link library (Windows SDK).

Så här använder du implicit länkning

Om du vill använda en DLL genom implicit länkning måste körbara klientfiler hämta dessa filer från providern för DLL:en:

  • En eller flera huvudfiler (.h-filer) som innehåller deklarationer av exporterade data, funktioner och C++-klasser i DLL-filen. Alla klasser, funktioner och data som exporteras av DLL:en måste markeras __declspec(dllimport) i huvudfilen. Mer information finns i dllexport, dllimport.

  • Ett importbibliotek som ska länkas till den körbara filen. Länkaren skapar importbiblioteket när DLL-filen skapas. Mer information finns i LIB-filer som indata för länkare.

  • Den faktiska DLL-filen.

Om du vill använda data, funktioner och klasser i en DLL genom implicit länkning måste alla klientkällfiler innehålla huvudfilerna som deklarerar dem. Från ett kodningsperspektiv är anrop till de exporterade funktionerna precis som andra funktionsanrop.

Om du vill skapa den körbara klientens fil måste du länka till DLL:ns importbibliotek. Om du använder en extern makefile eller ett byggsystem anger du importbiblioteket tillsammans med de andra objektfilerna eller biblioteken som du länkar.

Operativsystemet måste kunna hitta DLL-filen när den läser in den körbara anropet. Det innebär att du antingen måste distribuera eller verifiera att DLL-filen finns när du installerar programmet.

Om du vill använda en DLL genom explicit länkning måste program göra ett funktionsanrop för att uttryckligen läsa in DLL:en vid körning. För att uttryckligen länka till en DLL måste ett program:

  • Anropa LoadLibraryEx eller en liknande funktion för att läsa in DLL och hämta en modulreferens.

  • Anropa GetProcAddress för att hämta en funktionspekare till varje exporterad funktion som programmet anropar. Eftersom program anropar DLL-funktionerna via en pekare genererar kompilatorn inte externa referenser, så det finns ingen anledning att länka till ett importbibliotek. Du måste dock ha en typedef -instruktion som using definierar anropssignaturen för de exporterade funktioner som du anropar.

  • Anropa FreeLibrary när du är klar med DLL:en.

Exempelfunktionen anropar LoadLibrary till exempel för att läsa in en DLL med namnet "MyDLL", anropar GetProcAddress för att hämta en pekare till en funktion med namnet "DLLFunc1", anropar funktionen och sparar resultatet och anropar FreeLibrary sedan för att ta bort DLL:en.

#include "windows.h"

typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);

HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
    HINSTANCE hDLL;               // Handle to DLL
    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
    HRESULT hrReturnVal;

    hDLL = LoadLibrary("MyDLL");
    if (NULL != hDLL)
    {
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
        if (NULL != lpfnDllFunc1)
        {
            // call the function
            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
        }
        else
        {
            // report the error
            hrReturnVal = ERROR_DELAY_LOAD_FAILED;
        }
        FreeLibrary(hDLL);
    }
    else
    {
        hrReturnVal = ERROR_DELAY_LOAD_FAILED;
    }
    return hrReturnVal;
}

Till skillnad från det här exemplet bör du i de flesta fall bara anropa LoadLibrary och FreeLibrary bara en gång i ditt program för en viss DLL. Det gäller särskilt om du ska anropa flera funktioner i DLL eller anropa DLL-funktioner upprepade gånger.

Vad vill du veta mer om?

Se även

Skapa DLL:er för C/C++ i Visual Studio