D. Przydatne informacje

Ten dokument zgodny jest z NSIS 3.1


D.1 Poziomy błędu


Tak jak inne aplikacje, instalatory stworzone w NSIS zwracają poziomy błędów jako wynik ich wykonania. Sprawdzanie poziomu błędów może być użyteczne, jeśli wywołujesz instalatora NSIS z poziomu innej aplikacji lub instalatora.

  • 0 - Normalne wykonanie (brak błędów)
  • 1 - Instalacja przerwana przez użytkownika (przyciskiem Anuluj)
  • 2 - Instalacja przerwana przez skrypt

Począwszy od wersji NSIS 2.01, możesz ustawiać poziom błędów na inne wartości używając polecenia SetErrorLevel.

Należy zwrócić uwagę na fakt, że deinstalatory kopiują się do katalogu tymczasowego i tam się wykonują. Dzieki temu oryginalny deinstalator może być usunięty. Oznacza to, że poziom błędów deinstalatora nie jest dostępny dla wykonywanego procesu, dopóty symulowany jest ten process kopii deinstalatora oraz jest on wykonywany. Aby zasymulować ten proces, użyj:

CopyFiles $INSTDIR\uninstaller.exe $TEMP
ExecWait '"$TEMP\uninstaller.exe" _?=$INSTDIR' $0
DetailPrint "Poziom błędów deinstalatora: $0"

Jeśli nie wykonasz tych czynności, będziesz mógł jedynie stwierdzić czy kopiowanie deinstalatora do katalogu tymczasowego powiodło się.

D.2 Dodawanie informacji o deinstalacji do "Dodaj/Usuń programy"


Aby dodać wpis do apletu "Dodaj/Usuń programy" Panelu Sterowania Windows, utwórz klucz z nazwą twojego produktu w lokalizacji HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall. W systemach linii Windows NT (NT4/2000/XP), możliwym jest również stworzenie klucza w gałęzi HKCU, co spowoduje, że informacja o programie widoczna będzie tylko dla danego użytkownika. Istnieje kilka wartości, które możesz zapisać do klucza rejestru, aby przechować informacje o instalowanym programie i deinstalatorze. Wartości te zapisuje się przy użyciu polecenia WriteRegStr (dla łańcuchów znaków) lub polecenia WriteRegDWORD (dla wartości typu DWORD). Na przykład:

WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Product" "DisplayName" "Nazwa aplikacji"

Wymagane wartości

  • DisplayName (łańcuch znaków) - Nazwa aplikacji
  • UninstallString (łańcuch znaków) - Ścieżka dostępu oraz nazwa pliku deinstalatora. Powinieneś zawsze umieszczać ścieżkę dostępu w cudzysłowiu, by mieć pewność, że znaki spacji nie uniemożliwią systemowi Windows odnalezienia deinstalatora.

Wartości opcjonalne

Niektóre z poniższych wartości nie zostaną wykorzystane przez starsze wersje systemu Windows.

  • InstallLocation (łańcuch znaków) - Katalog docelowy instalacji ($INSTDIR)
  • DisplayIcon (łańcuch znaków) - Ścieżka dostępu, nazwa pliku oraz indeks ikony, wyświetlane obok nazwy aplikacji
  • Publisher (łańcuch znaków) - (Firma) nazwa wydawcy
  • ModifyPath (łańcuch znaków) - Ścieżka dostępu oraz nazwa pliku programu modyfikującego
  • InstallSource (łańcuch znaków) - Lokalizacja miejsca, z którego zainstalowana została aplikacja
  • ProductID (łańcuch znaków) - ID produktu aplikacji
  • RegOwner (łańcuch znaków) - Zarejestrowany użytkownik aplikacji
  • RegCompany (łańcuch znaków) - Zarejestrowana firma aplikacji
  • HelpLink (łańcuch znaków) - Łącze do strony ze wsparciem online
  • HelpTelephone (łańcuch znaków) - Numer telefonu wsparcia technicznego
  • URLUpdateInfo (łańcuch znaków) - Łącze do strony z uaktualnieniami aplikacji
  • URLInfoAbout (łańcuch znaków) - Łącze do strony domowej aplikacji
  • DisplayVersion (łańcuch znaków) - Wyświetlana wersja aplikacji
  • VersionMajor (DWORD) - Główny numer wersji aplikacji
  • VersionMinor (DWORD) - Numer podwersji aplikacji
  • NoModify (DWORD) - 1 jeśli deinstalator nie posiada opcji modyfikowania zainstalowanej aplikacji
  • NoRepair (DWORD) - 1 jeśli deinstalator nie posiada opcji naprawy instalacji

Jeśli zarówno NoModify jak i NoRepair ustawione są na 1, wyświetlany jest przycisk "Odinstaluj" zamiast "Odinstaluj/Zmień".

D.3 Wywoływanie zewnętrznych bibliotek DLL poprzez wtyczkę System.dll


Niektóre procesy instalatora wymagają wywołania funkcji, które znajdują się w zewnętrznych bibliotekach DLL. Świetnym przykładem jest tutaj instalacja Palm(TM).

Trochę informacji o wtyczce System.dll


Wtyczka System.dll (napisana przez Brainsucker) umożliwia wywoływanie funkcji z zewnętrznych bibliotek poprzez użycie funkcji Call. Istnieje mnóstwo innych funkcji obsługiwanych przez wtyczkę System.dll, ale nie będą one tutaj rozpatrywane. Więcej szczegółów o innych funkcjach znajdziesz w dokumencie opisującym wtyczkę System.


Typy danych

System.dll rozpoznaje następujace typy danych:

  • v - void (ogólnie do zwracania wartości)
  • p - pointer (void*, HANDLE, HWND, UINT_PTR, i tak dalej)
  • i - int (32-bitowa liczba całkowita)
  • l - long & long integer (typ całkowity, znany też jako int64)
  • t - text (tekst), string (łańcuch znaków) (LPCSTR, wskaźnik do pierwszego znaku)
  • k - callback (wywołania zwrotne). Zobacz sekcję wywołania zwrotne w pliku System.
  • * - pointer (wskaźnik) -> procedura wymaga wskaźnika do typu, działa na następny znak (parametr) [ex: '*i' - wskaźnik do int]

Mapowanie zmiennych System.dll do zmiennych skryptu NSIS

Nie ma wiekszego sensu wywoływanie funkcji z zewnątrz, jeśli nie pobiera się żadnych danych. System.dll mapuje zmienne funkcji do zmiennych skryptu NSIS w następujący sposób:

Zmienne NSIS: $0..$9 stają się zmiennymi System.dll: r0..r9, zmienne NSIS: $R0..$R9 stają się zmiennymi System.dll: r10..r19

Każdy parametr określony jest przez swój typ, wejście oraz wyjście. Aby pominąć wejście lub wyjściu użyj kropki. Przykłady:

Łańcuch znaków (wskaźnik na tablicę znaków), wejściem jest 'happy calling':

t 'happy calling'

Łańcuch znaków (wskaźnik na tablicę znaków), wejście jest pobrane z $5, a zmiany w tablicy dokonane przez wywołanie zapisywane są w $R8:

t r5R8

Wskaźnik do typu całkowitego (integer), wartość pobierana z $1 i wstawiana do $2:

*i r1r2

Wskaźnik do 64-bit liczby całkowitej (integer), wyjście odłożone na stos, brak wejścia:

*l .s

Sposób użycia: System.dll::Call

Aby wywołać funkcję w zewnętrznej bibliotece DLL, funkcja Call używana jest w następujący sposób:

System::Call 'NazwaTwojejBibliotekiDLL::FunkcjaTwojejBibliotekiDll(i, *i, t) i(r0, .r1, r2) .r3'

Sekcja '(r0, .r1, r2) .r3' na końcu jest sekcją parametrów, które przekazywane są pomiędzy twoją biblioteka DLL, a skryptem NSIS. Jak można zauważyć na tej liście parametrów typy oraz wejście/wyjście mogą być oddzielone. Każdy blok "(lista parametrów) zwraca wartość" nadpisując oraz/lub dodając do ostatniej. W takim przypadku, pierwszy blok określa typy, a kolejny określa wejście oraz wyjście.


Przed przystąpieniem do kodowania skryptu NSIS


