Statické knihovny snadno a dobře

Pokud již delší dobu používáme nějakou funkci (funkce), které linkujeme ke všem vytvářeným projektům ve formě zdrojových textů nebo ji kopírujeme z projektu do projektu, může se hodit statická knihovna, ve které je tato funkce obsažena. Převedením funkce do knihovny zajistíme snadnější opravu případných chyb a jednodušší vložení funkce do budoucích projektů.

Staticky linkované knihovny jsou alternativou k dynamicky linkovaným knihovnám. Mají své výhody i nevýhody. Musí se napevno linkovat k EXE/DLL souboru, při opravě chyby v knihovně je třeba znovu sestavit (a distribuovat celý EXE soubor). Na druhé straně pomůže při potřebě vytvořit aplikaci "se vším všudy" - tedy jeden EXE soubor, který obsahuje vše potřebné, není problém s verzemi DLL knihoven (DLL hell). V tomto článku se pokusím shrnout svoje zkušenosti a upozornit na úskalí při vytváření staticky linkovaných knihoven v prostředí MS Visual Studio 6.0.

Vytvoření projektu

Vytvoření projektu pro statickou knihovnu

Statickou knihovnu vytvoříme volbou "File\New ...". Na kartě "Projects" zvolíme "Win32 Static Library", zvolíme jméno knihovny, cílový adresář a odklepneme.

Do projektu vložíme všechny zdrojové texty, které potřebujeme a můžeme knihovnu vytvořit. Je vhodné vytvořit si hlavičkový soubor, který bude obsahovat deklarace všech veřejných funkcí, které knihovna exportuje a tento soubor nastavit do předkompilovaných hlaviček. Tento soubor bude použit i v projektech, které budou knihovnu používat. Doporučoval bych použít stejné jméno jako u knihovny, samozřejmě s příponou .h.

Po překompilování knihovny bude v adresáři Debug, resp. Release příslušná verze knihovny.

Problémy

K tomu, aby knihovna v projektu dobře fungovala, je potřeba dodržet několik pravidel:

#pragma comment(lib, "Version.lib")

Co to všechno obnáší

Stručné shrnutí předchozího odstavce napoví, že potřebujeme knihovnu v Debug a Release verzi, dále Ansi a Unicode verzi, a ještě verze pro statickou a vícethreadovou DLL verzi CRT knihovny (multithreadovou statickou verzi používat nebudeme). To je ale celkem 8 knihoven (2x2x2) ve všech kombinacích. Po vytvoření knihovny jsou v projektu pouze dvě verze (Debug a Release). Mně se osvědčilo odlišit verze písmeny na konci podle vzoru "KnihovnaXYZ.lib", kde

Např. KnihovnaDAS.lib je Debug verze používající ANSI řetězce a používá statickou verzi CRT.

Pro obě knihovny nastavíme předkompilované hlavičky (a připadně další volby) a použijeme je jako výchozí pro dvě "větve" knihoven. K vytvoření alternativní konfigurace použijeme volbu "Build\Configurations":

Z Debug verze knihovny vytvoříme následující verze:

Vytvoření alternativní konfigurace

Použití v programu

Představa že při vložení knihovny do projektu budete pátrat po tom, jakou verzi knihovny použijete, mnohé z čtenářů jistě zděsí. To raději použijeme přímo zdrojové texty, řeknete si. Mechanismus pro automatické vložení té správné verze přidáme do hlavičkového souboru ke knihovně. Je to sada příkazů "#ifdef", které zajistí vložení správné verze knihovny:

#ifdef _MSC_VER
  #ifdef _DEBUG                                 // DEBUG VERSIONS
    #ifndef _UNICODE                            
      #ifdef _DLL                               
        #pragma comment(lib, "UtilsDAD.lib")    // Debug Ansi Dynamic version
      #else        
        #pragma comment(lib, "UtilsDAS.lib")    // Debug Ansi Static version
      #endif
    #else
      #ifdef _DLL
        #pragma comment(lib, "UtilsDUD.lib")    // Debug Unicode Dynamic version
      #else        
        #pragma comment(lib, "UtilsDUS.lib")    // Debug Unicode Static version
      #endif
    #endif
  #else                                         // RELEASE VERSIONS
    #ifndef _UNICODE
      #ifdef _DLL
        #pragma comment(lib, "UtilsRAD.lib")    // Release Ansi Dynamic version
      #else        
        #pragma comment(lib, "UtilsRAS.lib")    // Release Ansi Static version
      #endif
    #else
      #ifdef _DLL
        #pragma comment(lib, "UtilsRUD.lib")    // Release Unicode Dynamic version
      #else        
        #pragma comment(lib, "UtilsRUS.lib")    // Release Unicode Static version
      #endif
    #endif
  #endif
#endif

Pro správnou funkčnost je třeba nastavit adresář, ve kterém budeme mít knihovny nebo zkopírovat všech 8 verzí do adresáře projektu. Osobně se kloním k první možnosti, která navíc umožňuje automatický update po sestavení nové verze knihovny (pokud do ní přidáte novou funkci nebo opravíte chybu v existující).

Možná zjednodušení

K předchozímu výkladu bych dodal, že není absolutně nezbytně nutné vytvářet všech 8 kombinací knihoven. Některé varianty je možné vypustit:

Jednoduchý příklad knihovny

Pro demonstraci, jak to celé vypadá, si můžete stáhnout příklad knihovny obsahující funkci VerifyUserPassword(), která ověří heslo uživatele na systému Windows řady NT. Knihovny sestavíte pomocí volby "Build\Batch Build". Dávkový soubor "PostBuild.bat" zajistí zkopírování hlavičkových souborů a zkompilovaných knihoven do příslušného adresáře.

Ukázková statická knihovna (26 KB)