|
Antwort |
Olli
Einführung
Hier soll es um CreateProcessWithLogonW() gehen, jene Funktion die seit einiger Zeit häufiger benutzt wird, weil manche den Umweg über das alte CreateProcessAsUser, dem man ein Token des bereits angemeldeten Benutzers (siehe LogonUser(), benötigte aber z.B. das Privileg SE_TCB_NAME) übergeben mußte. Dagegen ist nichts einzuwenden, zumal offenbar keinerlei spezielle Privilegien notwendig sind um CreateProcessWithLogonW() aufzurufen, bei der älteren Alternative jedoch schon. 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? (LPVOID entspricht Delphis Pointer) Die Frage ist sehr einfach zu beantworten. Da man sowohl einen Pointer auf eine nullterminierte Kette von ANSI-Zeichen (PAnsiChar, entspricht aktuell PChar) als auch auf eine nullterminierte Kette von Unicode-Zeichen (PWideChar) übergeben kann, ist es relativ klar, daß man keinen dieser beiden Typen bevorzugen kann. Stattdessen wird ein generischer Pointertyp gewählt. Alles klar? 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 AnsiString (je nach Compilereinstellung ist String identisch mit AnsiString) und WideString sind ja "counted strings" also gezählte Stringtypen. Den Unterschied macht hier, daß wir in einen solchen gezählten Strings nicht darauf angewiesen sind, daß ein #0 (Zeichen mit den Ordinalwert 0) den String abschließt. Es ist also durchaus zulässig in einen Delphistring eine #0 zu schreiben. Genau dies machen wir uns zunutze ... ja sogar noch besser, dank der Tatsache daß #0 im Kontext eines WideStrings eben das WideChar #0 beschreibt, ist der Code sogar fast portabel 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 CreateEnvironmentBlock(). Nachfolgend die Deklarationen dieser Funktion und ihres Gegenparts (DestroyEnvironmentBlock) in Delphi:
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 )! Die Parameternamen entsprechen denen der Ursprungs-API, bedürfen also keiner speziellen Erklärung.
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 CreateProcessWithLogonW() steht nun: 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 hier eingearbeitet. Ich hatte fälschlicherweise die Beschreibung einer anderen Funktion im PSDK aufgerufen. Die Kommentare im Anhang wurden entsprechend korrigiert.
(******************************************************************************
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 |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
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 |
LinkBack URL |
About LinkBacks |