Selbst schliessende MessageBox, MessageBoxTimeOut

Eine MessageBox wird immer modal aufgerufen, das heißt im Programm wird an der Stelle stehen geblieben, an der die MessageBox aufgerufen wurde. Erst nach dem schließen, wird dann an der Stelle fortgefahren. Das heißt mit anderen Worten, dass so lange die MessageBox angezeigt wird, das Programm quasi blockiert ist. Jetzt kann es aber vorkommen, dass das Programm auch weiterarbeiten muss, wenn eine solche MessageBox angezeigt wird. Sitzt nun niemand vor dem Rechner, um die MessageBox zu schließen, ist das Programm bis in alle Ewigkeit - nun ja fast bis in alle Ewigkeit oder das was für Windows eine Ewigkeit ist - blockiert. In so einem Fall wäre es eventuell wünschenswert, wenn sich die MessageBox von alleine nach einer gewissen Zeitspanne wieder schließen würde. Die normale MessageBox bietet diese Möglichkeit nicht. Jedoch gibt es eine undokumentierte API-Funktion, die genau dies kann, sich selber nach einer gewissen Zeitspanne zu schließen.

Diese API-Funktion ist in der user32.dll (Version 5.1.2600.2622 (xpsp_sp2_gdr.050301-1519) Ob diese Funktion auch in dieser DLL von anderen Windows Versionen zu finden ist, kann ich im Moment noch nicht sagen.) implementiert und heißt MessageBoxTimeout(A/W). Diese Funktion ist als ANSI Version (A) als auch als Unicode Version (W) implementiert. Sie hat noch zwei zusätzliche Parameter im Gegensatz zur herkömmlichen API-Funktion MessageBox. Nämlich einmal einen Parameter für die LanguagID und einen Parameter, der festlegt nach was für einer Zeitspanne sich die MessageBox wieder schließen soll. Entsprechende Deklarationen in Delphi würden dann so aussehen:

function MessageBoxTimeOut(hWnd: HWND; lpText: PChar; lpCaption: PChar; uType: UINT;
  wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;
  external user32 name 'MessageBoxTimeoutA'
function MessageBoxTimeOutA(hWnd: HWND; lpText: PChar; lpCaption: PChar; uType: UINT;
  wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;
  external user32 name 'MessageBoxTimeoutA'
function MessageBoxTimeOutW(hWnd: HWND; lpText: PWideChar; lpCaption: PWideChar; uType: UINT;
  wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;
  external user32 name 'MessageBoxTimeoutW'

Der Parameter für die LanguageID wird bisher nicht verwendet und sollte null sein. Der letzte Parameter gibt letztendlich die Zeit in Millisekunden an, nach der sich die MessageBox selber schließen soll. Hat sie sich selber geschlossen, ist ihr Rückgabewert MB_TIMEDOUT (0x7D00).

Beispielaufruf:

var
  ret               : DWORD;
  s                 : string;
begin
  ret := MessageBoxTimeOut(Handle, 'Diese MessageBox schließt sich in 3 Sekunden',
    'MessageBoxTimeOut', MB_OKCANCEL or MB_ICONINFORMATION, 0, 3000);
  case ret of
    IDOK: s := 'OK';
    IDCANCEL: s := 'Cancel';
    MB_TIMEDOUT: s := 'TimedOut';
  end;
  ShowMessage(s);

Wie immer bei undokumentierten API-Funktionen sollte man Vorsicht bei ihrer Nutzung walten lassen und überprüfen, ob diese undokumentierte Funktion auch auf allen Windows Versionen unterstützt wird, auf denen das Programm zum Einsatz kommen soll. In diesem Fall dürfte aber davon ausgegangen werden, dass sie auch in Zukunft noch zur Verfügung stehen wird. Grund ist der: Nach dem ich mir die user32.dll mit einem Disassembler angeguckt habe, habe ich dort folgenden Code gefunden:

In der neuen Version meiner Unit MpuWinNT.pas findet sich die Deklaration der API-Funktion MessageBoxTimeout.

Exported fn(): MessageBoxW - Ord:01E4h
:77D660F2 8BFF                    mov edi, edi
:77D660F4 55                      push ebp
:77D660F5 8BEC                    mov ebp, esp
:77D660F7 833DBC04D77700          cmp dword ptr [77D704BC], 00000000
:77D660FE 7424                    je 77D66124
:77D66100 64A118000000            mov eax, dword ptr fs:[00000018]
:77D66106 6A00                    push 00000000
:77D66108 FF7024                  push [eax+24]
:77D6610B 68240BD777              push 77D70B24

* Reference To: KERNEL32.InterlockedCompareExchange, Ord:0218h
                                  |
:77D66110 FF15C812D177            Call dword ptr [77D112C8]
:77D66116 85C0                    test eax, eax
:77D66118 750A                    jne 77D66124
:77D6611A C705200BD77701000000    mov dword ptr [77D70B20], 00000001

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:77D660FE(C), :77D66118(C)
|
:77D66124 6A00                    push 00000000
:77D66126 FF7514                  push [ebp+14]
:77D66129 FF7510                  push [ebp+10]
:77D6612C FF750C                  push [ebp+0C]
:77D6612F FF7508                  push [ebp+08]
* Reference To: USER32.MessageBoxExW
                                  |
:77D66132 E801A4FEFF              call 77D50538
:77D66137 5D                      pop ebp
:77D66138 C21000                  ret 0010
Exported fn(): MessageBoxExW - Ord:01DFh
:77D50538 8BFF                    mov edi, edi
:77D5053A 55                      push ebp
:77D5053B 8BEC                    mov ebp, esp
:77D5053D 6AFF                    push FFFFFFFF
:77D5053F FF7518                  push [ebp+18]
:77D50542 FF7514                  push [ebp+14]
:77D50545 FF7510                  push [ebp+10]
:77D50548 FF750C                  push [ebp+0C]
:77D5054B FF7508                  push [ebp+08]
* Reference To: USER32.MessageBoxTimeoutW
                                  |
:77D5054E E8EE590100              call 77D65F41
:77D50553 5D                      pop ebp
:77D50554 C21400                  ret 0014

Wie man sieht, ruft die dokumentierte API-Funktion MessageBox die dokumentierte API-Funktion MessageBoxEx auf, welche wiederum, zu guter letzt, die undokumentierte API-Funktion MessageBoxTimeout aufruft mit 0xFFFFFFFF als Wert für den Timeout. Dieser Wert ist die Obergrenze des Wertebereichs vom Typ DWORD und entspricht ungefähr 49 Tagen. Sprich, eine normale MessageBox würde sich nach 49 Tagen auch von alleine schließen. Jedenfalls die Tatsache, dass beide dokumentierte API-Funktionen, MessageBox und MessageBoxEx, letztendlich die undokumentierte API-Funktion MessageBoxTimeout aufrufen, läßt mit ziemlicher Sicherheit vermuten, dass auch diese undokumentierte Funktion in Zukunft noch existieren wird. Allerdings ist es durchaus möglich, dass sich die Parameter in einer zukünftigen Windows Version oder mit einem Servicepack ändern könnten!

2010-12-29T23:44:50 +0100, mail+homepage[at]michael-puff.de