Wtyczka System daje programistom możliwość wywoływania dowolnej wyeksportowanej funkcji z dowolnej biblioteki. Na przykład, możesz jej użyć do wywołania funkcji GetLogicalDriveStrings, aby pobrać listę dostępnych napędów na komputerze użytkownika.
Wtyczka System pozwala także na alokację, zwalnianie oraz kopiowanie pamięci; interakcję z obiektami COM oraz wykonywanie operacji matematycznych z 64 bitową precyzją.
Aby zrozumieć dobrze wtyczkę System wymagana jest wiedza z zakresu programowania.
Alloc SIZE
Alokuje liczbę SIZE bajtów i zwraca adres pamięci na stos.
Przykład:
System::Alloc 64 Pop $0 DetailPrint "Zaalokowano 64 bajty w zmiennej $0" System::Free $0
StrAlloc SIZE
Alokuje bufor łańcucha znaków TCHARs o rozmiarze SIZE i zwraca adres pamięci na stos. Funkcja ta jest bardzo użyteczna, gdy chcesz napisać skrypt NSI, który działa zarówno w NSIS ANSI jak i Unicode.
Przykład:
System::StrAlloc 64 ; Bufor łańcucha znaków o długości 63 znaków i znak końca \0. Pop $0 DetailPrint "Zaalokowano bufor łańcucha znaków o długości 64 znaki w zmiennej $0" System::Free $0
Copy [/SIZE] DESTINATION SOURCE
Kopiuje SIZE bajtów z SOURCE do DESTINATION. Jeśli nie określono SIZE, rozmiar SOURCE będzie używało GlobalSize. Oznacza to, że jeśli nie zaalokujesz SOURCE przez użycie składni System::Alloc, System::Call lub GlobalAlloc, musisz określić SIZE. Jeśli DESTINATION jest zerem zostanie on zaalokowany i jego adres zostanie odłożony na stos.
Przykład:
# alokuje bufor i odkłada łańcuch znaków 'napis przykładowy' oraz liczbę całkowitą int w nim System::Call "*(&t1024 'napis przykładowy', i 5) p .s" Pop $0 # kopiuje do automatycznie utworzonego bufora System::Copy 0 $0 Pop $1 # pobiera łańcuch znaków oraz liczbę całkowitą int z bufora $1 System::Call "*$1(&t1024 .r2, i .r3)" # zwalnia pamięć bufora System::Free $1 # drukuje wyniki DetailPrint $2 DetailPrint $3 # kopiuje do naszego bufora System::Alloc 1028 Pop $1 System::Copy $1 $0 # pobiera łańcuch znaków oraz liczbę całkowitą int z bufora $1 System::Call "*$1(&t1024 .r2, i .r3)" # free System::Free $0 System::Free $1 # drukuje wyniki DetailPrint $2 DetailPrint $3
Free ADDRESS
Zwalnia ADDRESS.
Przykład:
System::Alloc 64 Pop $0 DetailPrint "Zaalokowano 64 bajty w zmiennej $0" System::Free $0
Store "OPERATION [OPERATION [OPERATION ...]]"
Obsługuje operacje na stosie. Mogą to być operacje typu odłóż, zdejmij pojedynczy rejestr ze stosu NSIS lub odłożenie, zdjęcie wszystkich dostępnych rejestrów ($0-$9 oraz $R0-$R9) z odrębnego stosu wtyczki System. Operacje mogą być rozdzielone dowolnym znakiem.
Dostępne operacje:
Przykład:
StrCpy $0 "test" System::Store "p0" Pop $1 DetailPrint "$0 = $1"
StrCpy $2 "test" System::Store "p2 R2" DetailPrint "$2 = $R2"
StrCpy $3 "test" System::Store "s" StrCpy $3 "kolejny test" System::Store "l" DetailPrint $3
System::Store "r4" "test" DetailPrint $4
Call PROC [( PARAMS ) [RETURN [? OPTIONS]]] Get PROC [( PARAMS ) [RETURN [? OPTIONS]]]
Obydwa polecenia - Call jak i Get dzielą wspólną składnię. Tak jak sugerują ich nazwy, polecenie Call służy do wywoływania, zaś polecenie Get do pobierania. Cóż takiego jest wywoływane, pobierane? Zależy to od wartości PROC.
PARAMS jest listą parametrów. Możesz przechowywać dane w parametrach, możesz również pobierać z nich dane. Lista parametrów rozdzielana jest przecinkami. Każdy parametr składa się z trzech wartości: type, source oraz destination.
Wartość Type może być typu całkowitego, łańcuchem znaków, itp.
Wartość Source, która jest źródłem wartości parametru, może być rejestrem NSIS ($0, $1, $INSTDIR), stosem NSIS, konkretną wartością (5, "test", itp.) lub pusta (null).
Wartość Destination, która jest docelową wartością parametru po jej zwróceniu, może być rejestrem NSIS, stosem NSIS lub pusta, co oznacza, że nie wymagane jest wyjście.
Obie z tych wartości - source i destination mogą być również kropką (.), jeśli nie są potrzebne.
RETURN jest taki jak pojedyncza definicja parametru, ale wartość source jest używana tylko wtedy, gdy tworzone są funkcje zwrotne. Zazwyczaj wartość source jest kropką.
OPTIONS jest listą opcji, które kontrolują sposób zachowania wtyczki System. Każda z opcji może być wyłączona, poprzez poprzedzenie jej znakiem wykrzyknika. Na przykład: ?!e.
PARAMS, RETURN oraz OPTIONS mogą być powtarzane wielokrotnie w jednej linii Get/Call. Przy powtarzaniu, wiele może być pominięte i tylko to co chcesz zmienić może być użyte. Type, source oraz/lub destination mogą być pominięte dla każdego z parametrów, nawet dla zwracanej wartości. Opcje mogą być dodawane lub usuwane. Pozwala to na definicję prototypów funkcji i ich zapis. Ostatnie dwa przykłady pokazują taką sytuację.
PROC również może być powtarzana, ale musi być poprzedzona znakiem hash (#), poza sytuacją gdy znak hash jest poprzedzony znakiem dwukropka (shell32::#18) - w takim przypadku jest interpretowany jako liczba porządkowa funkcji.
DLL::FUNC
FUNC eksportowana z biblioteki DLL. Przykład: user32::MessageBox
::ADDR
Funkcja znajdująca się w ADDR. Przykład: zobacz poniżej
*ADDR
Struktura znajdująca się w ADDR. Przykład: zobacz poniżej
*
Nowa struktura. Przykład: zobacz poniżej
IPTR->IDX
Człon indeksowany IDX z interfejsu wskazywanego przez IPTR. Przykład: zobacz poniżej
<nic>
Nowa funkcja zwrotna. Przykład: zobacz poniżej
PROC
PROC zwrócona przez Get. Przykład: zobacz poniżej
Dodatkowo, każdy z typów (poza b, h, k oraz @) może być poprzedzony znakiem gwiazdki, żeby oznaczyć wskaźnik. Używając gwiazdki, wtyczka System wymaga raczej podania wartości parametru, niż adresu wskaźnika. Aby podać bezpośredni adres, użyj p, bez gwiazdki. Dostępny jest przykład. Funkcja Alloc zwraca adres i ta zwracana wartość powinna być użyta z p, bez gwiazdki.
Źródło Source jest wymagane, gdy używany jest typ @ i musi być rejestrem. Gdy wywołanie zwraca rejestr źródła source, zawiera już adres pamięci w postaci łańcucha znaków, więc użycie destination nie jest potrzebne.
Funkcje zwrotne są normalnymi funkcjami, które są przekazywane do funkcji i wywoływane przez nią. Używane są one często do podawania możliwie dużego zbioru danych, element po elemencie. Na przykład, EnumChildWindows używa funkcji zwrotnej. Jako że, funkcje NSIS niezupełnie są zwykłymi funkcjami, wtyczka System wprowadza własny mechanizm do obsługi funkcji zwrotnych. Pozwala on na tworzenie funkcji zwrotnych i uzyskiwanie informacji o każdym wywołaniu tych funkcji.
Tworzenie funkcji zwrotnych realizowane jest przez użycie Get oraz składni tworzenia funkcji zwrotnych. Jeśli nie wywołujesz samodzielnie funkcji zwrotnych, źródło source parametrów powinno być pomijane poprzez użycie znaku kropki. Wywołując funkcję zwrotną, destination parametrów zostanie wypełniony wartościami podanymi funkcji zwrotnej. Zwracana wartość funkcji zwrotnej jest ustalana przez źródło zwracanego "parametru". Source zwracanego "parametru" zawsze powinien być ustawiany jak System powiadomi, że funkcja zwrotna została wywołana.
System::Get "(i .r0, i .r1) iss"
Aby podać funkcję zwrotną do funkcji, użyj typu k.
System::Get "(i .r0, i .r1) isR0" Pop $0 System::Call "dll::UseCallback(k r0)"
Za każdym razem, gdy wywoływana jest funkcja zwrotna, ciag znaków callback#, gdzie # jest ilością funkcji zwrotnych, zostanie umieszczony w destination zwracanego "parametru". Pierwsza utworzona funkcja zwrotna równa jest 1, druga to 2, trzecia to 3 i tak dalej. Jako, że System jest pojedynczym wątkiem, funkcja zwrotna może być wywołana tylko podczas wywoływania innej funkcji. Na przykład, funkcja zwrotna EnumChildWindows może być wywołana tylko wtedy, gdy wywoływana jest funkcja EnumChildWindows. Powinieneś więc sprawdzać callback# po wywołaniu kazdej funkcji, która może wywołać twoją funkcję zwrotną.
System::Get "(i .r0, i .r1) isR0" Pop $0 System::Call "dll::UseCallback(k r0)" StrCmp $R0 "callback1" 0 +2 DetailPrint "UseCallback podaje ($0, $1) do funkcji zwrotnej"
Po wywołaniu funkcji zwrotnej, powinieneś użyć funkcji Call, przypisując ją do wartości zwracanej przez Get - funkcję zwrotną. Mówi to wtyczce System, aby zwrócił wartość z funkcji zwrotnej. Destination zwracanego "parametru" musi zostać wyczyszczony przed wywołaniem funkcji, aby uniknąć fałszywej detekcji wywołań funkcji zwrotnych. Jeśli określiłeś source dla zwracanego "parametru", po utworzeniu funkcji zwrotnej, powinieneś zapełnić source odpowiednią zwracaną wartością. Funkcje zwrotne nie są automatycznie zwalniane, więc pamiętaj o tym po zakończeniu ich używania.
System::Get "(i .r0, i .r1) isR0" Pop $0 System::Call "dll::UseCallback(k r0)" loop: StrCmp $R0 "callback1" 0 done DetailPrint "UseCallback podaje ($0, $1) do funkcji zwrotnej" Push 1 # zwracana wartość funkcji zwrotnej StrCpy $R0 "" # czyści $R0, jeśli nie wywołuje się już więcej funkcji zwrotnych System::Call $0 # mówi systemowi, aby zwrócił wartość z funkcji zwrotnej Goto loop done: System::Free $0
Kompletny przykład dostępny jest w sekcji z przykładami.
konwencja wywoływania cdecl (stos przywracany przez wywołującego). Domyślnie, na maszynach x86, używana jest konwencja wywoływania stdcall (stos przywracany przez callee).
Zawsze zwraca (dla GET oznacza to, że powinieneś zdjąć result (wynik) oraz proc, dla CALL oznacza to, że powinieneś zdjąć result (przynajmniej)). Domyślnie wynik zwracany jest tylko przy wystąpieniu błędów (dla GET zdejmujesz wyniki błędów jak i proc, a dla CALL zdejmiesz zwracaną wartość lub wynik w zdefiniowanym miejscu zwracanej wartości).
Brak przedefiniowań. Zawsze gdy tego użyjesz, nie bedzie nigdy przedefiniowywany zarówno przez GET jak i CALL. Opcja ta nigdy nie jest dziedziczona przez potomków.
Użyj ogólnego stosu (Stack). Jeśli pierwsza funkcja zwrotna jest zdefiniowana system zaczyna używać tymczasowego stosu dla wywołań funkcji.
Wywołaj GetLastError() po zakończeniu procedury i umieść wynik na stosie.
Wyładuj bibliotekę DLL po wywołaniu (używając FreeLibrary, będziesz mógł np. dokonać operacji usunięcia).
Eksperymentalna składnia v2.
Typy struktur pisane wielkimi literami są dostosowane do ich naturalnego wyrównania. Typy pisane małymi literami są pakowane bez wyrównania.
ID funkcji zwrotnej Callback id oparte jest na zaalokowanej funkcji zwrotnej
System::Call "user32::MessageBox(p $HWNDPARENT, t 'Wtyczka NSIS - System', t 'Test', i 0)" System::Call '"$SysDir\MyLibrary.dll"::MyFunction(i 42)'
System::Call "kernel32::GetModuleHandle(t 'user32.dll') p .s" System::Call "kernel32::GetProcAddress(p s, m 'MessageBoxA') p .r0" System::Call "::$0(p $HWNDPARENT, m 'GetProcAddress test', m 'Wtyczka NSIS - System', i 0)"
System::Get "user32::MessageBox(p $HWNDPARENT, t 'To jest domyślny tekst', t 'Domyślny', i 0)" Pop $0 System::Call "$0"
System::Get "user32::MessageBox(p $HWNDPARENT, t 'To jest domyślny tekst', \ t 'Domyślny', i 0x1|0x10)" Pop $0 System::Call "$0(, 'To jest test System::Get', 'Wtyczka NSIS - System',)"
System::Call "advapi32::GetUserName(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2" DetailPrint "Nazwa użytkownika - $0" DetailPrint "Długość łańcucha znaków - $1" DetailPrint "Zwracana wartość - $2"
System::Alloc 4 Pop $0 System::Call "*$0(i 5)" ; Zapis System::Call "*$0(i .r1)" ; Odczyt System::Free $0 DetailPrint $1
System::Call "*(i 5) p .r0" System::Call "*$0(i .r1)" System::Free $0 DetailPrint $1
System::Call '*0(p, &l.r2, &t2)' ; &l. nie jest częścią struktury DetailPrint "Struct size=$2" System::Call '*(&l4,i,i,i,i,&t128)p.r1' ; Wypełnia funkcję dwOSVersionInfoSize rozmiarem struktury int32 ${If} $1 Z<> 0 System::Call 'kernel32::GetVersionEx(pr1)i.r0' System::Call '*$1(i,i.R1,i.R2,i.R3)' System::Free $1 ${IfThen} $0 <> 0 ${|} DetailPrint "v$R1.$R2.$R3" ${|} ${EndIf}
System::Call "user32::GetClientRect(p $hwndparent, @ r0)" System::Call "*$0(i,i,i.r1,i.r2)" DetailPrint ClientRect=$1x$2
# definicje !define CLSCTX_INPROC_SERVER 1 !define CLSID_ActiveDesktop {75048700-EF1F-11D0-9888-006097DEACF9} !define IID_IActiveDesktop {F490EB00-1240-11D1-9888-006097DEACF9} # utwórz interfejs IActiveDesktop System::Call "ole32::CoCreateInstance( \ g '${CLSID_ActiveDesktop}', p 0, \ i ${CLSCTX_INPROC_SERVER}, \ g '${IID_IActiveDesktop}', *p .r0) i.r1" StrCmp $1 0 0 end # wywołaj IActiveDesktop->GetWallpaper System::Call "$0->4(w .r2, i ${NSIS_MAX_STRLEN}, i 0)" # wywołaj IActiveDesktop->Release System::Call "$0->2()" # wydruk wyników DetailPrint $2 end:
InitPluginsDir File "/oname=$PLUGINSDIR\MyDLL.dll" MyDLL.dll System::Call 'KERNEL32::AddDllDirectory(w "$PLUGINSDIR")' System::Call 'KERNEL32::LoadLibrary(t "$PLUGINSDIR\MyDLL.dll")p.r1' System::Call 'MyDLL::MyFunc(i 5) ? u' System::Call 'KERNEL32::FreeLibrary(pr1)' Delete $PLUGINSDIR\MyDLL.dll
System::Get "(p.r1, p) iss" Pop $R0 System::Call "user32::EnumChildWindows(p $HWNDPARENT, k R0, p) i.s" loop: Pop $0 StrCmp $0 "callback1" 0 done System::Call "user32::GetWindowText(pr1,t.r2,i${NSIS_MAX_STRLEN})" System::Call "user32::GetClassName(pr1,t.r3,i${NSIS_MAX_STRLEN})" IntFmt $1 "0x%X" $1 DetailPrint "$1 - [$3] $2" Push 1 # zwracana wartość funkcji zwrotnej System::Call "$R0" Goto loop done: System::Free $R0 DetailPrint "Funckja EnumChildWindows zwróciła $0"
System::Get '(m.r1)ir2r0 ?2' ; składnia v2 Pop $9 System::Call 'kernel32::EnumSystemLocalesA(k r9, i 0)' loop: StrCmp $0 "callback$9" 0 done DetailPrint "Locale: $1" StrCpy $2 1 ; zwracana wartość funkcji EnumLocalesProc System::Call $9 ; wynik funkcji EnumLocalesProc Goto loop done: System::Free $9
System::Call '*(&t50 "!")p.r2' ; DecimalSep System::Call '*(&t50 "`")p.r3' ; ThousandSep System::Call '*(i 2, i 0, i 3, P r2, P r3, i 1)p.r1 ?2' System::Call 'kernel32::GetNumberFormat(i 0, i 0, t "1337.666" r4, p r1, t.r5, i ${NSIS_MAX_STRLEN})' DetailPrint "Formatowanie użytkownika $4: $5" System::Free $3 System::Free $2 System::Free $1
!define MB "user32::MessageBox(p$HWNDPARENT,t,t'Wtyczka NSIS - System',i0)" System::Call "${MB}(,'mój komunikat',,)" System::Call "${MB}(,'inny komunikat',,) i.r0" MessageBox MB_OK "Ostatnie wywołanie zwróciło $0"
System::Call "user32::SendMessage(p $HWNDPARENT, t 'test', t 'test', p 0) p.s ? \ e (,t'Test zamieniania',,) i.r0 ? !e #user32::MessageBox" DetailPrint $0 ClearErrors Pop $0 IfErrors good MessageBox MB_OK "To okno z komunikatem nie zostanie wyświetlone" good:
Int64Op ARG1 OP [ARG2]
Wykonuje OP na ARG1 i opcjonalnie ARG2 oraz zwraca wynik na stosie. Zarówno ARG1 jak i ARG2 są 64-bitowymi liczbami całkowitymi. Oznacza to, że mogą one operować w zakresie pomiędzy -2^63 a 2^63 - 1.
System::Int64Op 5 + 5 Pop $0 DetailPrint "5 + 5 = $0" # 10
System::Int64Op 526355 * 1565487 Pop $0 DetailPrint "526355 * 1565487 = $0" # 824001909885
System::Int64Op 5498449498849818 / 3 Pop $0 DetailPrint "5498449498849818 / 3 = $0" # 1832816499616606
System::Int64Op 0x89498A198E4566C % 157 Pop $0 DetailPrint "0x89498A198E4566C % 157 = $0" # 118
System::Int64Op 1 << 62 Pop $0 DetailPrint "1 << 62 = $0" # 4611686018427387904
System::Int64Op 0x4000000000000000 >> 62 Pop $0 DetailPrint "0x4000000000000000 >> 62 = $0" # 1
System::Int64Op 0x8000000000000000 >> 1 Pop $0 DetailPrint "0x8000000000000000 >> 1 = $0" # -4611686018427387904 (0xC000000000000000)
System::Int64Op 0x8000000000000000 >>> 1 Pop $0 DetailPrint "0x8000000000000000 >>> 1 = $0" # 4611686018427387904 (0x4000000000000000)
System::Int64Op 0x12345678 & 0xF0F0F0F0 Pop $0 # Operacja IntFmt jest 32-bitowa, to tylko przykład IntFmt $0 "0x%X" $0 DetailPrint "0x12345678 & 0xF0F0F0F0 = $0" # 0x10305070
System::Int64Op 1 ^ 0 Pop $0 DetailPrint "1 ^ 0 = $0" # 1
System::Int64Op 1 || 0 Pop $0 DetailPrint "1 || 0 = $0" # 1
System::Int64Op 1 && 0 Pop $0 DetailPrint "1 && 0 = $0" # 0
System::Int64Op 9302157012375 < 570197509190760 Pop $0 DetailPrint "9302157012375 < 570197509190760 = $0" # 1
System::Int64Op 5168 > 89873 Pop $0 DetailPrint "5168 > 89873 = $0" # 0
System::Int64Op 189189 = 189189 Pop $0 DetailPrint "189189 = 189189 = $0" # 1
System::Int64Op 156545668489 ~ Pop $0 DetailPrint "156545668489 ~ = $0" # -156545668490
System::Int64Op 1 ! Pop $0 DetailPrint "1 ! = $0" # 0
Jak mogę przypisać struktury do funkcji?
Przede wszystkim, musisz zaalokować strukturę. Możesz to zrobić na dwa sposoby. Możesz użyć polecenia Alloc lub Call ze specjalną składnią alokowania struktury. Następnie, jeśli chcesz przypisać dane do struktury, musisz wypełnić ją danymi. Gdy to zrobisz, wywołaj funkcję z wskaźnikiem na strukturę. Na koniec, jeśli chcesz odczytac dane ze struktury, które mogły zostac tam zapisane przez wywołaną funkcję, musisz użyć polecenia Call ze składnią obsługujacą struktury. Gdy wszystko już zostanie zrobione, pamiętaj o zwolnieniu struktury.
Aby zaalokować strukturę, używając polecenia Alloc, musisz znać rozmiar struktury w bajtach. Dlatego też, łatwiej będzie użyć polecenia Call. W takim przypadku łatwo zobaczyć, że wymagany rozmiar to 16 bajtów, ale inne przypadki nie muszą byc tak proste. W obu przypadkach, adres struktury zostanie ulokowany na górze stosu i powinien byc stamtąd zdjęty przy użyciu polecenia Pop.
System::Alloc 16
System::Call "*(i, i, i, t)p.s"
Ustawianie danych można przeprowadzić używając polecenia Call. Można to zrobić w fazie alokowania, lub innej używając składni obsługującej struktury.
System::Call "*(i 5, i 2, i 513, t 'test')p.s"
# adres pamięci struktury przechowywany jest w $0 System::Call "*$0(i 5, i 2, i 513, t 'test')"
Wszystkie metody alokacji zwracają adres, typ wprowadzanych danych powinien być liczbą całkowitą, adresem w pamięci.
# adres pamięci struktury przechowywany jest w $0 System::Call "dll::func(p r0)"
Odczyt danych ze struktury odbywa się przy użyciu tej samej składni co ich ustawianie. Jedyną różnicą jest to, że część destination parametru zostanie ustawiona, zaś część source zostanie pominięta poprzez użycie kropki.
# adres pamięci struktury przechowywany jest w $0 System::Call "*$0(i .r0, i .r1, i .r2, t .r3)" DetailPrint "pierwszy int = $0" DetailPrint "drugi int = $1" DetailPrint "trzeci int = $2" DetailPrint "łańcuch znaków = $3"
Pamięć jest zwalniana przy użyciu polecenia Free.
# adres pamięci struktury przechowywany jest w $0 System::Free $0
# alokacja System::Call "*(i,i,p,p,p,p,p,p)p.r1" # wywołanie System::Call "Kernel32::GlobalMemoryStatus(p r1)" # pobranie System::Call "*$1(i.r2, i.r3, p.r4, p.r5, p.r6, p.r7, p.r8, p.r9)" # zwolnienie System::Free $1 # wydruk danych DetailPrint "Rozmiar struktury: $2 bajtów" DetailPrint "Używana pamięć: $3%" DetailPrint "Całkowity rozmiar pamięci fizycznej: $4 bajtów" DetailPrint "Wolnej pamięci fizycznej: $5 bajtów" DetailPrint "Całkowity rozmiar pliku stronicowania: $6 bajtów" DetailPrint "Wolnego w pliku stronicowania: $7 bajtów" DetailPrint "Całkowity rozmiar pamięci wirtualnej: $8 bajtów" DetailPrint "Wolnej pamięci wirtualnej: $9 bajtów"