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.
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ę.
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
Wartości opcjonalne
Niektóre z poniższych wartości nie zostaną wykorzystane przez starsze wersje systemu Windows.
Jeśli zarówno NoModify jak i NoRepair ustawione są na 1, wyświetlany jest przycisk "Odinstaluj" zamiast "Odinstaluj/Zmień".
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).
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:
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.
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:
Zwracane wartości:
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:
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!
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.
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
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.