|
![]() |
Olli
Einführung
Hier soll es um ![]() ![]() ![]() ![]() Voraussetzung: Es wird entsprechend der Verfügbarkeit der besprochenen API mindestens Windows 2000 vorausgesetzt! Environment Block (EB) Nun ist es so, daß man, wie bei einigen anderen Win32-APIs auch, einen sog. Environment-Block (dt.: "Umgebungsblock") - ab hier abgekürzt als EB - übergeben kann (, nicht muß!). Und genau hier gibt es offenbar ein Verständnisproblem.
Zuersteinmal kennt jeder von euch sicher schon den EB?! Richtig? Wer jetzt sagt: Nein! der darf mal die <Windowstaste+R> drücken und dann "CMD /K SET" (ohne die Anführungsstriche) eingeben und <Enter> drücken. In dem Konsolenfenster, welches sich daraufhin öffnet, sieht man die Ausgabe des EB. Jetzt geht dem einen oder anderen vielleicht schon ein Licht auf. Der EB enthält also die Umgebungsvariablen (%PATH% ist z.B. sehr wichtig!). Der Befehl "SET" ist nicht nur zur Ausgabe, sondern auch zum Setzen von Umgebungsvariablen zuständig. Daher sollten sich alle, die mehr Infos zu diesem Befehl wollen, diese mit "SET /?" selber holen ![]() Spezielle generische Eigenarten ![]() Nun da wir wissen was der EB enthält, kommt die nächste Sache: warum ist es ein generischer Pointertyp? ( ![]() ![]() Die Frage ist sehr einfach zu beantworten. Da man sowohl einen Pointer auf eine nullterminierte Kette von ANSI-Zeichen ( ![]() ![]() ![]() Datenformat Gut, also weiter im Text. Was bedeutet die Beschreibung im PSDK? Nunja, einigen, z.B. jenen die bereits mit den Registrywerten der Leistungsprotokolle (Performance) zu tun hatten; und auch jenen, die REG_MULTI_SZ schreiben mußten; oder sogar jenen die direkt auf API-Ebene den Datei-Öffnen/Speichern-Dialog aufgerufen haben, wird die Struktur bekannt vorkommen. In all diesen Fällen gilt nämlich, daß es sich um eine Liste von Listen von nullterminierten Zeichenketten handelt. Wie funktioniert das? Schauen wir uns ein Beispiel an, welches mit Delphi-Literalen veranschaulicht wird:
Delphi-Quellcode:
Hinweis: bla1 und bla2 enthalten identische Werte. Ich wollte nur darauf aufmerksam machen, daß man zwischen Zeichenliteralen (#0 und #0) auch das "+" weglassen kann.
const bla1 = 'String1' + #0 + 'String2' + #0 + 'LetzterString' + #0 + #0;
const bla2 = 'String1' + #0 + 'String2' + #0 + 'LetzterString' + #0#0; Dies ist also alles was uns das PSDK zu vermitteln sucht. Es ist einfach eine Aneinanderkettung von Einzelstrings, welche durch #0 getrennt und mit einem Extra-"#0" abgeschlossen sind. Nun ist das ja ziemlich kompliziert, wenn wir uns vorstellen, daß wir einen Puffer erstellen müßten, den wir mit Nullen füllen um danach auf die gezeigte Weise die Strings aneinanderzufügen. Aber das ist nicht halb so kompliziert wie man zuerst meinen könnte ... So geht's in Delphi So wird's gemacht: Delphis Stringtypen ![]() ![]() ![]() ![]() In Delphi schreiben wir also tatsächlich
Delphi-Quellcode:
oder
var StringVariable: AnsiString;
begin StringVariable := 'String1' + #0 + 'String2' + #0 + 'LetzterString' + #0 + #0; end;
Delphi-Quellcode:
Wobei man sofort bemerkt, daß sich nur der Typ der StringVariablen ändert, nicht jedoch der eigentliche Code der Zuweisung. Auch dies kann man sich zunutzemachen.
var StringVariable: WideString;
begin StringVariable := 'String1' + #0 + 'String2' + #0 + 'LetzterString' + #0 + #0; end; Erklärung: Ich verwende StringType und CharType ab jetzt als Platzhalter für WideString/AnsiString und WideChar/AnsiChar - wobei es auch die zusammengesetzte Form PWideChar/PAnsiChar geben wird! Selbstgekocht ist am leckersten ... Der EB möchte also beispielsweise folgende Form für zu definierende Umgebungsvariablen PATH und USERNAME:
Delphi-Quellcode:
War doch eigentlich ganz einfach, oder?
var StringVariable: StringType;
begin StringVariable := 'USERNAME=ottokar' + #0 + 'PATH=C:\Bla' + #0 + #0; end; ![]() ... abgekupfert schmeckt aber auch nicht schlecht ... Statt sich den EB mühsam selbst zusammenzubasteln, gibt es noch die Variante sich den EB des aktuellen Prozesses über eine API zu kopieren. Die besagte API heißt ![]() ![]()
Delphi-Quellcode:
Da man sich vorher noch vom aktuellen Prozeß das Token holen muß und auch noch Checks auf die Rückgabewerte nötig sind, ist dies ein typischer Kandidat für eine Wrapperfunktion:
function CreateEnvironmentBlock(
var lpEnvironment: LPVOID; hToken: HANDLE; bInherit: Boolean ): Boolean; stdcall; external 'userenv.dll'; function DestroyEnvironmentBlock( lpEnvironment: LPVOID ): Boolean; stdcall; external 'userenv.dll';
Delphi-Quellcode:
Diese Funktion gibt bei Erfolg den Pointer auf den EB (in Unicodeform!) zurück, oder nil bei Mißerfolg.
(******************************************************************************
Function to get a copy of the current environment. The returned pointer MUST BE DESTROYED using the DestroyEnvironmentBlock() API. ******************************************************************************) function GetCurrentEnvironmentBlock(): LPVOID; var hToken: HANDLE; begin Result := nil; // Open process token of current process if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY or TOKEN_DUPLICATE, hToken)) then try // Create copy of current environment block if (not CreateEnvironmentBlock(Result, hToken, True)) then Result := nil; finally CloseHandle(hToken); // Close the token of the current process end; end; Es wären also da die Varianten der Übergabe eines eigenen selbstgebastelten EB, eines kopierten EB und es wird noch eine weitere hinzukommen. Praxis Da ich mal hoffe, daß alle Leser halbwegs mit der Delphi-Syntax vertraut sind und sich entsprechende Änderungen (zusätzliche Parameter an die API übergeben, usw.) selbst implementieren können. Mein Code beschränkt sich auf eine Untermenge der verfügbaren Parameter und verwaltet den Rest intern. Dadurch muß man z.B. schonmal keine Strukturen übergeben - die sowieso nur relevant wären um Fenstergrößen und -positionen zu übergeben. Außerdem ist meine Funktion insofern hartkodiert, als daß sie immer CREATE_UNICODE_ENVIRONMENT in den Flags benutzt (zumal GetCurrentEnvironmentBlock() ja auch die Kopie als Unicode zurückgibt ![]()
Delphi-Quellcode:
Ich führe also die neue Funktion CreateProcessWithLogonWrapper() ein, die einfach nur die - für uns wichtigsten - Parameter weitergibt und den Rest intern selbermacht.
(******************************************************************************
Function to wrap around the CreateProcessWithLogon() API ******************************************************************************) function CreateProcessWithLogonWrapper( lpUsername: LPCWSTR; lpDomain: LPCWSTR; lpPassword: LPCWSTR; lpApplicationName: LPCWSTR; lpCommandLine: LPWSTR; dwCreationFlags: DWORD; lpEnvironment: LPVOID = nil ): Boolean; var sui: STARTUPINFOW; pi: PROCESS_INFORMATION; begin Result := False; // Fill sui with zeroes ZeroMemory(@sui, sizeof(sui)); // Fill size member sui.cb := sizeof(sui); // Create process if (CreateProcessWithLogonW( lpUsername, lpDomain, // Domain lpPassword, LOGON_WITH_PROFILE, // No special options lpApplicationName, // Module to execute lpCommandLine, // Activates extensions dwCreationFlags or CREATE_UNICODE_ENVIRONMENT, // Only these options for now lpEnvironment, nil, // Use current directory sui, // STARTUPINFO pi // PROCESS_INFORMATION )) then Result := True; // Return success end; Für uns ist ja im Kontext dieses Themas nur interessant, wie sich unsere Funktion bei den verschiedenen Varianten als Übergabe für lpEnvironment schlägt. Aber Moment ... ich hatte noch eine 3te Variante versprochen. Und hier ist sie denn auch: nil. Wenn wir im PSDK auf NULL stoßen, so ist dies ein Alias für nil in Delphi. In der Beschreibung zu ![]() auf Englisch ...: Pointer to an environment block for the new process. If this parameter is NULL, the new process uses the environment of the specified user instead of the environment of the calling process. ... und auf Deutsch heißt das: Zeiger auf den Umgebungsblock für den neuen Prozeß. Wenn dieser Parameter NULL ist, benutzt der neue Prozeß die Umgebung des angegebenen Benutzers statt der Umgebung des aufrufenden Prozesses Vergleichen wir nun alle 3 beschriebenen Methoden zur Übergabe des EB. Dazu übergeben wir einmal nil, einmal den selbstgebastelten EB und zuguterletzt den kopierten EB. Das gesamte Projekt mit dem das nachvollzogen werden kann, gibt es im Anhang. Hier sollten nur die Ergebnisse besprochen werden (jeweils der gleiche Ausschnitt an Variablen, sofern vorhanden):
Aufpassen ... Sieh mal einer an. Im Fall #1 wird also tatsächlich eine neue Umgebung erzeugt (ja, mein Testnutzerkonto heißt "ottokar"). In Fall #2 wird wiederum nur das wiedergegeben, was wir übergeben hatten. Im letzten Fall (#3) wird statt des angepaßten EB (siehe #1) tatsächlich eine exakte Kopie des aktuellen EB übergeben. Das Schmankerl zum Schluß ... Ganz zum Schluß möchte ich euch noch zwei Hilfsfunktionen (sind ebenfalls im Anhang enthalten) mit auf den Weg geben, die benutzt werden können um einen EB auszulesen:
Delphi-Quellcode:
EDIT: ringlis Einwand von
(******************************************************************************
Unicode version to dump the environment created using CreateEnvironmentBlock() ******************************************************************************) procedure DumpEnvironmentW(lpEnvironment: LPVOID); var Env: PWideChar; begin Env := lpEnvironment; Writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'); while (lstrlenW(Env) > 0) do begin Writeln(string(WideString(Env))); Env := PWideChar(DWORD(Env) + DWORD(lstrlenW(Env) + 1) * DWORD(sizeof(Env^))); end; Writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'); end; (****************************************************************************** ANSI version to dump the environment created using CreateEnvironmentBlock() ******************************************************************************) procedure DumpEnvironmentA(lpEnvironment: LPVOID); var Env: PAnsiChar; begin Env := lpEnvironment; Writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'); while (lstrlenA(Env) > 0) do begin Writeln(string(Env)); Env := PAnsiChar(DWORD(Env) + DWORD(lstrlenA(Env) + 1) * DWORD(sizeof(Env^))); end; Writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'); end; ![]() |
Olli |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |