Odolnost aplikací: Odemknutí skrytých funkcí Instalační služby systému Windows
Michael Sanford
701 Software
Dne
Shrnutí: Instalační služba systému Windows obsahuje několik funkcí, které vývojářská komunita z velké části nepovšimla. Tyto funkce umožňují aplikaci opravit se za běhu nebo nainstalovat volitelné komponenty na základě interakce uživatele s aplikací. (10 vytištěných stránek)
Stáhněte si ukázku integrace MSI Code.msi.
Obsah
Úvod
Odolnost prostřednictvím integrace prostředí
Představujeme rozhraní API Instalační služby systému Windows
Klíčová rozhraní API aplikací
Úkol č. 1: odolnost proti Self-Invoked
Výzva č. 2: Instalace na vyžádání
Závěr
Úvod
Jako vývojáři máme opravdu tendenci myslet na naše aplikace běžící v ideálních prostředích, na ideálních systémech a na ideálních uživatelích, kteří aplikaci používají po úspěšné instalaci. Skutečností je, že po úspěšné instalaci našich aplikací teprve začíná jejich životnost pro daného uživatele. Výzvy, kterým může naše aplikace čelit při zachování stability a funkčnosti, je mnoho, ale většina aplikací není připravená řešit změny v operačním prostředí, které by mohly způsobit, že aplikace nebude fungovat.
Instalační služba systému Windows poskytuje funkce odolnosti, které výrazně urychlily udržení stabilních aplikací, ale tato funkce je založená na určitých akcích, které uživatel provádí při interakci s prostředím, aby poskytoval "vstupní body", pomocí kterých může Instalační služba systému Windows zjišťovat problémy s konfigurací aplikace a provádět kroky k jejich opravě.
Tady je krátký seznam "vstupních bodů" Instalační služby systému Windows:
- Zkratky. Instalační služba systému Windows zavádí speciální typ zástupce, který je sice pro uživatele transparentní, ale obsahuje další metadata, která Instalační služba systému Windows používá prostřednictvím integrace prostředí k ověření stavu instalace zadané aplikace před spuštěním aplikace.
- Přidružení souborů. Instalační služba systému Windows poskytuje mechanismus pro zachytávání volání aplikace přidružené k dokumentu nebo souboru, takže když uživatel otevře dokument nebo soubor pomocí prostředí, instalační služba systému Windows může aplikaci před spuštěním přidružené aplikace ověřit.
- COM Advertising. Instalační služba systému Windows poskytuje mechanismus, který je připojen k podsystému COM, takže každá aplikace, která vytvoří instanci komponenty COM nainstalované Instalační službou systému Windows (a nakonfigurované pro použití této funkce), obdrží instanci této komponenty po ověření stavu instalace této součásti.
Za určitých okolností nemusí být integrované funkce odolnosti Instalační služby systému Windows schopné rozpoznat všechny problémy s konfigurací naší aplikace nebo může aplikace fungovat takovým způsobem, že se neaktivují požadované vstupní body. Chytří lidé z týmu Instalační služby systému Windows naštěstí pochopili tuto výzvu a zpřístupnili nám další funkce odolnosti prostřednictvím bohatého rozhraní API Instalační služby systému Windows.
Odolnost prostřednictvím integrace prostředí
Než přejdeme k pokročilým funkcím odolnosti, které poskytuje rozhraní API Instalační služby systému Windows, podívejme se na typický scénář odolnosti, který obvykle získáváme zdarma při nasazování aplikací pomocí Instalační služby systému Windows.
V tomto scénáři nasazujeme jednoduchou aplikaci pro úpravy textu, které budeme říkat SimplePad. K vytvoření instalace použijeme sadu Open Source WiX Toolkit od Microsoftu (další informace najdete v článku https://sourceforge.net/projects/wix/.), ale to samé můžete provést pomocí libovolného nástroje podle vašeho výběru.
<Directory Id="TARGETDIR" Name="SourceDir">
<Component Id="SimplePad" Guid="BDDFA5DC-BD69-4232-998E-5167814C21B9"
KeyPath="no">
<File Id="SimplePadConfig" Name="SP.cfg"
src="$(var.SrcFilesPath)SimplePad.exe.config"
LongName="SimplePad.exe.config" Vital="yes" KeyPath="no" DiskId="1" />
<File Id="SimplePad" Name="Simple~1.exe"
src="$(var.SrcFilesPath)SimplePad.EXE" LongName="SimplePad.exe" Vital="yes"
KeyPath="yes" DiskId="1" >
<Shortcut Id="SC1" Advertise="yes" Directory="ProgramMenuFolder"
Name="SimpPad" LongName="Run SimplePad" />
</File>
</Component>
<Directory Id="ProgramMenuFolder" Name="ProgMenu"></Directory>
</Directory>
Jak vidíte ve fragmentu XML výše, vytvořili jsme velmi jednoduchou instalaci s jedním souborem (SimplePad.exe) a jedním zástupcem umístěným v nabídce Start uživatele. Je důležité si uvědomit, že v tomto příkladu je zástupce, který vytváříme, vstupním bodem, který Instalační služba systému Windows použije ke zjištění stavu naší aplikace a k opravě podle potřeby.
V tuto chvíli můžeme sestavit instalační program, nainstalovat aplikaci a pomocí nově vytvořeného zástupce nabídky Start ji spustit. Podle očekávání bude aplikace fungovat přesně podle očekávání. Abychom mohli otestovat integrované funkce odolnosti Instalační služby systému Windows, můžeme odstranit soubor SimplePad.exe a zkusit spustit aplikaci z zástupce nabídky Start. Instalační služba systému Windows opět podle očekávání zjistí, že chybí SimplePad.exe, a spustí se oprava. Během operace opravy načte Instalační služba systému Windows požadované konfigurační informace z interní kopie instalačního balíčku uložené v mezipaměti a nakonec nahradí chybějící soubor a vyzve uživatele k zadání zdrojového instalačního média, pokud není v původním umístění, ze kterého bylo nainstalováno. Po dokončení operace opravy se aplikace spustí jako obvykle.
Obrázek 1: Probíhá operace opravy
Odolnost aplikací poskytuje instalační služba systému Windows také prostřednictvím několika dalších mechanismů, které stojí za zmínku. Druhou nejběžnější metodou, jak zajistit, aby aplikace zůstaly vysoce dostupné, je prostřednictvím přidružení souborů Instalační služby systému Windows. Tento mechanismus funguje velmi stejně jako zástupci Instalační služby systému Windows, ale místo přímého propojení se spustitelným souborem aplikace je přidružení provedeno registrovaným typem souboru. Jak vidíte na obrázku 2, přidružení souborů Instalační služby systému Windows jsou definována pomocí stejného mechanismu, který používají standardní přidružení souborů, s jednou výjimkou. Na obrázku 2 si všimněte, že pod typickým klíčem registru shell\Open\command je uvedena další hodnota. Hodnota navíc (označovaná také jako "příkaz") je místo, kde instalační služba systému Windows zobrazí pokaždé, když dvakrát kliknete na soubor v prostředí systému Windows. Tento kryptograficky vypadající řetězec, někdy označovaný jako "Darwin Descriptor", je ve skutečnosti zakódovanou reprezentací konkrétního produktu, komponenty a funkce. Pokud tato hodnota navíc existuje, Instalační služba systému Windows dekóduje data a použije je k provádění kontrol daného produktu a součásti. Pokud se zjistí, že součást chybí nebo je poškozená, Instalační služba systému Windows spustí opravu, která obnoví chybějící soubor nebo data, a nakonec spustí aplikaci, na které odkazujete, jako obvykle, a předá jí příslušné možnosti příkazového řádku.
Obrázek 2. Zobrazení popisovače "Darwin Descriptor" pro přidružení souborů
Konečný mechanismus odolnosti, který dnes budeme probírat, se běžně označuje jako reklama COM. Než se podíváme na mechaniku reklamy COM, je důležité pochopit případ použití za ním. Řekněme, že jste dodavatel komponent, který poskytuje sdílenou knihovnu založenou na modelu COM, která poskytuje poštovní sazby v reálném čase. Vzhledem k tomu, že tuto komponentu může používat mnoho různých produktů, instaluje se do jednoho sdíleného umístění v systému koncového uživatele. Pokud chcete zajistit, aby se komponenta vždy nainstalovala do stejného umístění a aby komponenta zůstala vysoce dostupná, odešlete ji zákazníkům ve slučovacím modulu, který je správně nakonfigurovaný tak, aby využíval výhody reklamy modelu COM. Samozřejmě, protože vaše řešení se dodává jako jeden .dll soubor bez uživatelského rozhraní, ostatní mechanismy odolnosti jednoduše nebudou stačit. V tomto případě se můžeme spolehnout na službu COM Advertising, abychom zajistili, že naše komponenta zůstane správně nainstalovaná a zaregistrovaná v systému uživatele. Když aplikace vytvoří instanci této komponenty prostřednictvím normálních mechanismů modelu COM, Instalační služba systému Windows se do procesu "zachytá" stejným způsobem, jak jsme to viděli u přidružení souborů. Na obrázku 3 si všimněte, že tentokrát je v hodnotě registru InprocServer32 uložen popisovač "Darwin Descriptor" pro registraci modelu COM naší komponenty. Znovu platí, že tyto informace jsou dekódovány a používány Instalační službou systému Windows k zajištění správné instalace a konfigurace naší součásti a provedení jakýchkoli oprav podle potřeby, než nakonec vrátí instanci vaší komponenty do volající aplikace.
Je vhodné poznamenat, že tato jedinečná funkce funguje zcela nezávisle na aplikaci, která komponentu používá. Jinými slovy, i když aplikace používající komponentu nebyla nainstalována pomocí Instalační služby systému Windows, bude reklama modelu COM použitá komponentou nadále fungovat správně, i když volající aplikace je pouze VBScript.
Obrázek 3: Zobrazení popisovače "Darwin Descriptor" pro server COM
Zatím vše, o čem jsme mluvili a ukázali, využilo možností Instalační služby systému Windows bez nutnosti psát jediný řádek kódu, ale teď je čas přejít k bohatší a robustnější implementaci.
Představujeme rozhraní API Instalační služby systému Windows
Výchozí chování Instalační služby systému Windows pro nás v předchozím scénáři fungovalo dobře, ale často máme v reálném světě o něco propracovanější aplikace. Pojďme náš ukázkový scénář rozšířit, abychom se zabývali náročnějším scénářem.
Aplikace se často skládá z více než jednoho spustitelného souboru. Jedním z příkladů může být aplikace, která používá spustitelný soubor zaváděcího nástroje ke kontrole a instalaci aktualizací aplikace, jak je vidět v bloku aplikace updateru. V tomto případě je prvním spustitelným souborem ten, který se vyvolá, když uživatel klikne na zástupce v nabídce Start. Pak spustí druhý spustitelný soubor, který obsahuje hlavní uživatelské rozhraní vaší aplikace. V závislosti na konfiguraci instalace existuje dobrá pravděpodobnost, že modul Instalační služby systému Windows nezjiště problémy s hlavním spustitelným souborem aplikace. I když jednou z možností může být napsání spousty kódu, který se spustí při spuštění, který kontroluje prostředí modulu runtime, jednoduše to nebude fungovat, pokud samotný spustitelný soubor chybí nebo je poškozený, a navíc by nebyl schopen problém snadno opravit. Mnohem efektivnějším řešením je využít znalosti Instalační služby systému Windows o konfiguraci vaší aplikace, která je už definovaná ve vašem balíčku pro nasazení.
Rozhraní API instalační služby systému Windows zveřejňuje stejné mechanismy pro ověření integrity aplikace, které používá při interakci uživatele s prostředím. Při použití těchto volání rozhraní API z naší aplikace si můžeme být jistí, že stále dosáhneme stejných výhod bez spoléhání na "vstupní body prostředí", které jsme probrali dříve.
Tady je seznam scénářů, které nejsou pokryté funkcemi odolnosti integrace prostředí Instalační služby systému Windows:
- Aplikace, které začínají operačním systémem (spouštějí nebo spouští klíče registru jednou)
- Systémové služby
- Plánované úlohy
- Aplikace spouštěné jinými aplikacemi
- Aplikace příkazového řádku
Jsem si jistý, že existuje mnoho dalších scénářů, které bychom mohli přidat do výše uvedeného seznamu, ale myslím, že máte představu. V následujícím příkladu předvedu, jak můžeme získat výhody odolnosti Instalační služby systému Windows bez závislosti na funkcích integrace prostředí, které jsme probrali dříve. Až skončíme, budete moct tyto koncepty vzít a snadno je použít v jakémkoli scénáři, který zahrnuje spuštění spustitelného kódu.
Klíčová rozhraní API aplikací
Než se podíváme na některé ukázkové scénáře, podívejme se na některá z klíčových rozhraní API Instalační služby systému Windows, která můžete používat ve svých aplikacích. Konkrétní informace o použití každého z těchto rozhraní API najdete v referenčních informacích o finction Instalační služby systému Windows v sadě Platform SDK.
Klíčové funkce Instalační služby systému Windows | Description |
---|---|
MsiProvideComponent | Načte nainstalované umístění komponenty a podle potřeby se nainstaluje nebo opraví, aby se zajistilo, že je komponenta dostupná. |
MsiQueryFeatureState | Vrátí stav instalace dané funkce. Tato funkce vám například řekne, jestli je tato funkce nainstalovaná, nenainstalovaná nebo inzerovaná. |
MsiQueryProductState | Vrátí stav instalace produktu. Tato funkce vám například řekne, jestli je produkt nainstalovaný, inzerovaný, nainstalovaný pro jiného uživatele nebo jestli se vůbec nenainstaluje. |
MsiConfigureProduct MsiConfigureProductEx |
Tyto dvě funkce umožňují programovou instalaci nebo odinstalaci aplikace. MsiConfigureProductEx poskytuje větší kontrolu tím, že umožňuje zadat možnosti podobné tomu, co byste normálně dělali na příkazovém řádku. |
MsiConfigureFeature | Tato funkce umožňuje nainstalovat, odinstalovat nebo inzerovat konkrétní funkci aplikace. |
MsiGetUserInfo | Tato funkce vrátí jméno uživatele, organizaci a sériové číslo produktu shromážděné během instalační sekvence produktu. |
MsiGetComponentPath MsiLocateComponent |
Tyto dvě funkce vám pomůžou určit fyzické umístění souboru komponenty nebo klíče registru v cílovém systému. MsiGetComponentPath vrátí cestu k instanci komponenty nainstalované konkrétním produktem, zatímco MsiLocateComponent vrátí první instanci komponenty nainstalované libovolným produktem. |
Úkol č. 1: odolnost proti Self-Invoked
Dříve jsme mluvili o velmi základním scénáři, ve kterém bychom mohli skutečně odstranit spustitelný soubor naší aplikace ze systému a pomocí zástupce způsobit, že Instalační služba systému Windows zjistí a opraví problém přeinstalací chybějícího souboru. I když tento scénář dobře fungoval pro demonstraci integrace prostředí, kterou Instalační služba systému Windows využívá, abychom tyto koncepty vzali hlouběji, podíváme se na trochu sofistikovanější scénář.
V tomto scénáři se naše aplikace skládá z jednoho souboru .exe a několika textových souborů, které aplikaci poskytují důležité informace o konfiguraci.
Pracovníci technické podpory v naší hypotetické softwarové společnosti obdrželi mnoho žádostí o podporu, které ukazují, že instalační služba systému Windows neřeší problémy s konfigurací aplikace, protože uživatelé spouští aplikaci přímo poklikáním na spustitelný soubor v Průzkumníku Windows namísto použití zástupce nabídky Start vytvořené naší instalací.
Po konzultaci s odborníkem na nasazení pracujícím v týmu se náš tým techniků rozhodne, že aplikace by velmi prospěla provedením vlastní kontroly odolnosti při spuštění, aby se ujistila, že je správně nakonfigurovaná. Za tímto účelem tým jednoduše přidá volání rozhraní MSIProvideComponent API, aby se zajistilo, že kritické komponenty definované v instalačním balíčku aplikace jsou správně nainstalovány a nakonfigurovány.
<DllImport("msi.dll")> _
Private Shared Function MsiProvideComponent(ByVal szProduct As String, ByVal _
szFeature As String, ByVal szComponent As String, ByVal dwInstallMode As _
MSI_REINSTALLMODE, ByVal lpPathBuf As System.Text.StringBuilder, ByRef _
pcchPathBuf As IntPtr) As Integer
End Function
Public Shared Function ProvideComponent(ByVal productCode As String, ByVal _
featureName As String, ByVal compID As String) As String
Dim iRet As Integer
Dim cbBuffer As Integer = 255
Dim buffer1 As New System.text.StringBuilder(cbBuffer)
Dim pSize As New IntPtr(cbBuffer)
iRet = MsiProvideComponent(productCode, featureName, compID, _
MSI_INSTALLMODE.INSTALLMODE_DEFAULT, buffer1, pSize)
Return buffer1.ToString
End Function
Pro lepší zapouzdření tohoto kódu je do projektu přidána nová třída s názvem WIHelper , která bude domovem deklarací metod a metod obálky rozhraní API Instalační služby systému Windows. Volání tohoto kódu bylo jednoduchou záležitostí přidání několika řádků do obslužné rutiny události Load našeho hlavního formuláře.
Private CONST PRODUCTID As String = "PRODUCT_GUID_HERE"
Private CONST MAIN_FEATUREID As String = "DefaultFeatureKey"
Private CONST COMPID_1 As String = "COMP1_GUID_HERE"
Private CONST COMPID_2 As String = "COMP2_GUID_HERE"
Private Sub MainForm_Load() Handles MyBase.Load
If WIHelper.IsProductInstalled(PRODUCTID) Then
WIHelper.ProvideComponent(PRODUCTID, MAIN_FEATUREID, COMPID_1)
WIHelper.ProvideComponent(PRODUCTID, MAIN_FEATUREID, COMPID_2)
End If
End Sub
V ukázkovém kódu výše nejprve testujeme, abychom zjistili, jestli byla naše aplikace skutečně nainstalována prostřednictvím instalačního balíčku. To je důležitý koncept, protože chceme mít jistotu, že i když ladíme ve vývojovém prostředí, bude naše aplikace pořád správně fungovat. Abychom toho dosáhli, voláme metodu v naší pomocné třídě s názvem IsProductInstalled. Tato metoda zase jednoduše volá MsiQueryProductState k určení, jestli byl produkt nainstalován v systému. Pokud naše volání IsProductInstalled odhalí, že náš produkt byl nainstalován, pak provedeme řadu volání metody ProvideComponent v naší pomocné třídě. Tato metoda je opět jednoduchou obálkou kolem rozhraní MSIProvideComponent API, která vrací úplnou cestu k zadané komponentě a zajišťuje, aby byla komponenta správně nainstalována a připravena k použití. V závislosti na potřebách vašich konkrétních produktů můžete metodu ProvideComponent volat tolikrát, kolikrát chcete, abyste zajistili, že vaše aplikace bude pro uživatele plně dostupná.
Výzva č. 2: Instalace na vyžádání
Naši obchodní manažeři hypotetických společností slýchali od zákazníků spoustu zpětné vazby, že by chtěli vidět sadu standardních šablon dodávaných s SimplePadem. I když někteří zákazníci vyjádřili silnou touhu po této funkci, jiní vyjádřili obavy z instalace nadbytečných dat, která většina jejich uživatelů nemusí potřebovat.
Po zaslecžení techniků, kteří diskutují o tom, jak se s tímto novým požadavkem vypořádat, náš neochvěný instalační technik naskočí a poukazuje na to, že Instalační služba systému Windows to může snadno zvládnout s malým množstvím dodatečného kódování.
Po rychlém plánování se tým rozhodne, že do nabídky Soubor aplikace implementuje novou položku nabídky Šablony. Pokud je funkce šablon nainstalovaná místně v systému uživatele, zobrazí se uživateli informační nabídka se seznamem všech dostupných šablon a možnost odinstalovat funkci šablon. Pokud nebyla nainstalována funkce šablon, bude mít informační nabídka šablon jedinou položku, která uživateli umožní nainstalovat další šablony.
Private Sub mnuFile_Popup(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles mnuFile.Popup
Dim newItem As MenuItem
With mnuTemplates.MenuItems
.Clear()
If WIHelper.IsFeatureInstalled(PRODUCTID, TEMPLATES_FEATUREID) Then
Dim dirInfo As New DirectoryInfo(Application.ExecutablePath)
For Each dirFile As FileInfo In dirInfo.Parent.GetFiles("*.tpl")
Dim mi As New MenuItem(Path.GetFileNameWithoutExtension(dirFile.Name))
AddHandler mi.Click, AddressOf OpenTemplate
.Add(mi)
Next
.Add("-")
newItem = New MenuItem("Uninstall Templates")
AddHandler newItem.Click, AddressOf UninstallTemplates
.Add(newItem)
Else
newItem = New MenuItem("Install Templates")
AddHandler newItem.Click, AddressOf InstallTemplates
.Add(newItem)
End If
End With
End Sub
Jak vidíte, nejprve zkontrolujeme, jestli je nainstalovaná funkce šablon. Pokud ano, pak provedeme výčet souborů v naší složce aplikací, které mají příponu "tpl", a přidáme název každé šablony do nabídky. Pokud tomu tak není, jednoduše přidáme možnost, aby si uživatel šablony nainstaloval. Než se na to podíváme, podívejme se nejprve na to, jak určíme, jestli je nainstalovaná funkce šablon.
<DllImport("msi.dll")> _
Private Shared Function MsiQueryFeatureState(ByVal szProduct As String,
ByVal szFeature As String) As MSI_INSTALLSTATE
End Function
Public Shared Function IsFeatureInstalled(ByVal pid As String, ByVal fid As String) As Boolean
Return MsiQueryFeatureState(pid, fid) = MSI_INSTALLSTATE.INSTALLSTATE_LOCAL
End Function
V této jednoduché funkci jednoduše zavoláme funkci MsiQueryFeatureState Instalační služby systému Windows a předáme kód ProductCode naší aplikace a název funkce, na kterou se ptáme. Pokud Instalační služba systému Windows vrátí INSTALLSTATE_LOCAL vrátíme hodnotu true, protože to znamená, že je funkce nainstalována místně.
Instalace a odinstalace naší funkce šablon se provádí stejně snadno.
<DllImport("msi.dll")> _
Private Shared Function MsiConfigureFeature(ByVal szProduct As String, ByVal szFeature As String, ByVal eInstallState As MSI_INSTALLSTATE) As Integer
End Function
Public Shared Function InstallFeature(ByVal pid As String, ByVal fid As String)
As Boolean
Return MsiConfigureFeature(pid, fid, MSI_INSTALLSTATE.INSTALLSTATE_LOCAL) = ERROR_SUCCESS
End Function
Public Shared Function UninstallFeature(ByVal pid As String, ByVal fid As String) As Boolean
Return MsiConfigureFeature(pid, fid,
MSI_INSTALLSTATE.INSTALLSTATE_ABSENT) = ERROR_SUCCESS
End Function
Když uživatel klikne na položku nabídky Instalovat šablony, provede se volání MsiConfigureFeature s kódem ProductCode, názvem funkce, kterou chceme konfigurovat, a hodnotou výčtu, která označuje, že chceme funkci nainstalovat místně. Během instalace funkce šablon se uživateli krátce zobrazí dialogové okno s průběhem Instalační služby systému Windows. Když dialogové okno zmizí, šablony budou nainstalovány a připraveny k použití. Když se uživatel vrátí do nabídky Soubor, podnabídka šablon se naplní názvy šablon, jak je popsáno výše.
Závěr
Využití "bezplatných" funkcí a rozhraní API vystavených Instalační službou systému Windows poskytuje některé skvělé funkce, které jdou dlouhou cestou ke snížení nákladů na podporu, zvýšení stability aplikací a zlepšení uživatelského prostředí. Zde uvedené příklady jsou ze své podstaty poněkud triviální, ale doufejme, že budou skvělým výchozím bodem pro implementaci vlastních jedinečných řešení. Podívali jsme se na některá z dostupných rozhraní API, ale určitě jsme je nepokryli všechna. Zkuste prozkoumat všechny funkce rozhraní API Instalační služby systému Windows a vím, že budete příjemně překvapeni, jak snadno můžete využít tyto relativně nevyužité funkce Instalační služby systému Windows.
O autorovi
Michael Sanford je prezidentem a hlavním softwarovým architektem společnosti 701 Software (http://www.701software.com). Před založením společnosti 701 byl Michael prezidentem a generálním ředitelem společnosti ActiveInstall Corporation, kterou koupil zero G Software. ActiveInstall dosáhla věhlasu pro svá řešení pro vytváření Instalační služby systému Windows. Michael je certifikovaný vývojář řešení microsoftu (MCSD), microsoft certified systems engineer (MCSE) a MVP Instalační služby systému Windows. Michaelův blog si můžete přečíst na adrese http://msmvps.com/michael.