Condividi tramite


Resilienza dell'applicazione: sbloccare le funzionalità nascoste di Windows Installer

 

Michael Sanford
Software 701

Marzo 2005

Riepilogo: Windows Installer ha diverse funzionalità che in gran parte non sono state rilevate dalla community di sviluppo. Queste funzionalità consentono a un'applicazione di ripristinare se stessa in fase di esecuzione o di installare componenti facoltativi in base all'interazione dell'utente con l'applicazione. (10 pagine stampate)

Scaricare l'esempio di integrazione MSI Code.msi.

Contenuto

Introduzione
Resilienza tramite l'integrazione di Shell
Introduzione all'API di Windows Installer
API dell'applicazione chiave
Sfida #1: resilienza Self-Invoked
Sfida 2: Installare su richiesta
Conclusione

Introduzione

Come sviluppatori, è davvero consigliabile pensare alle applicazioni in esecuzione in ambienti ideali, nei sistemi ideali e da utenti ideali che usano l'applicazione dopo un'installazione riuscita. La realtà è che dopo che le applicazioni sono state installate correttamente, la loro durata per l'utente ha appena iniziato. Le sfide che l'applicazione può affrontare in modo stabile e funzionale rimanente sono molte, ma la maggior parte delle applicazioni non è preparata a gestire le modifiche nell'ambiente operativo che potrebbe rendere inoperabile l'applicazione.

Windows Installer offre funzionalità di resilienza che hanno fatto passi significativi per mantenere stabili le applicazioni, ma questa funzionalità si basa su determinate azioni eseguite dall'utente durante l'interazione con la shell per fornire "punti di ingresso" attraverso cui Windows Installer può rilevare problemi con la configurazione dell'applicazione e adottare le misure per ripristinarlo.

Ecco un breve elenco di "punti di ingresso" di Windows Installer:

  • Collegamenti. Windows Installer introduce un tipo speciale di collegamento che, mentre trasparente all'utente, contiene metadati aggiuntivi usati da Windows Installer tramite la relativa integrazione della shell per verificare lo stato dell'installazione dell'applicazione specificata prima di avviare l'applicazione.
  • Associazioni di file. Windows Installer fornisce un meccanismo per intercettare le chiamate per un documento o un'applicazione associata di un file in modo che quando un utente apre un documento o un file usando la shell, Windows Installer può verificare l'applicazione prima di avviare l'applicazione associata.
  • Pubblicità COM. Windows Installer fornisce un meccanismo collegato al sottosistema COM, in modo che qualsiasi applicazione che crea un'istanza di un componente COM installato da Windows Installer (e configurato per l'uso di questa funzionalità) riceva un'istanza di tale componente dopo che Windows Installer ha verificato lo stato dell'installazione del componente.

In determinate circostanze, le funzionalità predefinite di resilienza di Windows Installer potrebbero non essere in grado di rilevare tutti i problemi relativi alla configurazione dell'applicazione oppure l'applicazione può funzionare in modo che i punti di ingresso necessari non vengano attivati. Fortunatamente, i ragazzi intelligenti del team di Windows Installer hanno capito questa sfida e reso disponibili funzionalità di resilienza aggiuntive per noi tramite l'API di Windows Installer avanzata.

Resilienza tramite l'integrazione di Shell

Prima di passare alle funzionalità avanzate di resilienza fornite dall'API di Windows Installer, si esaminerà uno scenario tipico di resilienza che in genere si ottiene gratuitamente quando si distribuiscono le app con Windows Installer.

In questo scenario si distribuisce un'applicazione di modifica del testo semplice che chiameremo SimplePad. Per creare l'installazione, verrà usato Open Source WiX Toolkit di Microsoft (per altre informazioni vedere https://sourceforge.net/projects/wix/.), ma è possibile eseguire la stessa operazione con qualsiasi strumento scelto.

<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>

Come si può vedere nel frammento XML precedente, è stata creata un'installazione molto semplice con un file (SimplePad.exe) e un singolo collegamento situato nel menu Start dell'utente. È importante notare che in questo esempio il collegamento che stiamo creando è il punto di ingresso che Windows Installer userà per rilevare lo stato dell'applicazione e ripristinarlo in base alle esigenze.

A questo punto, è possibile compilare il programma di installazione, installare l'applicazione e usare il collegamento a menu Start appena creato per eseguirlo. Come previsto, l'applicazione funzionerà esattamente come previsto. Per testare le funzionalità di resilienza predefinite di Windows Installer, è possibile eliminare il file di SimplePad.exe e provare a eseguire l'applicazione dal menu Start. Di nuovo, come previsto, Windows Installer rileva che SimplePad.exe è mancante e viene avviata una riparazione. Durante l'operazione di ripristino, Windows Installer legge le informazioni di configurazione necessarie dalla copia memorizzata nella cache interna del pacchetto di installazione e sostituisce infine il file mancante, richiedendo all'utente il supporto di installazione di origine se non è presente nel percorso originale da cui è stato installato. Al termine dell'operazione di ripristino, l'applicazione viene avviata come normale.

Figura 1. Operazione di ripristino in corso

La resilienza dell'applicazione viene fornita anche da Windows Installer tramite un paio di altri meccanismi che vale la pena menzionare anche qui. Il secondo metodo più comune per garantire che le applicazioni rimangano a disponibilità elevata è tramite associazioni di file di Windows Installer. Questo meccanismo funziona molto allo stesso modo dei collegamenti di Windows Installer, ma invece di collegare direttamente al file eseguibile di un'applicazione, l'associazione viene effettuata da un tipo di file registrato. Come si può vedere nella figura 2, le associazioni di file di Windows Installer vengono definite usando lo stesso meccanismo usato dalle associazioni di file standard, con un'eccezione. Si noti nella figura 2 che un valore aggiuntivo è elencato sotto la tipica chiave del Registro di sistema "shell\Open\command". Il valore aggiuntivo (denominato anche "comando") è dove Windows Installer sembra qualsiasi volta che si fa doppio clic su un file dall'interno della shell di Windows. Questa stringa di aspetto criptico, talvolta definita "Descrittore Darwin", è in realtà una rappresentazione codificata di un prodotto, un componente e una funzionalità specifici. Se questo valore aggiuntivo esiste, Windows Installer decodifica i dati e lo userà per eseguire controlli su tale prodotto e componente. Se il componente è mancante o danneggiato, Windows Installer avvierà una riparazione per ripristinare il file o i dati mancanti e infine avviare l'applicazione a cui si fa riferimento come normale, passando le opzioni della riga di comando appropriate.

Figura 2. Visualizzazione di un descrittore "Darwin" per un'associazione di file

Il meccanismo finale di resilienza verrà illustrato oggi è comunemente noto come PUBBLICITÀ COM. Prima di esaminare i meccanismi di COM Advertising, è importante comprendere il caso d'uso dietro di esso. Si supponga di essere un fornitore di componenti che fornisce una libreria condivisa basata su COM che fornisce tariffe postali in tempo reale. Poiché questo componente può essere usato da molti prodotti diversi, viene installato in una singola posizione condivisa nel sistema dell'utente finale. Per garantire che il componente venga sempre installato nella stessa posizione e per garantire che il componente rimanga a disponibilità elevata, lo si invia ai clienti in un modulo di merge configurato correttamente per sfruttare COM Advertising. Naturalmente, poiché la soluzione viene fornita come un singolo file .dll senza interfaccia utente, gli altri meccanismi di resilienza non sono sufficienti. In questo caso, è possibile basarsi su COM Advertising per garantire che il componente rimanga installato correttamente e registrato nel sistema dell'utente. Quando un'applicazione crea un'istanza di questo componente tramite normali meccanismi COM, Windows Installer "hooks" nel processo in modo analogo a quello illustrato con le associazioni di file. Si noti nella figura 3 che questa volta viene archiviato un descrittore "Darwin" nel valore del Registro di sistema InprocServer32 per la registrazione COM del componente. Anche in questo caso, queste informazioni vengono decodificate e usate da Windows Installer per assicurarsi che il componente sia installato correttamente e configurato, eseguendo eventuali riparazioni in base alle esigenze, prima di restituire un'istanza del componente all'applicazione chiamante.

Vale la pena sottolineare che questa funzionalità univoca funziona completamente indipendentemente dall'applicazione usando il componente. In altre parole, anche se l'applicazione che usa il componente non è stata installata tramite Windows Installer, la pubblicità COM usata dal componente continuerà a funzionare correttamente, anche se l'applicazione chiamante è semplicemente un VBScript.

Figura 3. Visualizzazione di un descrittore "Darwin" per un server COM

Finora, tutto ciò che abbiamo parlato e dimostrato ha sfruttato le funzionalità di Windows Installer senza dover scrivere una singola riga di codice, ma ora è il momento di passare a un'implementazione più ricca e più affidabile.

Introduzione all'API di Windows Installer

Il comportamento predefinito di Windows Installer funziona bene per noi nello scenario precedente, ma spesso, nel mondo reale, abbiamo applicazioni leggermente più sofisticate. Espandere lo scenario di esempio per gestire uno scenario più complesso.

Spesso un'applicazione è costituita da più file eseguibili. Un esempio può essere un'applicazione che usa un eseguibile di bootstrapper per verificare e installare gli aggiornamenti in un'applicazione, come illustrato nel blocco di applicazioni updater. In questo caso, il primo eseguibile è quello richiamato quando l'utente fa clic su un collegamento nel menu Start. A sua volta avvia il secondo eseguibile che contiene l'interfaccia utente principale dell'applicazione. A seconda del modo in cui è stata configurata l'installazione, è possibile che i problemi con il file eseguibile principale dell'applicazione non vengano rilevati dal motore di Windows Installer. Anche se un'opzione potrebbe essere quella di scrivere un gruppo di codice eseguito all'avvio che controlla l'ambiente di runtime, questo non funzionerà semplicemente se l'eseguibile stesso non è mancante o danneggiato e, inoltre, non sarebbe in grado di risolvere facilmente il problema. Una soluzione molto più efficace consiste nell'sfruttare la conoscenza di Windows Installer della configurazione dell'applicazione già definita nel pacchetto di distribuzione.

L'API di Windows Installer espone gli stessi meccanismi per verificare l'integrità di un'applicazione usata quando l'utente interagisce con la shell. Usando queste chiamate API dall'applicazione, è possibile assicurarsi di ottenere ancora gli stessi vantaggi senza la dipendenza dalla shell "punti di ingresso" illustrati in precedenza.

Ecco un elenco di scenari non coperti dalle funzionalità di resilienza della shell di Windows Installer:

  • App che iniziano con il sistema operativo (eseguire o eseguire le chiavi del Registro di sistema)
  • Servizi di sistema
  • Attività pianificate
  • App eseguite da altre app
  • App da riga di comando

Sono sicuro che ci sono molti altri scenari che potremmo aggiungere all'elenco precedente, ma credo che tu abbia l'idea. Nell'esempio seguente viene illustrato come è possibile ottenere i vantaggi della resilienza di Windows Installer senza la dipendenza dalle funzionalità di integrazione della shell illustrate in precedenza. Al termine, sarà possibile accettare questi concetti e applicarli facilmente a qualsiasi scenario che implica l'esecuzione di codice eseguibile.

API dell'applicazione chiave

Prima di esaminare alcuni scenari di esempio, è possibile esaminare alcune delle PRINCIPALI API di Windows Installer che è possibile usare dall'interno delle applicazioni. Per informazioni specifiche sull'utilizzo di ognuna di queste API, fare riferimento al riferimento di Finction di Windows Installer in Platform SDK.

Funzioni di Windows Installer chiave Descrizione
MsiProvideComponent Recupera il percorso installato di un componente, l'installazione o la riparazione in base alle esigenze per assicurarsi che il componente sia disponibile.
MsiQueryFeatureState Restituisce lo stato di installazione di una determinata funzionalità. Ad esempio, questa funzione indica se la funzionalità è installata, non installata o annunciata.
MsiQueryProductState Restituisce lo stato di installazione di un prodotto. Ad esempio, questa funzione indica se il prodotto è installato, annunciato, installato per un altro utente o non installato.
MsiConfigureProduct
MsiConfigureProductEx
Queste due funzioni consentono di installare o disinstallare a livello di codice un'applicazione. MsiConfigureProductEx offre un controllo maggiore consentendo di specificare opzioni simili a quello che normalmente si farebbe nella riga di comando.
MsiConfigureFeature Questa funzione consente di installare, disinstallare o annunciare una funzionalità specifica di un'applicazione.
MsiGetUserInfo Questa funzione restituisce il nome dell'utente, l'organizzazione e il numero di serie del prodotto raccolto durante la sequenza di installazione del prodotto.
MsiGetComponentPath
MsiLocateComponent
Queste due funzioni consentono di determinare la posizione fisica di un file di componente o di una chiave del Registro di sistema nel sistema di destinazione. MsiGetComponentPath restituisce il percorso dell'istanza del componente installata da un prodotto specifico, mentre MsiLocateComponent restituisce la prima istanza di un componente installato dal prodotto ANY.

Sfida #1: resilienza Self-Invoked

In precedenza abbiamo parlato di uno scenario molto di base in cui è stato effettivamente possibile eliminare il file eseguibile dell'applicazione dal sistema e usare un collegamento per causare il rilevamento e la riparazione del problema di Windows Installer reinstallare il file mancante. Anche se questo scenario ha funzionato bene per dimostrare l'integrazione della shell che Windows Installer sfrutta, per approfondire questi concetti, si esaminerà uno scenario leggermente più sofisticato.

In questo scenario l'applicazione è costituita da un singolo file .exe e diversi file di testo che forniscono informazioni di configurazione critiche all'applicazione.

Il personale del supporto tecnico presso la nostra società software ipotetica ha ricevuto molte richieste di supporto che rivelano che i problemi di configurazione dell'applicazione non vengono risolti da Windows Installer a causa del fatto che gli utenti eseguono l'applicazione direttamente facendo doppio clic sul file eseguibile in Esplora windows anziché usando il collegamento a menu Start creato dall'installazione.

Dopo aver consultato l'esperto di distribuzione del team, il team di ingegneri decide che l'applicazione potrebbe trarre vantaggio notevolmente eseguendo un controllo di resilienza personalizzato all'avvio per assicurarsi che sia configurato correttamente. A questo scopo, il team aggiunge semplicemente una chiamata all'API MsiProvideComponent per assicurarsi che i componenti critici definiti nel pacchetto di installazione dell'applicazione siano installati e configurati correttamente.

<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

Per incapsulare meglio questo codice, viene aggiunta una nuova classe denominata WIHelper al progetto per ospitare le dichiarazioni dei metodi di metodo api di Windows Installer e wrapper. La chiamata di questo codice è stata una semplice questione di aggiunta di alcune righe al gestore eventi load del modulo principale.

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
 

Nel codice di esempio precedente viene eseguito il primo test per verificare se l'applicazione è stata effettivamente installata tramite il pacchetto di installazione o meno. Questo è un concetto importante perché si vuole essere certi che anche se si esegue il debug nell'ambiente di sviluppo, l'applicazione funzionerà comunque correttamente. A questo scopo, viene chiamato un metodo nella classe helper denominata IsProductInstalled. Questo metodo, a sua volta, chiama semplicemente MsiQueryProductState per determinare se il prodotto è stato installato nel sistema. Se la chiamata a IsProductInstalled rivela che il prodotto è stato installato, viene eseguita una serie di chiamate al metodo ProvideComponent nella classe helper. Questo metodo è, di nuovo, un wrapper semplice intorno all'API MsiProvideComponent , che restituisce il percorso completo al componente specificato e garantisce che il componente sia installato correttamente e pronto per l'uso. A seconda delle esigenze dei prodotti specifici, è possibile chiamare il metodo ProvideComponent quante volte si vuole assicurarsi che l'applicazione sia completamente disponibile per l'utente.

Sfida 2: Installare su richiesta

Le nostre ipotetiche aziende sales executive hanno sentito un sacco di feedback dai clienti che vogliono vedere un set di modelli standard forniti con SimplePad. Mentre alcuni clienti hanno espresso un forte desiderio per questa funzionalità, altri hanno espresso preoccupazione per l'installazione di dati extraneous che la maggior parte dei loro utenti potrebbe non avere bisogno.

Dopo aver superato l'esperienza dei tecnici che illustrano come affrontare questo nuovo requisito, il nostro tecnico di installazione intrepid salta e sottolinea che Windows Installer può gestire facilmente questa operazione con una piccola quantità di codice aggiuntivo.

Dopo una pianificazione rapida, il team decide che implementerà una nuova voce di menu Modelli nel menu File dell'applicazione. Se la funzionalità modelli viene installata localmente nel sistema dell'utente, l'utente visualizzerà un menu a comparsa che elenca ognuno dei modelli disponibili e un'opzione per disinstallare la funzionalità dei modelli. Se la funzionalità dei modelli non è stata installata, il menu a comparsa dei modelli avrà una singola voce, consentendo all'utente di installare i modelli aggiuntivi.

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

Come si può vedere, verificare prima di tutto se la funzionalità dei modelli è installata. In caso affermativo, enumeriamo i file nella cartella delle applicazioni con l'estensione "tpl" e aggiungiamo il nome di ogni modello al menu. In caso contrario, è sufficiente aggiungere un'opzione per l'utente per installare i modelli. Prima di esaminare questo aspetto, si esaminerà prima di tutto come si determina se la funzionalità dei modelli è installata.

<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

In questa semplice funzione, si chiama semplicemente la funzione MsiQueryFeatureState di Windows Installer, passando il Codice ProductCode dell'applicazione e il nome della funzionalità in cui ci si sta chiedendo. Se Windows Installer restituisce INSTALLSTATE_LOCAL, viene restituito true perché la funzionalità viene installata in locale.

L'installazione e la disinstallazione delle funzionalità dei modelli vengono eseguite facilmente.

<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

Quando l'utente fa clic sulla voce di menu "Installa modelli", viene effettuata una chiamata a MsiConfigureFeature con ProductCode, il nome della funzionalità da configurare e un valore di enumerazione che indica che si vuole installare la funzionalità in locale. L'utente visualizzerà una finestra di dialogo di stato di Windows Installer visualizzata brevemente mentre la funzionalità dei modelli è installata. Quando la finestra di dialogo scompare, i modelli verranno installati e pronti per l'uso. Quando l'utente torna al menu File, i sottomenu modelli popolano con i nomi dei modelli come descritto in precedenza.

Conclusione

Sfruttando le funzionalità "gratuite" e le API esposte da Windows Installer ci offre alcune funzionalità interessanti che vanno a lungo per ridurre i costi di supporto, aumentare la stabilità dell'applicazione e migliorare l'esperienza utente. Gli esempi illustrati qui sono leggermente semplici in natura, ma si spera che formi un ottimo punto di partenza per implementare le proprie soluzioni uniche. Sono state esaminate alcune DELLE API disponibili, ma certamente non sono state descritte tutte. Per esplorare tutte le funzionalità dell'API di Windows Installer e so che sarai piacevolemente sorpreso in quanto facilmente puoi sfruttare queste funzionalità relativamente non sfruttate di Windows Installer.

 

Informazioni sull'autore

Michael Sanford è Presidente e Chief Software Architect for 701 Software (http://www.701software.com). Prima di formare 701, Michael è stato presidente e CEO di ActiveInstall Corporation, che è stato acquisito da Zero G Software. ActiveInstall ha ottenuto la notorietà per le sue soluzioni di creazione di Windows Installer. Michael è uno sviluppatore di soluzioni Microsoft Certified (MCSD), Microsoft Certified Systems Engineer (MCSE) e un MVP di Windows Installer. È possibile leggere il blog di Michael all'indirizzo http://msmvps.com/michael.