Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi MessageBox ersetzen (https://www.delphipraxis.net/72621-messagebox-ersetzen.html)

Codewalker 4. Jul 2006 06:44


MessageBox ersetzen
 
Hallo zusammen.

Ich programmiere mir ein kleines Tool, welches einige Daten auf meinem Server sammelt (Speicherauslastung,An- und Abmeldeereignisse). Das Problem ist, dass bei einem Fehler eine MessageBox aufgeht und das Programm lahmlegt, weil niemand an dem Rechner ist und auf OK drücken könnte. Es gibt ja eine undokumentierte Funktion MessageBoxTimeOut, welche sich nach x Sekunden selbst schließt. Das geht auch ganz gut, nur rufen die Delphi-Bibliotheken selbst ja MessageBox und nicht MEssageBoxTimeOut auf. Kann man irgendwie alle Aufrufe von MessageBox nach MessageBoxTimeOut "umbiegen" :gruebel:

r2c2 4. Jul 2006 07:12

Re: MessageBox ersetzen
 
Bin mir nicht sicher, aber irgendwie könnte das gehen. ggf. mit Hooks. Da hab ich aber (noch) keine Ahnung von.

Ne andere (einfachere) Möglichkeit is es das MessageBox-Fenster einfach nach x-Sekunden abzuschießen. Such mal nach FindWindow(Ex)...

mfg

Christian

Ralf Kaiser 4. Jul 2006 07:33

Re: MessageBox ersetzen
 
Zitat:

Zitat von Codewalker
Hallo zusammen.

Ich programmiere mir ein kleines Tool, welches einige Daten auf meinem Server sammelt (Speicherauslastung,An- und Abmeldeereignisse). Das Problem ist, dass bei einem Fehler eine MessageBox aufgeht und das Programm lahmlegt, weil niemand an dem Rechner ist und auf OK drücken könnte. Es gibt ja eine undokumentierte Funktion MessageBoxTimeOut, welche sich nach x Sekunden selbst schließt. Das geht auch ganz gut, nur rufen die Delphi-Bibliotheken selbst ja MessageBox und nicht MEssageBoxTimeOut auf. Kann man irgendwie alle Aufrufe von MessageBox nach MessageBoxTimeOut "umbiegen" :gruebel:

Halli Hallo,

vermeide einfach, daß Fehler auftauchen die eine Messagebox anzeigen könnten. Fange an kritischen Stellen Fehler mit try..execpt ab und reagiere entsprechend darauf (in ein Logbuch eintragen, Mail an den Admin schicken...).

Das ganze wird auf jeden Fall eine ganze Menge mehr Testaufwand bedeuten. Zu empfehlen wären da automatisierte Unittests mit DUnit oder anderen Testtools um die Serverapplikation auf mögliche Fehlerquellen abzuklopfen. Es ist auf jeden Fall nicht besonders elegant die Symptome (die Messageboxen) zu beseitigen die Krnkheit (die auftretenden Fehler) jedcoh im Programm zu lassen :wink:

Ciao,
Ralf

Luckie 4. Jul 2006 07:35

Re: MessageBox ersetzen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Diese undokumentierte Funktion ist mir unbeaknnt,. Ich habe aber mal eine Klasse geschriben TDemoProtect, die dies kann. Unit ist im Anhang. Wäre es aber nicht sinnvoller, statt Messageboxen anzuzeigen, eine Logdatei zu schreiben?

Codewalker 4. Jul 2006 07:52

Re: MessageBox ersetzen
 
Erst mal danke für die Antworten.

Zitat:

Zitat von Luckie
Diese undokumentierte Funktion ist mir unbeaknnt,. Ich habe aber mal eine Klasse geschriben TDemoProtect, die dies kann. Unit ist im Anhang. Wäre es aber nicht sinnvoller, statt Messageboxen anzuzeigen, eine Logdatei zu schreiben?

Die Funktion ist hier http://bdn.borland.com/article/32736 erklärt. Ich will ja auch keine MessageBoxen anzeigen, nur werden manche halt "von alleine" (jaja, stark vereinfacht gesehen) aufgerufen, sei es von Windows oder von anderen Delphi Klassen und Units. Und ich kann halt nicht die halbe VCL umschreiben... :zwinker:

Zitat:

Zitat von Alfi001
Es ist auf jeden Fall nicht besonders elegant die Symptome (die Messageboxen) zu beseitigen die Krnkheit (die auftretenden Fehler) jedoch im Programm zu lassen :wink:

Stimmt einerseits, aber das Programm muss halt unter allen Umständen weiterlaufen. Ich werde Fehler in einem Logbuch festhalten und vorher natürlich testen ohne Ende. Trotzdem kann man nicht alle Meldungen abfangen (z.B. Speichermangel - könnte ja fast immer passieren)


Zitat:

Zitat von r2c2
Bin mir nicht sicher, aber irgendwie könnte das gehen. ggf. mit Hooks. Da hab ich aber (noch) keine Ahnung von.

Ich leider auch nicht - das geht damit aber bestimmt. Hat das schon mal jemand gemacht??

SirThornberry 4. Jul 2006 11:58

Re: MessageBox ersetzen
 
nach "etwas" rumprobieren hab ichs hinbekommen die MessageBox zu ersetzen.
Dazu hab ich folgende Funktion geschrieben (die Funktion "ersetzt" die ursprungsfunktion):
Delphi-Quellcode:
function ReplaceFunktion(AOldFunction, ANewFunction: Pointer): Boolean;
var lProcess,
    lWritten : Cardinal;
    lBuffer : Array[0..(1 + 4 + 1)-1] of Byte;
begin
  lProcess := OpenProcess(PROCESS_VM_WRITE or PROCESS_VM_OPERATION, True, GetCurrentProcessId);
  if (lProcess <> 0) then
  begin
    PByte(@lBuffer)^       := $68;
    PPointer(@lBuffer[1])^ := ANewFunction;
    PByte(@lBuffer[5])^    := $C3;
    if WriteProcessMemory(lProcess, AOldFunction, @lBuffer, SizeOf(lBuffer), lWritten) then
      result := lWritten = SizeOf(lBuffer)
    else
      result := False;
  end
  else
    result := False;
end;
Die Alte Funktion und die neue Funktion müssen zu 100 Identisch sein.
Für dein Beispiel würde der Aufruf wie folgt aussehen (MessageBoxB ist meine neue Funktion):
Delphi-Quellcode:
//Messagebox-Ausgaben in Applicationtitle ausgaben
function MessageBoxB(AHandle: HWND; AMsg, ATitle: PChar; uType: Cardinal): Integer; stdcall;
begin
  Application.Title := String(AMsg);
end;

[...]
begin
  ReplaceFunktion(@MessageBox, @MessageBoxB);
  //Und siehe da, der Fehler wird nicht mehr als herkömliche Messagebox angezeigt
  StrToInt('abc');
end;

RavenIV 4. Jul 2006 12:27

Re: MessageBox ersetzen
 
Zitat:

Zitat von Luckie
Wäre es aber nicht sinnvoller, statt Messageboxen anzuzeigen, eine Logdatei zu schreiben?

Das würde ich auch sagen.
Meiner Meinung nach kann man JEDEN Fehler und JEDEN Hinweis der von irgendwem (Delphi, Windoes, Komponenten) angezeigt werden soll, abfangen und einen Eintrag in ein Logfile schreiben.

Ich finde es uncool, die roten Tupfer (MessageBoxen) zu überschminken, wenn ich Masern hab (ein Fehler vorkommt), nur um nicht zu zeigen, dass ich krank bin (nicht alle Fehler gesucht und gefunden habe).

SirThornberry 4. Jul 2006 12:39

Re: MessageBox ersetzen
 
in Bezug auf die Fehler wäre die Verwendung von Application.OnException sinnvoller.
Das ersetzen der MessageBox ist dann sinnvoll wenn diese nicht ins Design passt.

Codewalker 4. Jul 2006 15:45

Re: MessageBox ersetzen
 
Das sieht ja super aus. Ich werd das heute abend direkt mal ausprobieren. Schon mal danke, ich werd Rückmeldung geben, wie es gelaufen ist :dp:

TheAn00bis 4. Jul 2006 15:51

Re: MessageBox ersetzen
 
Zitat:

Zitat von SirThornberry
Dazu hab ich folgende Funktion geschrieben (die Funktion "ersetzt" die ursprungsfunktion):

[...]

Die Alte Funktion und die neue Funktion müssen zu 100 Identisch sein.

Sag mal, kann deine ReplaceFunction alle Funktionen ersetzen?
Und was heißt dann zu 100% identisch? Du meinst in den Aufrufparametern und im Namen, oder?

himitsu 4. Jul 2006 16:16

Re: MessageBox ersetzen
 
100%: die Parameter müssen übereinstimmen (denk ich mal)

und so wie das aussieht überschreibt man damit die ersten Bytes der Funktion ... also mit einem Sprung in die neue Funktion ... demnach könnte man damit alles ersetzten, zerstört damit aber die Originalfunktion, diese ist dann also nicht mehr nutzbar?
mir fällt grad ein, daß an Posiion @MessageBoxA doch auch nur ein Jump steht :gruebel:
@Bärchen: wirde es dann denn nicht ausreichen, wenn du den Jump-Befehl stehen läßt und nur die Adresse änderst?

Codewalker 4. Jul 2006 19:18

Re: MessageBox ersetzen
 
Ich habs mal ausprobiert und irgendwie bekomme ich immer eine Schutzverletzung. Hast du vielleicht ein Beispielprojekt?

Luckie 4. Jul 2006 19:33

Re: MessageBox ersetzen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Warum dieser Umstand und dieses rumgehacke im Quellcode? Du hast doch selber eine Möglichkeit gefunden (MessageboxTimeOut) und ich habe dir noch eine weitere gezeigt? Warum nimmst du nicht eine davon, wenn du schon nicht alles in ein Log schreiben willst.

Im Anhang noch mal beide Versionen in einer Demo von mir.

SirThornberry 4. Jul 2006 21:06

Re: MessageBox ersetzen
 
Zitat:

Zitat von himitsu
100%: die Parameter müssen übereinstimmen (denk ich mal)

und so wie das aussieht überschreibt man damit die ersten Bytes der Funktion ... also mit einem Sprung in die neue Funktion ... demnach könnte man damit alles ersetzten, zerstört damit aber die Originalfunktion, diese ist dann also nicht mehr nutzbar?
mir fällt grad ein, daß an Posiion @MessageBoxA doch auch nur ein Jump steht :gruebel:
@Bärchen: wirde es dann denn nicht ausreichen, wenn du den Jump-Befehl stehen läßt und nur die Adresse änderst?

@Himitsu: Bei der statichen Variante steht da wirklich nur ein Jump, wenn du die Funktion mit GetProcAddress ermittelst bekommst du die Adresse wo der staatiche Jump hinzeigt. Idealerweise sollte also die statiche Adresse von MessageBox, von MessageBoxA (ist eine andere als die von MessageBox) oder nur die Adresse welche bei GetProcAddress zurück geliefert wird (denn GetProcAddress liefert die richtige Adresse der Funktion und nicht nur die Adresse wo dann erst zur richten Funktion gesprungen wird.

Also Idealer Weise so:
Delphi-Quellcode:
var lLib: Cardinal;
begin
  lLib := LoadLibrary('user32.dll');
  if (lLib <> 0) then
  begin
    ReplaceFunktion(GetProcAddress(lLib, 'MessageBoxA'), @MessageBoxB);
    FreeLibrary(lLIb);
  end;
end;
@Codewalker: Wenn du eine AV bekommst ist dein Funktionrumpf nicht mit dem originalen Identisch. Die Parameter müssen gleich sein und auch die Aufrufkonvention. Wenn du zum Beispiel "stdcall" weglässt geht es schon schief.
Im falle von MessageBoxA bzw. MessageBox muss dein Funktionskopf exakt so aussehen (ausgenommen der Funktionsname):
Delphi-Quellcode:
function Funktionsname(AHandle: HWND; AMsg, ATitle: PChar; uType: Cardinal): Integer; stdcall;
Es darf also keine Methode etc. sein sondern muss eben exakt mit der declaration der Originalfunktion übereinstimmen.

Das die Originale Funktion dabei zerstört wird ist richtig. Aus dem Grund hab ich vor das ganze in eine Klasse zu packen so das man eine Funktion ersetzen kann und später auch wieder herstellen (so das beim ersetzen der Teil welcher überschrieben wird, gesichert wird).


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:44 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-2025 by Thomas Breitkreuz