Für ein selbständiges Bayern
Vorgestern habe ich im ZDF (oder war es doch in der ARD?) einen Werbespot zur Bundestagswahl von der Bayernpartei gesehen. Nun habe ich mir mal die Website angeschaut, und ich bin schwer begeistert. Schade, dass diese Partei nur in Bayern antritt. Ich bin mir sicher, dass die Bayernpartei gerade nach den Äußerungen von König Edmund dem 1. in den letzten Wochen auch außerhalb Bayerns mit vielen Stimmen rechnen könnte.
Aber das mit dem Rechnen müsst ihr noch üben… 😉
Schöner testen
In den letzten beiden Tagen habe ich den Dialog, der beim Programmstart auf die 60-Tage-Testversion hinweist, ein wenig runderneuert. Alexandra hatte wieder einmal eine tolle Idee, wie man diesen Dialog etwas frischer und moderner gestalten kann. Zusätzlich zu der Fortschrittsanzeige mit den ablaufenden Tagen zeigt nun auch ein passender Farbverlauf, dass die Testzeit langsam zu Ende geht.
Nach dem ersten Drittel erscheint das Fenster nun in einem Gelbton.
Das letzte Drittel der Testzeit wird dann rötlich dargestellt.
GetMenuItemInfo und x64
Das Platform SDK enthält alle (dokumentierten) Definitionen der Windows-API, die für die Entwicklung von Windows-Anwendungen nötig sind. Im Laufe der Zeit und der vielen Windows-Versionen kommen immer wieder ein paar neue Definitionen hinzu, häufig werden ältere Definitionen auch erweitert. Damit die Windows-Version, auf dem die Anwendung dann läuft, auch weiß, wie groß die Strukturdaten sind, die bei einem Aufruf einer API-Funktion übergeben werden, enthält eine Strukturdefinition in den meisten Fällen als ersten Wert die Größe der Struktur. So kann das Betriebssystem entscheiden, welche Daten in der Struktur gültig sind und von welchen Daten es zwecks Vermeidung eines Zugriffsfehlers die Finger lassen sollte.
In der Regel läuft das alles auch problemlos, eine recht heikle Funktion ist aber GetMenuItemInfo im Zusammenhang mit der MENUITEMINFO-Struktur:
typedef struct tagMENUITEMINFO { UINT cbSize; UINT fMask; UINT fType; // used if MIIM_TYPE (4.0) or MIIM_FTYPE (>4.0) UINT fState; // used if MIIM_STATE UINT wID; // used if MIIM_ID HMENU hSubMenu; // used if MIIM_SUBMENU HBITMAP hbmpChecked; // used if MIIM_CHECKMARKS HBITMAP hbmpUnchecked; // used if MIIM_CHECKMARKS ULONG_PTR dwItemData; // used if MIIM_DATA LPSTR dwTypeData; // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0) UINT cch; // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0) #if(WINVER >= 0x0500) HBITMAP hbmpItem; // used if MIIM_BITMAP #endif /* WINVER >= 0x0500 */ } MENUITEMINFO, FAR *LPMENUITEMINFO;
Diese Struktur wurde für Windows 2000 und Windows ME erweitert, wie man an
#if(WINVER >= 0x0500) HBITMAP hbmpItem; // used if MIIM_BITMAP #endif /* WINVER >= 0x0500 */
sieht. Durch das Makro WINVER lässt sich steuern, ab welcher Windows-Version die Anwendung laufen soll. Dieses und einige weitere Makros sind über die ganzen API-Definitionen verstreut. Definiert man WINVER z.B. mit 0x0400, damit die Anwendung auch unter Windows 95 und NT 4 lauffähig ist, werden sämtliche zusätzliche Definitionen ausgeblendet. Dies hat aber den Nachteil, dass man neue Funktionen und Flags überhaupt nicht mehr benutzen kann.
Daher setze ich WINVER immer auf 0x0501 und importiere Funktionen, die erst ab einer bestimmten Windows-Version verfügbar sind, immer dynamisch. Somit laufen meine Anwendungen alle ab Windows 95/NT4, sofern zumindestens der Internet Explorer 5.5 installiert ist. Dieser enthält einige wichtige Systemerweiterungen, die von Entwicklerseite nicht einzeln redistributierbar sind.
Bei der Funktion GetMenuItemInfo gibt es aber einen kleinen Stolperstein. Übergibt man hier als Strukturgröße in cbSize die neue Größe, dann verweigern Windows 95 und NT4 mit der Fehlermeldung 87 (Ungültiger Parameter) die Arbeit. Beide Systeme verlangen explizit die alte und ihnen bekannte Strukturgröße ohne das zusätzliche hbmpItem.
Als Workaround hatte ich mir daher vor langer Zeit einmal ein paar Definitionen zusammengestellt, mit deren Hilfe dieses Problem umgangen wird:
#define MENUITEMINFO_SIZE_VERSION_400A CDSIZEOF_STRUCT(MENUITEMINFOA, cch) #define MENUITEMINFO_SIZE_VERSION_400W CDSIZEOF_STRUCT(MENUITEMINFOW, cch) #define MENUITEMINFO_SIZE_VERSION_500A sizeof(MENUITEMINFOA) #define MENUITEMINFO_SIZE_VERSION_500W sizeof(MENUITEMINFOW) #ifdef UNICODE #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400W #define MENUITEMINFO_SIZE_VERSION_500 MENUITEMINFO_SIZE_VERSION_500W #else #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400A #define MENUITEMINFO_SIZE_VERSION_500 MENUITEMINFO_SIZE_VERSION_500A #endif #define MENUITEMINFO_SIZE MENUITEMINFO_SIZE_VERSION_400
Da ich die zusätzliche Strukturvariable hbmpItem fast nie benötige, verwende ich für die Größe der Struktur in cbSize immer MENUITEMINFO_SIZE, somit funktioniert die Funktion problemlos auf allen Systemen ab Windows 95.
Beim Testen der x64-Version vom SpeedCommander musste ich aber leider feststellen, dass nun Windows XP x64 an dieser Stelle herumzickt. Alle Aufrufe von GetMenuItemInfo wurden wieder mit Fehler 87 zurückgegeben. Ändert man die Strukturgröße jedoch auf MENUITEMINFO_SIZE_VERSION_500, dann funktioniert es unter Windows XP x64 wieder wie erwartet. Eine kleine Erweiterung der obigen Definition schafft Abhilfe:
#define MENUITEMINFO_SIZE_VERSION_400A CDSIZEOF_STRUCT(MENUITEMINFOA, cch) #define MENUITEMINFO_SIZE_VERSION_400W CDSIZEOF_STRUCT(MENUITEMINFOW, cch) #define MENUITEMINFO_SIZE_VERSION_500A sizeof(MENUITEMINFOA) #define MENUITEMINFO_SIZE_VERSION_500W sizeof(MENUITEMINFOW) #ifdef UNICODE #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400W #define MENUITEMINFO_SIZE_VERSION_500 MENUITEMINFO_SIZE_VERSION_500W #else #define MENUITEMINFO_SIZE_VERSION_400 MENUITEMINFO_SIZE_VERSION_400A #define MENUITEMINFO_SIZE_VERSION_500 MENUITEMINFO_SIZE_VERSION_500A #endif #ifdef _M_IX86 #define MENUITEMINFO_SIZE MENUITEMINFO_SIZE_VERSION_400 #else #define MENUITEMINFO_SIZE MENUITEMINFO_SIZE_VERSION_500 #endif
Beim Kompilieren für 32bit-Systeme wird wie bisher immer die Größe der alten Struktur verwendet, beim Kompilieren für 64bit-Systeme dagegen die Größe der neuen Struktur.
hackbsd Crew 0wnz y0u! @ jank0
So stand es heute Mittag hier auf dieser Seite. Ursache für den Hack ist anscheinend eine Sicherheitslücke in WordPress 1.2. Es gibt zwar bereits seit April eine Version 1.5, zwei Dinge haben mich aber bisher vom Update zurückgehalten. Zum einen war ich mit der 1.2 vollkommen zufrieden und zum anderen wurde mit der 1.5 der Unterbau ziemlich geändert, so dass einige Arbeiten am mühsam gebastelten Layout zu erwarten gewesen wären.
Wie es ausschaut, war mein Blog nicht das erste Opfer. Nun blieb mir leider nichts anderes übrig, als WordPress 1.5 zu installieren. Das Update selbst geschah per Script völlig problemlos, aber dann ging der K(r)ampf wie erwartet los. Sämtliche permanente Links funktionierten nicht mehr, weil WordPress 1.5 das ‚index.php‘ in den Links entfernt. Zwar kann man in der ‚.htaccess‘ wohl ein paar Rewrite-Konditionen setzen, aber ich habe es nicht geschafft, mich mit Erfolg durch die Hilfe zu kämpfen. Im WordPress-Forum fand ich dann eine einfache Lösung, mit der es wieder wie vorher funktioniert. In der Datei ‚wp-includes/template-functions-links.php‘ müssen alle sechs Vorkommen von ‚/?‘ durch ‚/index.php?‘ ersetzt werden.
Knappe zwei Stunden habe ich dann noch gebraucht, bis sich die Seite wieder im gewohnten Layout präsentierte. Sollte ich noch etwas vergessen haben, dann würde ich mich über eine Nachricht freuen. Ansonsten macht WordPress 1.5 bisher einen sehr guten Eindruck. Es bietet nun auch einen eingebauten Spamschutz für Kommentare, dieser beschränkt sich aber nur darauf, verdächtige Kommentare zur Moderation vorzumerken. Unbedingt empfehlenswert ist daher die Installation von Spam Karma 2, dessen Vorversion mir schon große Dienste erwiesen hat. Seit Anfang des Jahres wurden hier knapp 1300 Kommentare abgelegt, von denen lediglich 166 durch reale Leser geschrieben wurden. Der Rest stammt von Spam-Bots, die für Onlinepoker und sonstige Sachen werben.
Heureka
Ein großes Produktivitätshemmnis in Visual Studio 2005 ist für mich das Suchen- und Ersetzen-Fenster. Im Gegensatz zu früheren Versionen wird dieses Fenster nun als normales Dokumentenfenster innerhalb der geöffneten Quelltextdateien angezeigt. Das Suchen und Ersetzen wird somit zur Qual, da man während der Eingabe des Suchstrings den Quelltext aus den Augen verliert.
Trotz des intensiven Durcharbeitens aller Einstellungsdialoge konnte ich keine Möglichkeit finden, wie man dieses Fenster deaktivieren und wieder den normalen Dialog verwenden kann. Beim Debuggen auf dem x64-Rechner blinzelte mich plötzlich der gewohnte Dialog an, ich fand zuerst keine Erklärung, warum auf dem einen Rechner das Suchfenster und auf dem anderen der Dialog angezeigt wurde. Zufällig klickte ich dann mit der rechten Maustaste auf die Titelzeile des Dialoges und fand die verzweifelt gesuchte Einstellung:
Falsch multipliziert
Beim Debuggen von SpeedCommander unter x64 bin ich auf einen unschönen Bug in der ATL vom Visual Studio 2005 gestoßen. Während des Einlesens der Symbolleisten-Einstellungen wurde eine Exception geworfen und SpeedCommander stürzte ab. Nach dem Durchkämpfen bis zum Auslöser der Exception war der Fehler auch recht schnell sichtbar.
Die Exception wurde nach der Funktion AtlMultiplyThrow ausgelöst. AtlMultiplyThrow wird beim Reservieren eines Speicherbereichs aufgerufen und dient dazu, die Größe des zu reservierenden Speicherbereichs aus einer Anzahl von Elementen und deren Blockgröße zu ermitteln. Der Parameter tLeft gibt in diesem Fall die Anzahl der zu reservierenden Elemente und der Parameter tRight die Blockgröße an:
template <typename T> inline T AtlMultiplyThrow(T tLeft, T tRight) { T tResult; HRESULT hr=AtlMultiply(&tResult, tLeft, tRight); if(FAILED(hr)) { AtlThrow(hr); } return tResult; }
AtlMultiplyThrow ruft lediglich AtlMultiply auf, im Fehlerfall wird eine Exception ausgelöst:
/* generic but compariatively slow version */ template<typename T> inline HRESULT AtlMultiply(T* ptResult, T tLeft, T tRight) { /* avoid divide 0 */ if(tLeft==0) { return 0; } if(::ATL::AtlLimits<T>::_Max/tLeft < tRight) { return E_INVALIDARG; } *ptResult= tLeft * tRight; return S_OK; }
Der Fehler tritt nur auf, wenn der Wert tLeft Null ist. In diesem Fall gibt AtlMultiply 0 zurück, was auch der Wert für S_OK ist. AtlMultiplyThrow wird erfolgreich verlassen und gibt den Wert von tResult zurück. Wenn man sich die beiden Funktionen nun etwas genauer anschaut, dann entdeckt man, dass der Inhalt von tResult undefiniert ist. In der Debugversion werden alle Werte automatisch mit 0xCCCCCCCC initialisiert, somit gibt AtlMultiplyThrow hier 3435973836 zurück.
Die Allokation des Speichers erfolgt beim Einlesen der Einstellungen, genauer gesagt wenn ein String geladen wird. In der entsprechenden MFC-Funktion wird Speicher entsprechend der Zeichen im String reserviert. Für einen leeren String werden 0 Bytes reserviert, durch den Bug in der Berechnung soll Windows allerdings knapp 3.5 GB bereitstellen. Das kann natürlich nicht gut gehen.
Die Korrektur ist sehr einfach, in der Funktion AtlMultiply muss lediglich die Variable ptResult genullt werden, wenn tLeft den Wert 0 enthält:
/* generic but compariatively slow version */ template<typename T> inline HRESULT AtlMultiply(T* ptResult, T tLeft, T tRight) { /* avoid divide 0 */ if(tLeft==0) { *ptResult = 0; return S_OK; } if(::ATL::AtlLimits<T>::_Max/tLeft < tRight) { return E_INVALIDARG; } *ptResult= tLeft * tRight; return S_OK; }
Abschließend muss die MFC zwingend neu kompiliert werden, da der Fix ansonsten nur für eigene Anwendungen gilt und nicht für Funktionen innerhalb der MFC.
Standards fordern und selbst nicht einhalten
Paul Thurrot fordert in seinem Blog auf, den Internet Explorer 7 zu boykottieren, weil er unsicher sei und keine Standards einhält. Dies macht ihn für Anwender und Web-Entwickler unbenutzbar. Anlass dafür ist die Aussage von IE-Produktmanager Chris Wilson, dass auch die finale Version vom IE7 den Acid2-Test nicht bestehen wird. Im übrigen besteht nur eine speziell angepasste Version von Safari diesen Test, alle anderen Browser (Firefox, Opera) scheitern ebenso.
Vielleicht sollte sich Paul Thurrot erst einmal um seine Website kümmern, bevor er lauthals von anderen die Einhaltung von Standards fordert. Der w3C-Validator zeigt hier stolze 270 Fehler. Paul Thurrot sollte daher froh sein, dass seine Seite von den Browsern überhaupt angezeigt wird.
Grenzgänge
Unter Windows XP x64 erfolgt eine konsequente Trennung zwischen 32bit-Anwendungen und nativen 64bit-Anwendungen. Eine 64bit-Anwendung kann keine 32bit-Dll laden – damit haben momentan alle Entwickler zu kämpfen, die ihre 32bit-Module in das Kontextmenü des 64bit-Explorers einbinden möchten. Die einzige Lösung hierfür ist, die Shellerweiterung als 64bit-Version zu kompilieren.
Auf ein ähnliches Problem stößt man, wenn man eine native 64bit-Anwendung ausliefern möchte und einige Module nur als 32bit-Dll zur Verfügung stehen. Wenn die Module nicht überlebenswichtig sind, kann man sie vorerst auch ausklammern. Ein Beispiel hierfür ist Squeez, der in der 64bit-Ausgabe keine Anzeige von Bildern in der Schnellansicht erlaubt und auch keine ACE-Archive entpacken kann. Von den WinACE-Entwickler war bisher leider noch nicht zu erfahren, ob bzw. wann eine x64-Fassung der UnAceV2.dll zu erwarten ist. Mit der zunehmend immer geringeren Verbreitung von ACE-Archiven lässt sich die fehlende UnACE-Unterstützung aber sicher verschmerzen.
Ganz anders sieht es mit der Schnellansicht von Bildern aus, die im SpeedCommander noch eine sehr viel größere Bedeutung hat. Hierfür verwende ich seit einigen Jahren die PictView-Dll von Jan Patera. Diese Dll kann über 50 verschiedene Bildformate lesen und ist gerade einmal 191KB groß. Auf meine Anfrage zu einer x64-Version teilte mir Jan leider mit, dass er vorerst keine Umsetzung auf 64bit plane. Die Dll selbst benutzt viel Assemblercode, so dass eine Portierung auch nicht ganz einfach ist.
Beim Stöbern in der MSDN-Library entdeckte ich folgenden Satz:
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link library (DLL). Additionally, a 32-bit process cannot load a 64-bit DLL. However, 64-bit Windows supports remote procedure calls (RPC) between 64-bit and 32-bit processes (both on the same computer and across computers). On 64-bit Windows, an out-of-process 32-bit COM server can communicate with a 64-bit client, and an out-of-process 64-bit COM server can communicate with a 32-bit client. Therefore, if you have a 32-bit DLL that is not COM-aware, you can wrap it in an out-of-process COM server and use COM to marshal calls to and from a 64-bit process.
„Gesagt, tun getan“ pflegte Onkel Hotte immer zu sagen. Mit externen COM-Servern hatte ich bisher noch keinerlei Erfahrung, aber glücklicherweise bietet das Visual Studio 2005 mit der ATL dafür eine umfangreiche Unterstützung dafür. Ein COM-Server ist mit wenigen Handgriffen erstellt. Zusätzlich benötigt man noch eine Proxy-Dll, deren Projekt auch gleich generiert wird.
Das COM-Objekt definiert eine einfache Schnittstelle für den Zugriff auf die PictView-Dll. Es bekommt den Namen für die anzuzeigende Datei übergeben, lädt die Dll und bekommt dann von der Dll ein Bitmap (HBITMAP) zurück. Dieses wird in ein DIB (Device Independent Bitmap) konvertiert und an den Aufrufer zurückgegeben. Die Konvertierung in ein DIB ist notwendig, da das Marshaling zwischen dem externen COM-Server und dem Client bei einigen Grafiken nicht korrekt funktioniert, wenn ein HBITMAP übertragen wird. Vermutlich gibt es da noch einen kleinen Fehler im Windows-Marshaling-Code für HBITMAPs.
Damit ist nun auch die letzte große Hürde für die 64bit-Version von SpeedCommander genommen, die nun bis auf die fehlende UnACE-Unterstützung keinerlei Einschränkungen gegenüber der 32bit-Version mehr hat.
Visual Studio 2005 (CTP Juli)
Seit zwei Wochen gibt es nun die Juli-CTP (Community Technology Preview) vom Visual Studio 2005. Im Gegensatz zu den von Uwe gemachten Erfahrungen hatte ich keine Probleme, diese Version auf XP und XP x64 zu installieren.
Microsoft hat nun endlich (nach meinem Bugreport) die x64-DEF-Dateien für die MFC beigelegt, so dass man die MFC jetzt auch selbst kompilieren kann. Im Prinzip ging das vorher zwar auch schon, ich musste mir dazu aber die x64-DEF-Dateien selbst zusammenfrickeln, was mich damals einen ganzen Tag Arbeit gekostet hatte.
Beim Kompilieren der CRT (C-Runtime) gibt es mit der neuen Version leider Probleme, Microsoft hat wieder vergessen, neue hinzugefügte Dateien auch in die Installation zu packen. Als Workaround kann man aber die CRT von der Juni-CTP verwenden, diese läuft ohne größere Probleme durch.
Wenn man im Eigenschaftenfenster eines Projekts (oder einer Datei) die Buildkonfiguration wechselt, dann wird leider immer wieder die Seite „Common Properties | References“ angezeigt anstatt die zuvor aktive Seite. Bis zur Beta 2 hat alles noch korrekt funktioniert, laut Bugreport sollte dies in der nächsten Version gefixt sein.
Windows Longhorn Beta 1 verfügbar
Ab sofort können alle MSDN-Abonnenten die erste Beta von Longhorn Windows Vista herunterladen. Das DVD-Image hat eine stolze Größe von 2.42 GB, die Downloadgeschwindigkeit schwankt derzeit zwischen 30 und 60 KB/s.
Weiterhin im Angebot:
- Windows Longhorn Professional Edition Beta 1 – checked/debug (English)
- Windows Longhorn Professional x64 Edition Beta 1 (English)
- Windows Longhorn Professional x64 Edition Beta 1 – checked/debug (English)
- Windows Longhorn Professional Edition Beta 1 Windows Developers Kit (English)
Merkwürdig ist, dass die x64-Version nur 1.73 GB groß ist.