Zanim zaczniesz pisać jakikolwiek kod skryptu NSIS, musisz poznać całkowity prototyp funkcji, którą zamierzasz wywołać. Dla celów tego przykładu, użyjemy funkcji CmGetHotSyncExecPath z biblioteki CondMgr.dll Palm. Funkcja ta jest używana do zwrócenia pełnej ścieżki dostępu do pliku HotSync.exe.


Definicja Funkcji

int CmGetHotSyncExecPath(TCHAR *pPath, int *piSize);

gdzie:

  • pPath jest wskaźnikiem do bufora znaków. Zwracana wartość jest ścieżką & nazwą pliku zainstalowanego menadżera HotSync.
  • piSize jest wskaźnikiem do liczby całkowitej, która określa rozmiar (w TCHAR), bufora odniesionego do parametru pPath.

Zwracane wartości:

  • 0: Brak błędów
  • -1: Wystąpił nieokreślony błąd
  • ERR_REGISTRY_ACCESS(-1006): Błąd dostępu do wpisów konfiguracji Palm
  • ERR_BUFFER_TOO_SMALL(-1010): Bufor jest zbyt mały, by móc przechować żądane informacje
  • ERR_INVALID_POINTER(-1013): Określony wskaźnik nie jest poprawnym wskaźnikiem

Także, jeśli bufor jest zbyt mały wartość w *int jest rozmiarem (w TCHAR), jaki powinien mieć ten bufor.

Ta definicja funkcji mapuje następującą definicję System.dll:

CmGetHotSyncExecPath(t, *i) i

Np. Pobiera tekstową zmienną, wskaźnik do int, i zwraca wartość typu int.


Używanie funkcji z zewnętrznych bibliotek dll

Jak już wiemy, co funkcja robi i w jaki sposób mapuje do formatu System.dll, możemy użyć ją w skrypcie NSIS.

Po pierwsze, musisz zmienić wyjściowy katalog, na ten w którym znajduje się biblioteka DLL, którą chcesz użyć. Może to również działać w przypadku, gdy ta biblioteka DLL znajduje się w ścieżce systemowej, ale nie zostało to przetestowane.

Poniższy fragment kodu zainstaluje bibliotekę condmgr.dll do katalogu tymczasowego, wykona funkcję CmGetHotSyncExecPath i wyświetli zwrócone dane. Zapisz ten skrypt.

; **** wycinek ****
Function loadDll

  SetOutPath $TEMP\eInspect               ; tworzenie katalogu tymczasowego
  File bin\CondMgr.dll                     	   ; skopiowanie tam biblioteki dll
  StrCpy $1 ${NSIS_MAX_STRLEN}         ; przydzielenie pamięci dla zmiennej $0
  System::Call 'CondMgr::CmGetHotSyncExecPath(t, *i) i(.r0, r1r1).r2'
  DetailPrint 'Ścieżka dostępu: "$0"'
  DetailPrint "Długość ścieżki dostępu: $1"
  DetailPrint "Zwrócona wartość: $2"

FunctionEnd
; **** wycinek ****

Powyższa funkcja wypisywana jest w dzienniku instalacji jako:

  • Katalog wyjściowy: c:\windows\TEMP\eInspect
  • Wyodrębnij: CondMgr.dll
  • Ścieżka dostępu: "C:\Dave\palm\Hotsync.exe"
  • Długość ścieżki dostępu: 24
  • Zwrócona wartość: 0

Napisał: djc

Wyrazy uznania & podziękowania

Podziękowania dla KiCHiK oraz Sunjammer za spędzenie mnóstwa czasu przy asystowaniu w rozwiązywaniu tego problemu. Również, przede wszystkim, dzięki dla brainsucker za stworzenie wtyczki System.dll. Powodzenia!

D.4 Zrzut zawartości okna dziennika do pliku


Funkcja ta zrzuci tekst dziennika instalacji do określonego pliku. Stworzyłem tę funkcję dla użytkownika Afrow_UK, który prosił o rozwiązanie problemu zrzutu tekstu dziennika do pliku w tym wątku forum.

Aby jej użyć, podaj nazwę pliku i wywołaj ją. Zrzuci ona tekst dziennika do tego określonego pliku. Na przykład:

GetTempFileName $0
Push $0
Call DumpLog

A oto i funkcja:

!define LVM_GETITEMCOUNT 0x1004
!define LVM_GETITEMTEXT 0x102D

Function DumpLog
  Exch $5
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $6

  FindWindow $0 "#32770" "" $HWNDPARENT
  GetDlgItem $0 $0 1016
  StrCmp $0 0 error
  FileOpen $5 $5 "w"
  StrCmp $5 0 error
    SendMessage $0 ${LVM_GETITEMCOUNT} 0 0 $6
    System::StrAlloc ${NSIS_MAX_STRLEN}
    Pop $3
    StrCpy $2 0
    System::Call "*(i, i, i, i, i, i, i, i, i) p (0, 0, 0, 0, 0, r3, ${NSIS_MAX_STRLEN}) .r1"
    loop: StrCmp $2 $6 done
      System::Call "User32::SendMessage(p, i, p, p) i ($0, ${LVM_GETITEMTEXT}, $2, r1)"
      System::Call "*$3(&t${NSIS_MAX_STRLEN} .r4)"
      FileWrite $5 "$4$\r$\n"
      IntOp $2 $2 + 1
      Goto loop
    done:
      FileClose $5
      System::Free $1
      System::Free $3
      Goto exit
  error:
    MessageBox MB_OK error
  exit:
    Pop $6
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Pop $0
    Exch $5
FunctionEnd

Napisał: KiCHiK


Poniżej znajduje się funkcja, która tworzy plik Unicode, w przypadku gdy kompilujesz instalator Unicode.

!define LVM_GETITEMCOUNT 0x1004
!define LVM_GETITEMTEXT 0x1073

Function DumpLog
  Exch $5
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $6

  FindWindow $0 "#32770" "" $HWNDPARENT
  GetDlgItem $0 $0 1016
  StrCmp $0 0 error
  FileOpen $5 $5 "w"
  FileWriteWord $5 0xfeff ; Zapisz znacznik BOM
  StrCmp $5 0 error
    SendMessage $0 ${LVM_GETITEMCOUNT} 0 0 $6
    System::StrAlloc ${NSIS_MAX_STRLEN}
    Pop $3
    StrCpy $2 0
    System::Call "*(i, i, i, i, i, i, i, i, i) p \
      (0, 0, 0, 0, 0, r3, ${NSIS_MAX_STRLEN}) .r1"
    loop: StrCmp $2 $6 done
      System::Call "User32::SendMessageW(p, i, p, p) i ($0, ${LVM_GETITEMTEXT}, $2, r1)"
      System::Call "*$3(&t${NSIS_MAX_STRLEN} .r4)"
      FileWriteUTF16LE $5 "$4$\r$\n"
      IntOp $2 $2 + 1
      Goto loop
    done:
      FileClose $5
      System::Free $1
      System::Free $3
      Goto exit
  error:
    MessageBox MB_OK error
  exit:
    Pop $6
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Pop $0
    Exch $5
FunctionEnd

Zmodyfikował: Jim Park.

D.5 Jak odczytywać wartości rejestru REG_MULTI_SZ


Napisałem ten skrypt by pomóc użytkownikowi rpetges w rozwiązaniu problemu opisanego w tym wątku forum. Odczytuje on wartość typu REG_MULTI_SZ z rejestru Windows i wypisuje ją. Pamiętaj, o jego edycji, tam gdzie napisane jest "Edytuj tutaj!" gdy testujesz swój skrypt. Wartości muszą wskazywać na wartość REG_MULTI_SZ, bo w przeciwnym razie przykład wygeneruje błędy.

OutFile "REG_MULTI_SZ Reader.exe"

Name "REG_MULTI_SZ Reader"

ShowInstDetails show

!define HKEY_CLASSES_ROOT			0x80000000
!define HKEY_CURRENT_USER			0x80000001
!define HKEY_LOCAL_MACHINE			0x80000002
!define HKEY_USERS					0x80000003
!define HKEY_PERFORMANCE_DATA		0x80000004
!define HKEY_PERFORMANCE_TEXT		0x80000050
!define HKEY_PERFORMANCE_NLSTEXT	0x80000060
!define HKEY_CURRENT_CONFIG		0x80000005
!define HKEY_DYN_DATA				0x80000006

!define KEY_QUERY_VALUE			0x0001
!define KEY_ENUMERATE_SUB_KEYS		0x0008

!define REG_NONE					0
!define REG_SZ						1
!define REG_EXPAND_SZ				2
!define REG_BINARY					3
!define REG_DWORD					4
!define REG_DWORD_LITTLE_ENDIAN	4
!define REG_DWORD_BIG_ENDIAN		5
!define REG_LINK					6
!define REG_MULTI_SZ				7

!define RegOpenKeyEx     				"Advapi32::RegOpenKeyExA(i, t, i, i, *i) i"
!define RegQueryValueEx  				"Advapi32::RegQueryValueExA(i, t, i, *i, i, *i) i"
!define RegCloseKey      				"Advapi32::RegCloseKeyA(i) i"

####### Edytuj tutaj!

!define ROOT_KEY         				${HKEY_CURRENT_USER}
!define SUB_KEY          				"Software\Joe Software"
!define VALUE            				"Strings"

####### Koniec edycji

Section "Read"
  StrCpy $0 ""
  StrCpy $1 ""
  StrCpy $2 ""
  StrCpy $3 ""

  System::Call "${RegOpenKeyEx}(${ROOT_KEY}, '${SUB_KEY}', \
    0, ${KEY_QUERY_VALUE}|${KEY_ENUMERATE_SUB_KEYS}, r0) .r3"

  StrCmp $3 0 goon
    MessageBox MB_OK|MB_ICONSTOP "Nie udało się otworzyć klucza rejestru! ($3)"
    Goto done
goon:

  System::Call "${RegQueryValueEx}(r0, '${VALUE}', 0, r1, 0, r2) .r3"

  StrCmp $3 0 read
    MessageBox MB_OK|MB_ICONSTOP "Nie udało się przetworzyć wartości rejestru! ($3)"
    Goto done

read:

  StrCmp $1 ${REG_MULTI_SZ} multisz
    MessageBox MB_OK|MB_ICONSTOP "Wartość rejestru nie jest wartością typu SZ_MULTI_SZ! ($3)"
    Goto done

multisz:

  StrCmp $2 0 0 multiszalloc
    MessageBox MB_OK|MB_ICONSTOP "Wartość rejestru jest pusta! ($3)"
    Goto done

multiszalloc:

  System::Alloc $2
  Pop $1

  StrCmp $1 0 0 multiszget
    MessageBox MB_OK|MB_ICONSTOP "Nie można zaalokować wystarczającej ilości pamięci! ($3)"
    Goto done

multiszget:

  System::Call "${RegQueryValueEx}(r0, '${VALUE}', 0, n, r1, r2) .r3"

  StrCmp $3 0 multiszprocess
    MessageBox MB_OK|MB_ICONSTOP "Nie udało się przetworzyć wartości rejestru! ($3)"
    Goto done

multiszprocess:

  StrCpy $4 $1

  loop:

    System::Call "*$4(&t${NSIS_MAX_STRLEN} .r3)"
    StrCmp $3 "" done
    DetailPrint $3
    StrLen $5 $3
    IntOp $4 $4 + $5
    IntOp $4 $4 + 1
    Goto loop

done:

  System::Free $1

  StrCmp $0 0 noClose
    System::Call "${RegCloseKey}(r0)"

noClose:

SectionEnd

Napisał: KiCHiK

D.6 Predefiniowane makra dla obsługi Unicode


Istnieją dwa makra, które pomogą ci w pisaniu skryptów, które działają zarówno w instalatorach Unicode jak i ANSI. Aby zorientować się, czy skrypt został skompilowany do wygenerowania instalatora Unicode, użyj instrukcji !ifdef, aby sprawdzić wartość makra ${NSIS_UNICODE}. Aby dowiedzieć się, jaki jest rozmiar domyślnego znaku, użyj makra ${NSIS_CHAR_SIZE}. Instalatory ANSi będą miały wartość równą 1, zaś instalatory Unicode będą miały tę wartość równą 2.