Der "InitCommonControlsEx"-Bug


Was ist der "InitCommonControlsEx"-Bug?

Wenn Sie beim Test der beiliegenden Programme bemerken, dass einige der "Common Controls" nicht angezeigt werden, dann liegt dies nicht am Programm. Als Beispiel möchte ich das Eingabefeld für IP-Adressen anführen.

Dazu muss man wissen, dass der Befehl "InitCommonControls" im Platform SDK von Microsoft als veraltet (obsolete) bezeichnet wird. Die meisten Beispiele dieses Tutorials nutzen ihn dennoch. In einigen Fällen, etwa beim schon erwähnten IP-Control, reicht dieser Befehl aber nicht mehr aus. Hier muss zwingend der Befehl "InitCommonControlsEx" verwendet werden. Auf Grund des Bugs werden die Controls jedoch nicht korrekt initialisiert, und das IP-Eingabefeld ist nicht zu sehen.

Welche Versionen sind betroffen?

Delphi-VersionStatus
Delphi 2-
Delphi 3-
Delphi 4-
Delphi 5der Fehler tritt ohne und mit SP1 auf
Delphi 6der Fehler tritt ohne und mit Updates/Service-Packs auf
Delphi 7der Fehler tritt ohne und mit SP1 auf
Delphi 2005 (Delphi 9)in der Preview tritt der Fehler auf
Delphi 2006-
TurboDelphi für Win32der Fehler tritt auf (lt. Quellcode der "CommCtrl.pas")

(keine Angabe bedeutet: mir lagen zu diesem Zeitpunkt keine Informationen vor, ob der Fehler auftritt oder nicht)

Der Work-Around

Wenn Sie eine Standard- oder Personal-Version von Delphi besitzen, haben Sie keinen Zugriff auf die Quellcodes der Unit. Mein Rat lautet in dem Fall: benutzen Sie eine möglichst aktuelle Standardversion (in der Hoffnung, dass der Fehler dort behoben wurde).
Ist das nicht der Fall, dann rufen Sie in Ihrem Programm zusätzlich den Befehl

InitCommonControls;

auf. Es spielt zwar keine Rolle, an welcher Stelle Sie das tun, aber wg. der besseren Übersichtlichkeit empfehle ich, dass Sie den Befehl vor "InitCommonControlsEx" in den Quellcode eintragen. Sollte der Fehler einmal behoben worden sein, müssen Sie nicht lange suchen und können die dann überflüssige Anweisung schnell entfernen.


Der Patch

Hierfür benötigen Sie Borlands Quellcode der "CommCtrl"-Unit. Wenn wir uns die Deklaration am Beispiel von Delphi 5 einmal anschauen, dann finden wir in der Unit eine eigene Funktion mit dem Namen "InitCommonControlsEx". Dies ist kein Fehler, sondern hat damit zu tun, dass die Funktion unter Windows 95 und NT4 den Internet Explorer 3 als Minimum erfordert. Arbeitet noch jemand mit einer Originalversion dieser Systeme (ohne installiertem IE), existiert die Funktion bei ihm noch gar nicht. Der Versuch, sie dennoch (statisch) zu laden, würde eine unschöne Fehlermeldung verursachen.

Daher wird in der Unit von Borland nun versucht, die Funktion dynamisch zu laden. Zu dem Zweck wird eine zweite, interne Funktion aufgerufen, die (im Fall von D5 und beispielsweise TurboDelphi) wie folgt aussieht:

procedure InitComCtl;
begin
  if ComCtl32DLL = 0 then
  begin
    ComCtl32DLL := GetModuleHandle(cctrl);
    if ComCtl32DLL <> 0 then
      @_InitCommonControlsEx := GetProcAddress(ComCtl32DLL, 'InitCommonControlsEx');
  end;
end;

Und hier liegt meines Erachtens nach der Fehler. In der Zeile

    ComCtl32DLL := GetModuleHandle(cctrl);

wird das Handle der geladenen DLL "comctl32.dll" gesucht und an die Variable übergeben. Wenn die DLL aber nicht geladen ist, erhält die Variable den Wert Null und führt in dem Fall die nachfolgenden Anweisungen auch nicht mehr aus. Fügen Sie daher nach der eben gezeigten Zeile folgendes ein:

    if ComCtl32DLL = 0 then ComCtl32DLL := LoadLibrary(cctrl);

Sollte also "GetModuleHandle" tatsächlich den Wert Null als Ergebnis liefern, wird die Bedingung aktiv und die DLL geladen.