AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Problem mit ShellMessageBox [gelöst]
Thema durchsuchen
Ansicht
Themen-Optionen

Problem mit ShellMessageBox [gelöst]

Ein Thema von MathiasSimmack · begonnen am 22. Mär 2004 · letzter Beitrag vom 23. Mär 2004
Antwort Antwort
MathiasSimmack
(Gast)

n/a Beiträge
 
#1

Problem mit ShellMessageBox [gelöst]

  Alt 22. Mär 2004, 19:46
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:
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.
Das Interessante an der Box ist nun, das man gleich Stringressourcen angeben kann, ohne diese vorher laden zu müssen, etwa
Delphi-Quellcode:
ShellMessageBoxA(hInstance,self.Handle,
  MAKEINTRESOURCE(1000), // <-- Verweis auf Stringressource
  nil,MB_OK,[nil]);
(Klappt übrigens beim Titel auch!) Oder man kann mit der Escape-Sequenz %n (s. auch PSDK -> MSDN-Library durchsuchenFormatMessage @Daniel: ) einen Zeilenumbruch erzeugen:
Delphi-Quellcode:
ShellMessageBoxA(hInstance,self.Handle,
  'Hallo, Echo!%nHallo, Otto! :o)',
  nil,0,[nil]);
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:
Delphi-Quellcode:
szString := 'Hallo, Welt!';

ShellMessageBoxA(hInstance,self.Handle,
  '%1',nil,0,[pointer(szString)]);
Ich mache irgendwas falsch.
Aber was?

  Mit Zitat antworten Zitat
NicoDE
(Gast)

n/a Beiträge
 
#2

Re: Problem mit ShellMessageBox

  Alt 22. Mär 2004, 22:40
Zitat von MathiasSimmack:
Ich mache irgendwas falsch. Aber was?
Liegt nur daran, dass es nicht gerade einfach ist, mit Delphi Language variante Parameter (...) umzusetzen.

Delphi-Quellcode:
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.
Gruss Nico

[edit] BASM Optimierungen [/edit]
[edit] Anpassungen D2-D6 [/edit]
[edit] Unicode-Anpassung [/edit]
  Mit Zitat antworten Zitat
MathiasSimmack
(Gast)

n/a Beiträge
 
#3

Re: Problem mit ShellMessageBox

  Alt 23. Mär 2004, 07:49
Hallo Nico.
Dankeschön kann ich da nur sagen.

Zitat von NicoDE:
Delphi-Quellcode:
////////////////////////////////////////////////////////////////////////////////
//
// ShellMessageBox
//
// ( [url]http://search.microsoft.com/?View=msdn&qu=ShellMessageBox[/url] )
Ich wusste gar nicht, dass man mittlerweile überhaupt was dazu findet. Aber das ist bei einigen anderen Funktionen auch so. Offiziell unterstützt werden sie, laut PSDK, erst ab Windows 2000 (der "PickIconDlg" ist auch so ein Fall), aber tatsächlich vorhanden und nutzbar sind sie mit Ordinalwert auch schon vorher unter 98 meinetwegen. Na ja, Microsoft eben ...

Als kleine Frage, just for the sake of completeness, , was genau passiert im Assemberteil?
Zitat:
Delphi-Quellcode:
      asm
              push ebx
              mov ecx, [Arguments-04h] // High(Arguments)
              mov ebx, ecx
              shl ebx, 02h
              inc ecx
              jz @@call
              mov edx, [Arguments]
              add edx, ebx
      @@loop: push dword ptr [edx]
              sub edx, 04h
              loop @@loop
      @@call: push dword ptr [Style]
              push dword ptr [Title]
              push dword ptr [Msg]
              push dword ptr [Wnd]
              push dword ptr [Inst]
              call FNShellMessageBox
              add esp, 18h
              add esp, ebx
              mov [Result], eax
              pop ebx
      end;
Also im Prinzip weiß ich es: du ordnest Titel, Caption, Style usw. zu (bzw. legst sie auf dem Stack? ab) und rufst dann die Dialogbox auf. Aber warum so? Gab´s nicht eine Lösung in ... äh ... normaler Delphi-Sprache, die auch ich verstehe?

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.
  Mit Zitat antworten Zitat
NicoDE
(Gast)

n/a Beiträge
 
#4

Re: Problem mit ShellMessageBox

  Alt 23. Mär 2004, 08:25
Zitat von MathiasSimmack:
Dankeschön kann ich da nur sagen.
Keine Ursache

Zitat von MathiasSimmack:
was genau passiert im Assemberteil?
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 von MathiasSimmack:
Gab´s nicht eine Lösung in ... äh ... normaler Delphi-Sprache
Leider nicht, ...
Delphi-Quellcode:
asm
        push ...
end;
ShellMessageBox(...)
asm
        add esp,...
end;
...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).


Gruss Nico

ps: kurz vor Deiner Antwort hab ich oben noch ne Kleinigkeit geändert
[edit] elende Tippfehler, nicht mein Tag heute ... [/edit]
  Mit Zitat antworten Zitat
MathiasSimmack
(Gast)

n/a Beiträge
 
#5

Re: Problem mit ShellMessageBox

  Alt 23. Mär 2004, 12:20
Zitat von NicoDE:
ps: kurz vor Deiner Antwort hab ich oben noch ne Kleinigkeit geändert
´ne Kleinigkeit, wie?
Zitat:
Zuletzt bearbeitet von NicoDE am 23.03.2004, 08:47, insgesamt 22-mal bearbeitet.
Na ja.


@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: )), 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 ...

Ach ja, das Programm verursacht absichtlich zwei Compilerfehler, damit ihr lest was an der Stelle im Quellcode steht.


Nochmals Danke, @Nico.
Grüße.
Angehängte Dateien
Dateityp: zip undok.zip (60,9 KB, 35x aufgerufen)
  Mit Zitat antworten Zitat
NicoDE
(Gast)

n/a Beiträge
 
#6

Re: Problem mit ShellMessageBox

  Alt 23. Mär 2004, 12:28
Zitat von MathiasSimmack:
Na ja.
Alles wird gut
Edit 23 erweiterte es - dank Deines Hinweises - um Kommentare im BASM-Code...
(habe unbewußt versucht einen neuen Tippfehler/Edit-Record aufzugestellen *g*)
  Mit Zitat antworten Zitat
MathiasSimmack
(Gast)

n/a Beiträge
 
#7

Re: Problem mit ShellMessageBox [gelöst]

  Alt 23. Mär 2004, 15:33
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:
Es ist für den Standard-User eher besser, den UNICODE-Schalter zu vermeiden, da er sonst die ANSI-RTL mit seinem Unicode-Code mischt (und zudem fälschlicherweise der Meinung sein könnte, dass es noch auf Win9x läuft...).
Gruß.
Miniaturansicht angehängter Grafiken
dialogbox.png  
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:24 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz