Erste Schritte - Teil7: Benutzerdefinierte Aktionen, Teil 3 Eigene DLLs

Antworten
Volker
Beiträge: 37
Registriert: 08.07.2022, 09:25

Erste Schritte - Teil7: Benutzerdefinierte Aktionen, Teil 3 Eigene DLLs

Beitrag von Volker »

Nachdem sich Teil 6 mit Skripten ohne Programmierung beschäftigt hat, widmet sich dieser
Teil der Installer-API.

Bild

Die Königsdisziplin, eigene DLLs die den Funktionsumfang des Setups erweitern. Genau diese Schnittstelle
für DLLs wird unter anderem von folgenden Erweiterungen genutzt: Firewall, Instanzen, Treiber, geplante
Tasks, NT-Dienste, .NET-Assemblies
und Windows Features.

Das zeigt schon das man damit eine ganze Menge bewerkstelligen kann.

:idea: Tipp: In dem Ordner ?:\Programme\AKApplications\AKInstaller\Extensions\ finden Sie ein Demo-Projekt mit
Namen EXT_Demo.zip dazu.

Die API bietet eine Vielzahl von Funktionen mit der Sie auf den Datenbestand des Setups zugreifen oder mit der Sie
Daten wandeln bzw. prüfen können.

Neben WriteToLog um Meldungen in das Logfile zu schreiben oder GetProperty, SetProperty und
EvaluateCondition auch Befehle wie GetUserSID oder CompareVersion und Zugriff auf den
RestartManager mittels RMRegisterFile.

Dabei sind viele Befehle nach dem gleichen Schema aufgebaut, dazu schauen wir und mal einen davon an:

Code: Alles auswählen


typedef long ( WINAPI * AKI_GetProperty)(LPCWSTR szProperty, LPWSTR pValueBuf, DWORD& chValueBuf);

Im ersten Parameter wird der Name der Eigenschaft erwartet.

Der Zweite erwartet einen WCHAR-Buffer entsprechender Länge.

Der dritte ein DWORD mit der Länge.

Die einfachste Variante wäre also:

Code: Alles auswählen


WCHAR szString1[100-1] = {0};
DWORD dwSize= 100;
akiStruct->GetProperty("VAR_MeineEigenschaft", szString1, dwSize);
Etwas aufwendiger wird es, wenn die Länge unbekannt ist, hier mal ein Beispiel mit Text:

Code: Alles auswählen


dwSize = 0;
if(akiStruct->GetText( L"IDC_WelcomeSetup", NULL, dwSize) == 0)
   {
   WCHAR* pBuffer1 = reinterpret_cast<WCHAR*>(malloc(dwSize*sizeof(WCHAR))));

   if(pBuffer1)
      {
       akiStruct->GetText( L"IDC_WelcomeSetup", pBuffer1 pBuffer11, dwSize);
...

Häufig kommt es vor, das man Texte ähnlich sprintf() formatieren möchte, man hat z. B. einige Daten
ermittelt und möchte jede nach Situation an unterschiedlicher Stelle platzieren und damit sinnvoll
ausgeben oder entsprechen für Zugriffe aufarbeiten. 


Ein sehr einfaches Beispiel:
"Hallo [1], [2] ist [3]" ist unsere Vorlage.
Text1=Benutzer,
Text2=heute,
Text3=Freitag

Code: Alles auswählen

dwSize= 0;
if(akiStruct->FormatString( L"Hallo [1], [2] ist [3]", NULL, dwSize, L"Benutzer", L"heute", L"Freitag", NULL) == 0)
   {
   WCHAR *pBuffer2 = reinterpret_cast< char*>(malloc(dwSize * sizeof(WCHAR)));

   if( pBuffer2)
      {
      akiStruct-> FormatString( L"Hallo [1], [2] ist [3]", pBuffer2, dwSize, L"Benutzer", L"heute", L"Freitag, NULL);

      MessageBox(0, pBuffer2, L"FormatString",0);
      free ( pBuffer2);
      }
   } 
  

Der Vorteil gegenüber sprintf() ist hier, das der Aufbau der Vorlage durch die Syntax von Sprachen
durchaus variieren kann, z. B. das Tag/Monat im Deutschen Monat/Tag im Englischen.

In diesem Falle wäre die Vorlage die auf der Seite Sprachen angegeben und die vorher mittels
GetText() geholt wurde "[1].[2].[3]" oder "[2].[1].[3]"
während die Parameter immer "24", "12", "2022" lauten.


Wie bei normalen Benutzerdefinierten Aktionen die Setup-Seiten, müssen diese DLLs mindestens eine der
folgenden Einstiegspunkte haben:
  • EXT_IAppSearchProcess
  • EXT_IAppSearchExecute
  • EXT_ILaunch
  • EXT_IPrepare
  • EXT_IProcess
  • EXT_IExecute
  • EXT_IRollbackPre
  • EXT_IRollbackPost
  • EXT_ICommit
  • EXT_UProcess
  • EXT_UExecute

Code: Alles auswählen

long WINAPI EXT_IExecute(struct AKIStructI*akiStruct) ...

Hat eine DLL eine dieser Funktionen wird diese aufgerufen.
Und im Idealfall kehrt die Funktion mit ERROR_SUCCESS (0) zurück. An dieser Stelle sei noch
mal das Demo-Projekt ..\AKApplications\AKInstaller\Extensions\EXT_Demo.zip erwähnt welches
viele Funktionen mit Beispielen bedient.

Nun das Einbinden der DLLs:

Bild

Beim Einbinden eigener DLLs kommen wir zu diesem rechte einfach gehaltenen Dialog, der aber
alles beinhaltet, was wir benötigen.

Der Bezeichner ist die ID und wird auch für die Ausgabe im Logfile verwendet. Die DLL muss,
wie im oben erwähnt, eine der nötigen Einstiegspunkte haben.

Wichtig ist für uns eigentlich die Datendatei. Hat die Erweiterung keine feste Aufgabe, sondern
eine individuelle wie z. B. die Erweiterung NT-Dienste, werden hier die zu verarbeitenden Daten
übergeben, in welcher Form auch immer.

Bild

Wie in der Struktur zusehen wird uns diese Datei mittels szDataFile geliefert. Außerdem eine
szUnInstallFile welche wir selbst mit Daten beliefern.


:?: Wie ist nur der normale Ablauf bei einer Erweiterung, die Install, Rollback und Uninstall Abhandeln soll?

Für unser Beispiel nehmen wir an, wir möchten vor der Erstinstallation 1-5 Systemdateien retten und
dieses bei einer Deinstallation wieder zurück kopieren. Achtung, dieses ist NUR als Beispiel gedacht.

Bei der Installation wird in der Funktion EXT_IProcess ermittelt ob und welche Daten geändert werden müssen.
Dazu holen wir und mittels GetFileVersion die Version und vergleichen diese per CompareVersion.
Hierbei stellen wir fest, es sind 5 Dateien die wir retten müssen.

Wir geben dem Setup zu verstehen, das wir 5 Installationsschritte benötigen und setzen in EXT_IProcess:
akiStruct->dwStepSizeI= MAKELONG(5, PROGRESS_WIDTH);
Diese Schritte spiegeln sich auf im Fortschrittsbalken wieder.

Welche Dateien wir ändern müssen könnten wir (einfach gehalten) als INI-Dateienwerte in der szUnInstallFile vermerken:

[Update]
File1=C:\Windows\Sytem32\Datei1.dll
File2=C:\Windows\Sytem32\Datei2.dll


In EXT_IExecute holen wir und die Anzahl der Schritte:

Code: Alles auswählen

WORD wSteps = LOWORD(akiStruct->dwStepSizeI); 

// Ordner in dem die zu rettenden Dateien liegen sollen erstellen...
// Fehler? return ERROR_INSTALL_FAILURE;

for(WORD i = 1; i <= wSteps; i++)
   {
   // Datei aus INI holen
  sprintf(szString1, _T("File%ld"), i);
   // Alte Datei kopieren und durch Neue ersetzen, Backup-Pfad in INI-Datei schreiben 
   // Fehler? return ERROR_INSTALL_FAILURE;
   // wenn fehlerfrei ...
   akiStruct->;dwStepSizeR = MAKELONG(i , PROGRESS_WIDTH); 
...

Im Fehlerfall wird in EXT_IRollbackPost folgendes gemacht:

Code: Alles auswählen

WORD wSteps = LOWORD(akiStruct->dwStepSizeR); 
for(WORD i = 1; i <= wSteps; i++)
   {
   // Datei aus INI holen
  sprintf(szString1, _T("File%ld"), i);
  // Systemdatei durch Backup-Datei ersetzen.
EXT_IRollbackPre benötigen wir in dem Falle nicht, da die Schritte schon ermittelt wurden
und wir vor dem Rollback des Setups keine weiteren Aufgaben haben bzw. erledigen müssen.


Das gleiche Verfahren nutzen wir in bei der Deinstallation, darum macht es Sinn für die 'INI-Datei'
den szUnInstallFile-Pfad zu nutzen.

In EXT_UProcess werden die Anzahl der Schritte ermittelt, also wie viele Backup-Dateien
haben wir. EXT_UExecute erledigt dann das Wiederherstellen der Dateien.


Das Ganze war jetzt bewusst einfach gehalten, da es nur den Weg aufzeigen sollte wie
mit Process, Execute und Rollback bzw. Uninstall zu verfahren ist.


Natürlich sollte man bei diesem Beispiel im Uninstall prüfen, ob die aktuelle Datei vielleicht eine
höhere Versionsnummer hat als das Backup und ob die Backupdatei tatsächlich auch die ehemalige
Systemdatei ist.

Man sollte die Daten bei INI-Dateien also verschlüsseln und Hashwerte der Dateien sichern, ebenfalls
verschlüsselt oder man verpackt die Backups in ein gesichertes Zip.


Sichtbarer Fortschritt für den Benutzer:
Außerdem könnten wir Process weglassen, da das Kopieren von 5 Dateien wohl nicht länger dauert,
als der Benutzer braucht um den Finger von der Maustaste zu lösen.

Anders sieht es z. B. bei der Erweiterung Windows Features aus, wo vorher ermittelt wird, welche der
Feature die benötigt werden installiert werden müssen und welche Abhängigkeiten diese benötigen und
den darauf folgenden Installationsschritten ggf. sogar mit dem Aufruf von Windows Update, was einige
Zeit in Anspruch nehmen kann. Hier muss der Benutzer Feedback erhalten, das sind zum einen die
Installationsschritte die den Fortschrittsbalken bewegen aber auch Aufrufe von SetDlgInfoText und
SetDlgDataText.

Mit freundlichen Grüßen,
AKApplications, Volker J.
Antworten