![]() |
Problem mit ShellMessageBox [gelöst]
Ich habe mal wieder eine kleine Spielerei inkl. Frage für die Interessierten unter euch. Ein Beitrag im DF hat mich wieder daran erinnert, dass ich auf der Platte noch eine HTML-Seite mit ein paar so genannten undokumentierten Funktionen von Windows habe. Darunter auch die "ShellMessageBox", s. hier:
Delphi-Quellcode:
Das Interessante an der Box ist nun, das man gleich Stringressourcen angeben kann, ohne diese vorher laden zu müssen, etwa
type
TShellMessageBoxA = function(Module: THandle; Owner: HWND; Text: pchar; Caption: pchar; Style: UINT; Parameters: array of pointer): integer; cdecl; TShellMessageBoxW = function(Module: THandle; Owner: HWND; Text: PWideChar; Caption: PWideChar; Style: UINT; Parameters: array of pointer): integer; cdecl; // Die "cdecl"-Deklaration ist kein Fehler. var ShellMessageBoxA : TShellMessageBoxA; ShellMessageBoxW : TShellMessageBoxW; dll : dword = 0; begin dll := LoadLibrary('shell32.dll'); if(dll <> 0) then begin @ShellMessageBoxA := GetProcAddress(dll,pointer(183)); @ShellMessageBoxW := GetProcAddress(dll,pointer(182)); if(@ShellMessageBoxA = nil) or (@ShellMessageBoxW = nil) then begin MessageBox(0,'Funktionen wohl nicht vorhanden',nil,0); FreeLibrary(dll); exit; end; { ... } FreeLibrary(dll); end; end.
Delphi-Quellcode:
(Klappt übrigens beim Titel auch!) Oder man kann mit der Escape-Sequenz %n (s. auch PSDK ->
ShellMessageBoxA(hInstance,self.Handle,
MAKEINTRESOURCE(1000), // <-- Verweis auf Stringressource nil,MB_OK,[nil]); ![]()
Delphi-Quellcode:
Aber eigentlich interessiert mich der letzte Parameter (das Pointer-Array). Wie bei "FormatMessage" soll man nämlich (ähnlich wie bei "wvsprintf" oder "Format") Strings ersetzen können. Dazu soll man eigentlich eine Sequenz wie #n nutzen, wobei das N eine Zahl von 1 bis 99 sein kann. Nur irgendwie bekomme ich keinen Fuß in die Tür (mal so gesprochen). Es wird alles angezeigt, nur nicht das was ich will:
ShellMessageBoxA(hInstance,self.Handle,
'Hallo, Echo!%nHallo, Otto! :o)', nil,0,[nil]);
Delphi-Quellcode:
Ich mache irgendwas falsch.
szString := 'Hallo, Welt!';
ShellMessageBoxA(hInstance,self.Handle, '%1',nil,0,[pointer(szString)]); Aber was? :gruebel: |
Re: Problem mit ShellMessageBox
Zitat:
Delphi-Quellcode:
Gruss Nico
program ShMsgBox;
uses Windows, ShellAPI; type {$IFDEF UNICODE} LPCTSTR = PWideChar; {$ELSE} LPCTSTR = PAnsiChar; {$ENDIF} //////////////////////////////////////////////////////////////////////////////// // // ShellMessageBox // // ( [url]http://search.microsoft.com/?View=msdn&qu=ShellMessageBox[/url] ) // // Notes: // Maximum used ressource string length is 80 chars // ShellMessageBox adds MB_SETFOREGROUND to fuStyle // function ShellMessageBox(Inst: HINST; Wnd: HWND; Msg, Title: LPCTSTR; Style: UINT; const Arguments: array of LPCTSTR): Integer; stdcall; type TFNShellMessageBoxA = function(hInst: HINST; hWnd: HWND; pszMsg: LPCSTR; pszTitle: LPCSTR; fuStyle: UINT {...}): Integer; cdecl; TFNShellMessageBoxW = function(hInst: HINST; hWnd: HWND; pszMsg: LPCWSTR; pszTitle: LPCWSTR; fuStyle: UINT {...}): Integer; cdecl; TFNShellMessageBox = function(hInst: HINST; hWnd: HWND; pszMsg: LPCTSTR; pszTitle: LPCTSTR; fuStyle: UINT {...}): Integer; cdecl; const ShellMessageBoxProcNameA = 'ShellMessageBoxA'; ShellMessageBoxProcNameW = 'ShellMessageBoxW'; ShellMessageBoxProcOrdA = PAnsiChar(183); ShellMessageBoxProcOrdW = PAnsiChar(182); {$IFDEF UNICODE} ShellMessageBoxProcName = ShellMessageBoxProcNameW; ShellMessageBoxProcOrd = ShellMessageBoxProcOrdW; {$ELSE} ShellMessageBoxProcName = ShellMessageBoxProcNameA; ShellMessageBoxProcOrd = ShellMessageBoxProcOrdA; {$ENDIF} var ShellModule: HMODULE; FNShellMessageBox: TFNShellMessageBox; begin Result := 0; ShellModule := LoadLibrary(shell32); if (ShellModule <> 0) then try FNShellMessageBox := TFNShellMessageBox( GetProcAddress(ShellModule, ShellMessageBoxProcName)); if not Assigned(FNShellMessageBox) then FNShellMessageBox := TFNShellMessageBox( GetProcAddress(ShellModule, ShellMessageBoxProcOrd)); if Assigned(FNShellMessageBox) then begin asm push ebx // save EBX mov ecx,[Arguments-$04] // High(Arguments) mov ebx,ecx // offset to last argument shl ebx,$02 // (High * SizeOf) inc ecx // Length(Arguments) jz @@call // no arguments? mov edx,[Arguments] // first argument add edx,ebx // last argument @@loop: push dword ptr [edx] // push argument sub edx,$04 // next argument loop @@loop // until ecx = 0 @@call: push dword ptr [Style] // push params push dword ptr [Title] // ... push dword ptr [Msg] push dword ptr [Wnd] push dword ptr [Inst] call FNShellMessageBox // call function add esp,$18 // cleanup stack (params) add esp,ebx // cleanup stack (arguments) mov [Result],eax // return value into Result pop ebx // restore EBX end; end; finally FreeLibrary(ShellModule); end; end; //////////////////////////////////////////////////////////////////////////////// // Sample begin ShellMessageBox(0, 0, 'function %2(%4: %3; %6: %5; %8, %10: %7; %12: %11 {...}): %1;', 'ShellMessageBox', MB_OK or MB_DEFBUTTON1 or MB_ICONINFORMATION, ['Integer', 'ShellMessageBox', 'HINST', 'hInst', 'HWND', 'hWnd', 'LPCTSTR', 'pszMsg', 'LPCTSTR', 'pszTitle', 'UINT', 'fuStyle']); end. [edit] BASM Optimierungen [/edit] [edit] Anpassungen D2-D6 [/edit] [edit] Unicode-Anpassung [/edit] |
Re: Problem mit ShellMessageBox
Hallo Nico.
Dankeschön kann ich da nur sagen. Zitat:
Als kleine Frage, just for the sake of completeness, :oops:, was genau passiert im Assemberteil? Zitat:
Gruß. PS: Ich setze trotzdem mal den Erledigt-Haken, denn das Grundproblem ist ja gelöst. Alles, was jetzt noch an Fragen und Antworten kommt, wäre nur Bonus. ;) |
Re: Problem mit ShellMessageBox
Zitat:
Zitat:
Code:
[color=blue] { EBX muss gesichert werden (wird auf dem Stack abgelegt) }[/color]
[color=gray] push ebx[/color] [color=blue] { Bei 'array of' befindet sich vor dem ersten Element der Wert von High }[/color] [color=gray] mov ecx,[Arguments-$04][/color] [color=blue] { Hier wird der Offset zum letzten Element zusammengebaut (High*4) }[/color] [color=gray] mov ebx,ecx[/color] [color=gray] shl ebx,$02[/color] [color=blue] { Aus dem High(Arguments) wird nun Length(Arguments) (+1) }[/color] [color=gray] inc ecx[/color] [color=blue] { Prüfen ob es gar keine zusätzlichen Argumente gibt (ecx ist 0) }[/color] [color=blue] { Wenn dann geht es sofort zur Übergabe der benötigten Parameter }[/color] [color=gray] jz @@call[/color] [color=blue] { Erste Element holen und dann zum letzten Element wechseln }[/color] [color=blue] { cdecl erwartet die Parameter in 'umgekehrter' Reihenfolge }[/color] [color=gray] mov edx,[Arguments][/color] [color=gray] add edx,ebx[/color] [color=blue] { Aktuelles Element auf dem Stack ablegen }[/color] [color=gray]@@loop: push dword ptr [edx][/color] [color=blue] { Zum nächsten Element wechseln (rückwärts) }[/color] [color=gray] sub edx,$04[/color] [color=blue] { ECX um 1 verringern und auf 0 prüfen }[/color] [color=blue] { solange ECX <> 0 weiter mit Schleife }[/color] [color=gray] loop @@loop[/color] [color=blue] { Nun die benötigten Parameter auf dem Stack ablegen }[/color] [color=gray]@@call: push dword ptr [Style][/color] [color=gray] push dword ptr [Title][/color] [color=gray] push dword ptr [Msg][/color] [color=gray] push dword ptr [Wnd][/color] [color=gray] push dword ptr [Inst][/color] [color=blue] { Funktion aufrufen (Ergebnis ist dann in EAX) }[/color] [color=gray] call FNShellMessageBox[/color] [color=blue] { Stack mit benötigten Parametern abräumen }[/color] [color=blue] { ($14, aber ebx basiert nicht auf Length, sondern auf High) }[/color] [color=gray] add esp,$18[/color] [color=blue] { Stack mit zusätzlichen Argumenten abräumen }[/color] [color=gray] add esp,ebx[/color] [color=blue] { Ergebnis in Result speichern }[/color] [color=gray] mov [Result],eax[/color] [color=blue] { EBX wiederhergestellen (vom Stack holen) }[/color] [color=gray] pop ebx[/color] Zitat:
Delphi-Quellcode:
...wäre viel zu unsicher (da es von der Annahme ausgeht, dass der Compiler keine Anweisung zischen die asm-Bläcke und den Aufruf der Funktion packt).
asm
push ... end; ShellMessageBox(...) asm add esp,... end; Gruss Nico ps: kurz vor Deiner Antwort hab ich oben noch ne Kleinigkeit geändert :) [edit] elende Tippfehler, nicht mein Tag heute ... [/edit] |
Re: Problem mit ShellMessageBox
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Zitat:
@all: Ich hänge mal das Ergebnis ran. Ein kleines Programm zum Rumspielen mit noch ein paar anderen Funktionen. Inkl. Hilfedatei (undok.chm) und gepatchter "base.chm" für die CHM-Version der Win32-API-Tutorials (@Luckie: :stupid:)), wodurch der Inhalt der "undok.chm" unter den Systemfunktionen erscheint. Die Erklärung für den Assemblerteil habe ich 1:1 von Nico übernommen. Das einzige, das ich dazu beigetragen habe, ist, dass man´s ein- und ausblenden kann ... :mrgreen: Ach ja, das Programm verursacht absichtlich zwei Compilerfehler, damit ihr lest was an der Stelle im Quellcode steht. Nochmals Danke, @Nico. Grüße. |
Re: Problem mit ShellMessageBox
Zitat:
Edit 23 erweiterte es - dank Deines Hinweises - um Kommentare im BASM-Code... (habe unbewußt versucht einen neuen Tippfehler/Edit-Record aufzugestellen *g*) |
Re: Problem mit ShellMessageBox [gelöst]
Liste der Anhänge anzeigen (Anzahl: 1)
Auf Einwand bzw. Hinweis von Nico wollte ich noch was zum Anhang sagen:
Das Beispielprogramm ist ein Beispiel wie man lieber nicht programmieren sollte. Das liegt an dem Mischmasch von Ansi- und Unicode. Die einzige Sache, die richtig funktioniert, ist Nicos "ShellMessageBox". Aber das liegt daran, dass diese Funktion sowohl als Ansi- als auch als Unicode-Version exportiert wird. Beim "Anderes Symbol"-Dialog (PickIconDlg) sieht das schon anders aus. Den Dialog gibt es auch unter 9x, dort allerdings undokumentiert und nur als Ansi-Version. Den PSDK-Deklarationen zufolge benutzt der Dialog ab und unter Windows 2000 aber Widestrings (also Unicode, salopp gesagt). Wenn ich unter XP die AnsiChar-Version nutze, dann erhalte ich die Fehlermeldung, weil der Dateiname nicht erkannt wird (s. Bild im Anhang). Das gleiche mit umgekehrten Vorzeichen passiert unter 98, wenn ich dort die Unicode-Version ausprobiere. Wer die Funktionen also tatsächlich in einem Programm verwenden will, der sollte den Weg gehen, den Nico vorgemacht hat, bzw. der sollte beide Varianten laden und OS-abhängig nutzen. Betrachtet die Demo von mir bitte daher als das was sie ist: als Programm, das unter bestimmten Bedingungen funktioniert aber keinesfalls als produktives Beispiel dienen sollte. ;) Nico wird´s mir sicher nicht übelnehmen, wenn ich aus seiner PM zitiere: Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz