Programujeme pro PocketPC: instalační DLL knihovna
21.5.2004, eXEden, článek
V článku o tvorbě instalačních CAB souborů jsme se dozvěděli, že instalaci můžeme řídit. Řízení instalace nám obstarává speciální DLL knihovna, která je volaná během instalace a která může být interaktivním prostředníkem mezi uživatelem a instalovaným programem. V tomto článku bych se tedy ještě jednou rád vrátil k problematice tvorby a distribuce instalací a nastínil základní principy tvorby instalačních DLL knihoven.
Instalace můžeme z hlediska interaktivnosti rozdělit na automatické, která nevyžadují žádný zásah a poloautomatické (či manuální), které zásah uživatele vyžadují. O tvorbě instalace ve formě klasických CAB souborů byl pojednán předchozí článek. Ve formě, jak jsem ji prezentoval, se jedná o instalaci automatickou, která aplikaci pouze nainstaluje, provede nezbytné změny v registrech a ukončí se. Ne vždy je ovšem taková instalace žádoucí. V mnohých případech je požadován vstup uživatele ve smyslu potvrzení nastavení, volby další operace, apod. O způsobu, jak zprostředkovat tento vstup, pojednává právě tento článek, který vám osvětlí základy tvorby instalačních DLL knihoven, jež jsou tím můstkem mezi uživatelem a instalací.
V článku o tvorbě instalačních CAB souborů byla ve struktuře INF souboru v sekci DefaultInstall zmíněna podsekce CESetupDLL. Jejím účelem je definice DLL knihovny, která bude volaná během instalace podle vzoru:
[DefaultInstall]
Copyfiles=copyfile_list_section[,copyfile_list_section]
AddReg=add_registry_section[,add_registry_section]
[CEShortcuts=shortcut_list_section[,shortcut_list_section]]
[CESetupDLL=setup_DLL]
[CESelfRegister=self_reg_DLL_filename[,self_reg_DLL_filename]
Budeme pokračovat v tom samém příkladě a rozšíříme jej o knihovnu s názvem SetupTemplate.dll, která bude uložena v našem projektu ve složce SetupDLL. Náš INF soubor (resp. upravená část) bude potom vypadat následovně:
[DefaultInstall]
CopyFiles = CopyLinks,CopyToWindows
AddReg = RegSettings
CESetupDLL = SetupTemplate.dll
[SourceDisksNames]
1 = ,"A1",,A1
2 = ,"A2",,A2
3 = ,"SetupDLL",,SetupDLL
[SourceDisksFiles]
CAB Install.lnk = 1
Database Explorer.lnk = 1
Aliens.tsk = 2
SetupTemplate.dll = 3
Tímto si zabezpečíme, že knihovna SetupTemplate.dll bude automaticky volaná během instalačního procesu a bude-li vytvořená podle jistých pravidel, bude zprostředkovávat interakci mezi uživatelem a instalací. Úkolem této knihovny bude zobrazovat pouze jednoduché dotazy tak, aby byla interakce vidět. Příklady praktičtějšího a složitějšího použití vám přinese až praxe. Celý projekt pro vytvoření CAB souboru si můžete stáhnout zde (87,05 KB) . V následujících částech se seznámíme se strukturou volání instalační DLL knihovny a potřebnými exportními funkcemi.
Jak jsem se již zmínil, instalační knihovna je volaná automaticky správcem aplikací. K volání dochází na 4 místech:
Každému místu patří volání jedné funkce, která je z DLL exportovaná a kterou se správce aplikací automaticky snaží volat. Rozeberme si jednotlivá místa jedno po druhém.
Před započetím samotné instalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. nezbytné kontroly, připravit si dočasná úložiště souborů apod. Správce aplikací volá exportovanou funkci, jejiž předpis je následující:
enum codeINSTALL_INIT
{
codeINSTALL_INIT_CONTINUE = 0,
codeINSTALL_INIT_CANCEL
};
extern "C" codeINSTALL_INIT Install_Init(HWND hpar, BOOL fFirstCall, BOOL fPreviouslyInstalled, LPCTSTR pszInstallDir);
Parametry této funkce jsou následující:
Má-li instalace pokračovat dále, musí funkce vrátit hodnotu codeINSTALL_INIT_CONTINUE. Jiná návratová hodnota způsobí ukončení instalace.
Na závěr instalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. ošetření chyb (ke kterým došlo během instalace), spuštění aplikace, apod. Správce aplikací volá exportovanou funkci, jejíž předpis je následující:
enum codeINSTALL_EXIT
{
codeINSTALL_EXIT_DONE = 0,
codeINSTALL_EXIT_CANCEL
};
extern "C" codeINSTALL_EXIT Install_Exit(HWND hpar, LPCTSTR pszChosenInstallDir,
WORD cFailedDirs, WORD cFailedFiles, WORD cFailedRegKeys,
WORD cFailedRegVals, WORD cFailedShortcuts);
Parametry této funkce jsou následující:
Má-li instalace skončit úspěšně, musí funkce vrátit hodnotu codeINSTALL_EXIT_DONE. Jiná návratová hodnota způsobí přerušení instalace a odinstalování již nainstalovaných součástí.
Před započetím odinstalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. ukončení běhu aplikace jež se má odinstalovat, apod. Správce aplikací volá exportovanou funkci, jejíž předpis je následující:
enum codeINSTALL_INIT
{
codeUNINSTALL_INIT_CONTINUE = 0,
codeUNINSTALL_INIT_CANCEL
};
extern "C" codeUNINSTALL_INIT Uninstall_Init(HWND hpar, LPCTSTR pszInstallDir);
Parametry této funkce jsou následující:
Má-li odinstalace pokračovat dále, musí funkce vrátit hodnotu codeUNINSTALL_INIT_CONTINUE. Jiná návratová hodnota způsobí ukončení odinstalace.
Na závěr odinstalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. odstranění databází, které aplikace používala, apod. Správce aplikací volá exportovanou funkci, jejiž předpis je následující:
enum codeUNINSTALL_EXIT
{
codeUNINSTALL_EXIT_DONE = 0
};
extern "C" codeUNINSTALL_EXIT Uninstall_Exit(HWND hpar);
Parametry této funkce jsou následující:
Návratovou hodnotou je vždy codeUNINSTALL_EXIT_DONE.
Funkce je nutné exportovat s direktivou extern "C" tak, aby kompilátor jazyka eVC++ zachoval jejich jména. Správce aplikací si všechny funkce importuje podle jména a neuvedení této direktivy by mohlo způsobit nefunkčnost instalační knihovny.
Obecně platí, že DLL knihovny můžeme s pomocí eVC++ psát dvěma způsoby:
1. Bez použití MFC - klasická DLL knihovna využívající pouze Win32 API
2. S použitím MFC - tzv. Regular DLL library using shared/static MFC, v níž můžeme využívat všech aspektů MFC. Jedinou podmínkou je použití makra AFX_MANAGE_STATE na začátku každé exportované funkce, která MFC používá.
Pro náš příklad jsem zvolil variantu s použitím MFC a podle výše napsaného bude pouze zobrazovat hlášení a dotazy tak, aby byla vidět interaktivnost. Ukázkový eVC projekt (16,35 KB) obsahuje zdrojový kód (SetupTemplate.cpp), jehož výsek je dostatečně názorný a osvětluje použití výše zmíněných funkcí:
extern "C" codeINSTALL_INIT Install_Init(HWND hpar, BOOL fFirstCall, BOOL fPreviouslyInstalled, LPCTSTR pszSuggestedInstallDir)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CSetupTemplateDlg dlg;
if (!dlg.DoModal())
return codeINSTALL_INIT_CANCEL;
return codeINSTALL_INIT_CONTINUE;
}
extern "C" codeINSTALL_EXIT Install_Exit(HWND hpar, LPCTSTR pszChosenInstallDir,
WORD cFailedDirs, WORD cFailedFiles, WORD cFailedRegKeys,
WORD cFailedRegVals, WORD cFailedShortcuts)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(_T("Instalace proběhla úspěšně."), MB_OK | MB_ICONINFORMATION);
return codeINSTALL_EXIT_DONE;
}
extern "C" codeUNINSTALL_INIT Uninstall_Init(HWND hpar, LPCTSTR pszInstallDir)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (AfxMessageBox(_T("Opravdu chcete odinstalovat tento program?"), MB_YESNO | MB_ICONQUESTION) == IDNO)
return codeUNINSTALL_INIT_CANCEL;
return codeUNINSTALL_INIT_CONTINUE;
}
extern "C" codeUNINSTALL_EXIT Uninstall_Exit(HWND hpar)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(_T("Odinstalace proběhla úspěšně."), MB_OK | MB_ICONINFORMATION);
return codeUNINSTALL_EXIT_DONE;
}
Problematika tvorby instalačních DLL knihoven není vůbec složitá. Pro plnou funkcionalitu stačí jednoduchá knihovna exportující 4 funkce podle daného předpisu. Kód, který vykonávají, záleží plně na typu aplikace, která je instalována a bude se lišit aplikace od aplikace. Pomocí takových knihoven jsme schopni vytvořit opravdu kvalitní a plnohodnotné instalační sady, které zajistí správný chod aplikace okamžitě po jejím nainstalování. Tento článek neměl být podrobným průvodcem, ale snažil se nastínit možnosti a principy tvorby instalačních knihoven a tento úkol snad splnil.
Připomenutí
V článku o tvorbě instalačních CAB souborů byla ve struktuře INF souboru v sekci DefaultInstall zmíněna podsekce CESetupDLL. Jejím účelem je definice DLL knihovny, která bude volaná během instalace podle vzoru:
[DefaultInstall]
Copyfiles=copyfile_list_section[,copyfile_list_section]
AddReg=add_registry_section[,add_registry_section]
[CEShortcuts=shortcut_list_section[,shortcut_list_section]]
[CESetupDLL=setup_DLL]
[CESelfRegister=self_reg_DLL_filename[,self_reg_DLL_filename]
Budeme pokračovat v tom samém příkladě a rozšíříme jej o knihovnu s názvem SetupTemplate.dll, která bude uložena v našem projektu ve složce SetupDLL. Náš INF soubor (resp. upravená část) bude potom vypadat následovně:
[DefaultInstall]
CopyFiles = CopyLinks,CopyToWindows
AddReg = RegSettings
CESetupDLL = SetupTemplate.dll
[SourceDisksNames]
1 = ,"A1",,A1
2 = ,"A2",,A2
3 = ,"SetupDLL",,SetupDLL
[SourceDisksFiles]
CAB Install.lnk = 1
Database Explorer.lnk = 1
Aliens.tsk = 2
SetupTemplate.dll = 3
Tímto si zabezpečíme, že knihovna SetupTemplate.dll bude automaticky volaná během instalačního procesu a bude-li vytvořená podle jistých pravidel, bude zprostředkovávat interakci mezi uživatelem a instalací. Úkolem této knihovny bude zobrazovat pouze jednoduché dotazy tak, aby byla interakce vidět. Příklady praktičtějšího a složitějšího použití vám přinese až praxe. Celý projekt pro vytvoření CAB souboru si můžete stáhnout zde (87,05 KB) . V následujících částech se seznámíme se strukturou volání instalační DLL knihovny a potřebnými exportními funkcemi.
Struktura instalační DLL knihovny
Jak jsem se již zmínil, instalační knihovna je volaná automaticky správcem aplikací. K volání dochází na 4 místech:
- Instalace - úvod
- Instalace - závěr
- Odinstalace - úvod
- Odinstalace - závěr
Každému místu patří volání jedné funkce, která je z DLL exportovaná a kterou se správce aplikací automaticky snaží volat. Rozeberme si jednotlivá místa jedno po druhém.
Instalace - úvod
Před započetím samotné instalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. nezbytné kontroly, připravit si dočasná úložiště souborů apod. Správce aplikací volá exportovanou funkci, jejiž předpis je následující:
enum codeINSTALL_INIT
{
codeINSTALL_INIT_CONTINUE = 0,
codeINSTALL_INIT_CANCEL
};
extern "C" codeINSTALL_INIT Install_Init(HWND hpar, BOOL fFirstCall, BOOL fPreviouslyInstalled, LPCTSTR pszInstallDir);
Parametry této funkce jsou následující:
- hpar - handle rodičovského okna
- fFirstCall - indikace prvního volání této funkce
- fPreviouslyInstalled - příznak existující předchozí instalace právě instalované aplikace
- pszInstallDir - instalační adresář
Má-li instalace pokračovat dále, musí funkce vrátit hodnotu codeINSTALL_INIT_CONTINUE. Jiná návratová hodnota způsobí ukončení instalace.
Instalace - závěr
Na závěr instalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. ošetření chyb (ke kterým došlo během instalace), spuštění aplikace, apod. Správce aplikací volá exportovanou funkci, jejíž předpis je následující:
enum codeINSTALL_EXIT
{
codeINSTALL_EXIT_DONE = 0,
codeINSTALL_EXIT_CANCEL
};
extern "C" codeINSTALL_EXIT Install_Exit(HWND hpar, LPCTSTR pszChosenInstallDir,
WORD cFailedDirs, WORD cFailedFiles, WORD cFailedRegKeys,
WORD cFailedRegVals, WORD cFailedShortcuts);
Parametry této funkce jsou následující:
- hpar - handle rodičovského okna
- pszChosenInstallDir - instalační adresář
- cFailedDirs - počet adresářů, které se nepodařilo vytvořit
- cFailedFiles - počet souborů, které se nepodařilo zkopírovat
- cFailedRegKeys - počet klíčů, které se nepodařilo vytvořit v registrech
- cFailedRegVals - počet hodnot, které se nepodařilo do registrů zapsat
- cFailedShortcuts - počet zástupců, kteří nebyli vytvořeni
Má-li instalace skončit úspěšně, musí funkce vrátit hodnotu codeINSTALL_EXIT_DONE. Jiná návratová hodnota způsobí přerušení instalace a odinstalování již nainstalovaných součástí.
Odinstalace - úvod
Před započetím odinstalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. ukončení běhu aplikace jež se má odinstalovat, apod. Správce aplikací volá exportovanou funkci, jejíž předpis je následující:
enum codeINSTALL_INIT
{
codeUNINSTALL_INIT_CONTINUE = 0,
codeUNINSTALL_INIT_CANCEL
};
extern "C" codeUNINSTALL_INIT Uninstall_Init(HWND hpar, LPCTSTR pszInstallDir);
Parametry této funkce jsou následující:
- hpar - handle rodičovského okna
- pszInstallDir - instalační adresář
Má-li odinstalace pokračovat dále, musí funkce vrátit hodnotu codeUNINSTALL_INIT_CONTINUE. Jiná návratová hodnota způsobí ukončení odinstalace.
Odinstalace - závěr
Na závěr odinstalace správce aplikací předává řízení instalační knihovně tak, aby mohla provést např. odstranění databází, které aplikace používala, apod. Správce aplikací volá exportovanou funkci, jejiž předpis je následující:
enum codeUNINSTALL_EXIT
{
codeUNINSTALL_EXIT_DONE = 0
};
extern "C" codeUNINSTALL_EXIT Uninstall_Exit(HWND hpar);
Parametry této funkce jsou následující:
- hpar - handle rodičovského okna
Návratovou hodnotou je vždy codeUNINSTALL_EXIT_DONE.
Poznámky
Funkce je nutné exportovat s direktivou extern "C" tak, aby kompilátor jazyka eVC++ zachoval jejich jména. Správce aplikací si všechny funkce importuje podle jména a neuvedení této direktivy by mohlo způsobit nefunkčnost instalační knihovny.
Příklad
Obecně platí, že DLL knihovny můžeme s pomocí eVC++ psát dvěma způsoby:
1. Bez použití MFC - klasická DLL knihovna využívající pouze Win32 API
2. S použitím MFC - tzv. Regular DLL library using shared/static MFC, v níž můžeme využívat všech aspektů MFC. Jedinou podmínkou je použití makra AFX_MANAGE_STATE na začátku každé exportované funkce, která MFC používá.
Pro náš příklad jsem zvolil variantu s použitím MFC a podle výše napsaného bude pouze zobrazovat hlášení a dotazy tak, aby byla vidět interaktivnost. Ukázkový eVC projekt (16,35 KB) obsahuje zdrojový kód (SetupTemplate.cpp), jehož výsek je dostatečně názorný a osvětluje použití výše zmíněných funkcí:
extern "C" codeINSTALL_INIT Install_Init(HWND hpar, BOOL fFirstCall, BOOL fPreviouslyInstalled, LPCTSTR pszSuggestedInstallDir)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CSetupTemplateDlg dlg;
if (!dlg.DoModal())
return codeINSTALL_INIT_CANCEL;
return codeINSTALL_INIT_CONTINUE;
}
extern "C" codeINSTALL_EXIT Install_Exit(HWND hpar, LPCTSTR pszChosenInstallDir,
WORD cFailedDirs, WORD cFailedFiles, WORD cFailedRegKeys,
WORD cFailedRegVals, WORD cFailedShortcuts)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(_T("Instalace proběhla úspěšně."), MB_OK | MB_ICONINFORMATION);
return codeINSTALL_EXIT_DONE;
}
extern "C" codeUNINSTALL_INIT Uninstall_Init(HWND hpar, LPCTSTR pszInstallDir)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (AfxMessageBox(_T("Opravdu chcete odinstalovat tento program?"), MB_YESNO | MB_ICONQUESTION) == IDNO)
return codeUNINSTALL_INIT_CANCEL;
return codeUNINSTALL_INIT_CONTINUE;
}
extern "C" codeUNINSTALL_EXIT Uninstall_Exit(HWND hpar)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxMessageBox(_T("Odinstalace proběhla úspěšně."), MB_OK | MB_ICONINFORMATION);
return codeUNINSTALL_EXIT_DONE;
}
Závěr
Problematika tvorby instalačních DLL knihoven není vůbec složitá. Pro plnou funkcionalitu stačí jednoduchá knihovna exportující 4 funkce podle daného předpisu. Kód, který vykonávají, záleží plně na typu aplikace, která je instalována a bude se lišit aplikace od aplikace. Pomocí takových knihoven jsme schopni vytvořit opravdu kvalitní a plnohodnotné instalační sady, které zajistí správný chod aplikace okamžitě po jejím nainstalování. Tento článek neměl být podrobným průvodcem, ale snažil se nastínit možnosti a principy tvorby instalačních knihoven a tento úkol snad splnil.