Qt GPL-Version mit Visual Studio 6
Zum Entwickeln von Plattformunabhängigen GUIs gibt es mehrere Toolkits auf dem Markt, welche alle ihre Vor- und Nachteile haben.
In einem meiner vorherigen Blog-Einträge habe ich ja schon über die Vorzüge von GTK geschrieben.
Nichtsdestotrotz hat GTK auch einige Nachteile, u.A. zeichnet es alle seine Widgets selbst und man hat somit nich den Look&Feel der Zielplattform. Das war nur einer der Gründe, warum bei uns in der Firma beschlossen wurde, für diesen Zweck trotz entsprechender Lizenzkosten das QT-Toolkit der Firma Trolltech einzusetzen. Ein großer Nachteil von QT ist sicherlich, dass es leider C++ only ist, und damit keine schöne C-API bietet, so wie die WIN32 API, was das Programmieren damit natürlich etwas verkompliziert. Es soll allerdings nicht unerwähnt bleiben, dass es eine libqtc gibt, welche ein C-Binding für QT darstellt. Aufgrund des Designs von QT ist das Programmieren mittels des Bindings aber leider nicht wirklich so viel einfacher (vergleicht man es mit der relativ gut programmierbaren WIN32-API) und nachdem es sich um ein binding handelt hat man damit natürlich nur noch mehr overhead. Sonst läuft die Bibliothek jedoch ziemlich stabil und ist man bereit, für die Multiplattformfähigkeit etwas mehr Haupspeicher zu opfern (ca. 6 MB verschlingen alleine die DLLs!), ist es gegenüber speicherfressenden Java-basierenden Toolkits oder instabileren Toolkits wie WxWidgets immer noch eine gute Wahl.
Wie allgemein bekannt sein dürfte, gibt es auch eine GPL-Version des Toolkits. Nachdem man natürlich nicht die Katze im Sack kaufen will, kann man damit zuerst einmal nach Herzenslust damit programmieren und erst wenn man sein Produkt kommerziell vertreibt eine Lizenz kaufen. Die GPL-Version hat jedoch für die WIN32-Entwicklung einen entscheidenen Nachteil: Sie funktioniert standardmäßig nur mit dem MinGW environment, welches nicht nur fettere Executables produziert als der Microsoft C compiler, sondern auch keine komfortable IDE bietet, wie Visual Studio 6.0. Für kommerzielle Kunden wird natürlich eine visual Studio Integration angeboten. Allerdings wird auch hier neuerdings nur noch Visual Studio .NET unterstützt, welches für den praktischen Gebrauch meines Erachtens nach nicht zu gebrauchen ist.
Ich habe mir daher die Mühe gemacht, mehrere Patches zusammenzusuchen, die eine Integration in VS 6 ermöglichen und selber eine Workspace-Template gebaut und auch einige kleinere Sourcecode-Patches für QT geschrieben, damit das Erstellen von VS 6 Projekten reibungslos funktioniert.
Die einzelnen Patches aufzuzählen würde hier zuviel Platz einnehmen, sie sind aber in der Anleitung innerhalb des ZIP-Pakets beschrieben. In der Langanleitung steht, woher die ursprünglichen Patches sind und welche Teile davon verwendet wurden, bzw. welche Patches ovn mir hinzugefügt wurden. Nachdem das Ganze doch etwas komplexer ist, habe ich eine kleine Batchdatei geschrieben, die das Patches automatisch übernehmen sollte. Es werden die QT-Sources hierfür benötigt, man kann sich also auf eine mehrstündige Kompilierorgie gefasst machen (am besten über Nacht rennen lassen).
Das Paket gibt es hier.
Bei Fragen oder Anregungen einfach einen Kommetar hinterlassen (sollte jetzt endlich wieder funktionieren).
Bash: Alle Tage in einem Datumsbereich ausgeben
Nur weil ich es heute in der Arbeit gebraucht habe, aber auf die Schnelle keine brauchbare Lösung gefunden habe: Mit GNUdate funktioniert das Ganze relativ einfach:
DAT_FROM=20060101 # Starting date
DAT_TO=20060228 # End date
DAYS=$((($(date +%s --date $DAT_TO)-$(date +%s --date $DAT_FROM))/86400))
STARTDATE=`date --date="$DAT_FROM" +%Y-%m-%d`
for i in $(seq 0 $DAYS); do
date --date="$STARTDATE + $i days" +%Y%m%d
done
Sonst muss man die Datumskalkulationen selbst durchführen. Hierfür gibt es ein ganz praktisches Script:
http://www.unix.com/showthread.php?s=&postid=16559
Die Sache mit den verflixten #defines
Oft entstehen Probleme in Programmen aufgrund von ungenügender Kenntnis einer Programmiersprache.
Dieses Problem tritt vermehrt natürlich dann auf, wenn mehrere LEute an einem PRogramm arbeiten.
So ein Problem stellte sich unlängst bei uns in der Firma:
Für unsere Softwareprodukte haben wir ein eigenes Window-Management System entwickelt, welches die Terminal-Ausgabe über verschiedene Messages, die über ein eigenes RPC-System zum Client gesendet werden, auf eine GUI umsetzt, welche die entsprechenden Controls dann anzeigt.
Somit gibt es auch eine Funktion, mit welcher man sich für verschiedene Events registrieren kann, um den Netzwerktraffic-Overhead möglichst gering zu halten. Die jeweiligen Events werden dann zum Client geschickt.
Die Events sind logischerweise als Flags definiert, welche man setzen kann.
Irgendwer hat das Window-Management System dann um ein neues Event, und damit um ein neues Flag, nennen wir es FLAGx, erweitert. Folgende #defines wurden verwendet:
#define FLAG1 00000010
#define FLAG2 00000020
#define FLAG3 00000040
#define FLAG4 00000100
#define FLAGx 00000256
Und jetzt die Rätselfrage: Was hat der Programmierer, der FLAGx deklariert hat hier falsch verstanden?
Bitte als Comment posten 😉
Einfärben von ListBox und ListView controls
Die Aufgabenstellung selbst erscheint relativ simpel: Man möchte in einer ListBox bzw. in einem ListView einen Eintrag mit einer Hintergrundfarbe hinterlegen, um diesen z.B. besonders hervorzuheben.
Leider unterscheiden sich die Methoden hierfür bei ListBox und ListView erheblich.
Beim ListView ist das Ganze relativ einfach zu lösen:
Das ListView sendet eine WM_NOTIFY
Message an den Parent-Dialog, der das Control beinhaltet. Als lParam
erhält man eine LPNMLISTVIEW
Struktur, welche im hdr
Member wiederum einen Member code
enthält, welcher den eigentlichen Notification code angibt. Für das ListView ist hier für uns der Code NM_CUSTOMDRAW
interessant. Durch Erhalt der Message ist sichergestellt, dass es sich bei der in lParam an uns übergebene Struktur um eine NMLVCUSTOMDRAW
Struktur handelt. In deren Member nmcd
befindet sich die zur Message zugehörige Struktur NMCUSTOMDRAW
, welche wiederum den Member dwDrawStage
beinhaltet, der uns über den aktuellen Zeichnungsstatus informiert. Erhalten wir hier die Nachricht CDDS_PREPAINT
, wissen wir, dass das Zeichnen des ListViews gerade beginnt. An dieser Stelle müssen wir nun unser Interesse an den Notifications für die einzelnen Items der Liste anmelden. Das geht ganz einfach mit folgendem Kommando:
SetWindowLong (hWnd, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
Mit besagtem Kommando teilen wir dem Notifier unsere Antwort mit, die da eben lautet CDRF_NOTIFYITEMDRAW
.
Nun können wir den Drawstage pro Item mit der Nachricht CDDS_ITEMPREPAINT
dispatchen.
An dieser Stelle können wir nun auf den Zeichenprozess einfluss nehmen, indem wir die Members de rStruktur in lParam entsprechend modifizieren und den Notifier diese Änderung zur Kenntnis bringen.
Hier ein Beispiel, welches den Text rot färbt:
lplvcd->clrText = RGB(255,0,0);
SetWindowLong (hWnd, DWL_MSGRESULT, CDRF_NEWFONT);
Den kompletten Source zur besseren Übersicht gibt’s hier
Bei der ListBox ist das Ganze leider nicht ganz so einfach. Diese bietet nämlich leider keinen so bequemen WM_NOTIFY
-Mechanismus. Stattdessen muss man sich hier mit einem owner-Drawn control aushelfen, was natürlich einiges an Arbeit mit sich bringt. Daher sollte man als Style für das Control LBS_OWNERDRAWFIXED
verwenden. Ein LBS_HASSTRINGS
könnte sich auch als nützlich erweisen.
Bei einem Owner-Drawn control erhält der Parent eine WM_DRAWITEM
Notification, welche entpsrechend behandelt werden muss.
Als lParam erhält man ein LPDRAWITEMSTRUCT
, welches den Member itemAction
hat. Interessant sind hierbei die Actions ODA_SELECT
und ODA_DRAWENTIRE
.
Ein Zeichnen des ListBox-Eintrags im entsprehcenden Style erreicht man hier mittels Modifikation des device contexts, welcher sich im hDC
Member des DRAWITEMSTRUCT
s befinder.
Je nach itemState
Member sollte man die Vordergrund- und Hintergrundfarbe des Eintrags entsprechend setzen. Um die Systemfarben zu ermitteln, empfiehlt sich der Einsatz der Funktion GetSysColor
.Mit FillRect
die Auswahlmarkierung erzeugen und mit TextOut
schließlich den Text zeichnen. Also ein relativ hoher Aufwand, um nur die Farbe des Textes zu ändern.
Ein Codebeispiel gibt’s wie immer hier.
MessageBox mit Mauscursor auf default button
Ich bin unlängst vor dem Problem gestanden, dass ich eine Software, welche sich über ein Diktiergerät
steuern lässt (im Prinzip ein USB HID mit Zusatzknöpfen), entsprechend erweitern sollte, sodass der
Mauscursor auch in Dialogen entsprechend immer auf dem Default-Knopf platziert wird.
Das bietet den Vorteil, dass man beim schnellen Arbeiten z.B. bei der Frage, ob man ein Diktat speichern will,
nur noch die linke Maustaste drücken muss, um zu bestätigen, und nicht jedesmal mit der Trackpad-Kugel
hinfahren muss.
In selbst definierten Dialogen geht das ja relativ einfach mit SetCursorPos
. Was aber macht man bei Standard Windows-Dialoge, welche man mit MessageBox
generiert? Hier hat man ja normalerweise keinen Einfluss auf die Gestaltung des Dialogs. Aufmerksame Leser meines Blogs werden sich an meinen Artikel zur Anpassung von Standarddialogen erinnern, wo diese Problematik ja schon einmal besprochen wurde. Windows bietet ja glücklicherweise die Möglichkeit, mittels SetWindowsHookEx
und einer CBTProc-Funktion
die Erstellung von Messageboxes zu hooken und damit auch zu beeinflussen.
Konstruiert man also nun so einen Hook, kann man die HCBT_ACTIVATE
Notification abfangen. Zu diesem Zeitpunkt sind bereits alle Controls positioniert und gesetzt. Daher kann man dann mittels GetWindowLong
mit dem GWL_STYLE
Parameter abfragen, welcher Button default ist. Um das Ganze für alle möglichen Buttons in einer Schleife realisieren zu können, muss man die IDs der einzelnen Buttons wissen:
#define IDOK 1
#define IDCANCEL 2
#define IDABORT 3
#define IDRETRY 4
#define IDIGNORE 5
#define IDYES 6
#define IDNO 7
Es reicht also, zwischen IDOK
und IDNO
zu loopen.
Interessanterweise kann sich die Position des MessageBox-Fensters auch nach HCBT_ACTIVATE
noch ändern, sodass man mittels SetWindowPos
die Fensterposition auf eine marginal andere Position als die aktuelle ändern muss, damit das Fenster dort bleibt, wo es ist.
Dann einfach mit SetCursorPos
den Mauscursor auf das Default-Button platzieren und fertig.
Das Sourcecode dafür gibt es hier
Win32 API Tutorial
Beim Herumstöbern im Netz bin ich auf eine gute Seite mit nützlichen Win32-Codesnippets gestoßen, die ich dem geneigten Leser dieses Blogs nicht vorenthalten möchte: Win32 tutorial
COM Interface von C aus ansprechen
Ich habe ja schon bereits vor einiger Zeit erklärt, wie man COM-Interfaces von reinem C aus über die VTable eines COM-Objekts richtig anspricht. Allerdings gibt es natürlich manchmal das Problem, dass keine C-Style Deklaration des Interfaces vorliegt. Beim Surfen durch das Netz bin ich hier auf einen interessantes Artikel gestoßen, der gut erklärt, wie man nun COM-Interfaces von C aus anspricht: COM-Interfaces von C aus ansprechen
Bluetooth Crack
Thierry Zoller und Kevin Finistere haben auf der Hack.lu-Konferenz beeindruckend bewiesen, dass es mit der Sicherheit in Bluetooth-Geräten nicht weit her ist. Sie zeigten eine Demonstration mit dem Windows-Tool BTCrack. Bluetooth-PIN und Linkkey wurden nach dem „Handshake“ in fast Echtzeit gecrackt.
Größe eines struct members ohne Instanzierung ermitteln
Manchmal steht man vor dem Problem, die Größe eines einzelnen Eintrags einer Struktur wissen möchte, ohne die Struktur vorher instanzieren zu müssen, beispielsweise, um die Größe einer anderen Struktur entsprechend anzupassen.
Folgendes Beispiel soll dies verdeutlichen:
typedef struct {
char szText[10];
int iInt;
} Struct1;
typedef struct {
char szBuf[sizeof(Struct1.szText)];
long lLong;
} Struct2;
Natürlich funktioniert dieses Beispiel nicht, weil der sizeof() Operator nur auf “instanzierte” Strukturen angewandt werden kann, nicht auf die Typendefinition an sich. Was kann man in diesem Fall also tun?
Eine Funktion zur Auflösung zu schreiben fällt aus, da die Größe ja schon zur Compilezeit feststehen muss.
Nach einiger Überlegung kommt man dann zur eigentlich trivialen Lösung des Problems:
Wenn der Compiler unbedingt eine “Instanz” haben will, dann soll er sie doch mittels eines Typecasts haben.
Also erstellt man einfach einen Pointer auf die Struktur an Adresse 0, und verwendet dann wie gewohnt den sizeof()-
Operator. Folgendes Makro bewerkstelligt die Aufgabe:
#define SIZEOFMEMBER(struct_type, member) sizeof((((struct_type *)0)->member))
Die Definition von Struct2 auf dem obrigen Beispiel würde dann also folgendermaßen funktionieren:
typedef struct {
char szBuf[SIZEOFMEMBER(Struct1, szText)];
long lLong;
} Struct2;
Cursed GTK package
Bei meinen Recherchen zu einem GUI-Toolkit, welcher Win32 und Linux unterstützt und wenn möglichst auch noch eine rudimentäre Terminal-Darstellung zur einfacheren Wartung bietet, bin ich auf das folgende geniale Projekt gestoßen:
Cursed GTK.
Lädt man diese Bibliothek beim Laden einer GTK-Anwendung auf der Shell, so bekommt man eine ncurses-Oberfläche mit den wichtigsten Widgets. Somit ist es zumindest möglich, einfache Wartungsprogramme ohne viel Aufwand mit dem GLADE Userinterface builder zu basteln, welche gleichzeitig auch von der Shell aus noch bedienbar sind, sofern man sich auf einfache Widgets beschränkt.
Das Projekt hat nur leider den Nachteil, dass es seit Ende 2003 nicht mehr weitergewartet wird. Dementsprechend basiert es auch auf etwas älteren Libraries und funktioniert u.A. mit aktuellen Versionen der libpango (Teil von GTK) nicht mehr. Eine ältere libPango lässt sich wiederum nicht mehr ohne herumpatchen mit einer neueren Freetype-Version kompilieren usw. Kurzum: Das Ganze ist leider recht mühselig einzurichten.
Um einem diesen Aufwand zu ersparen, habe ich mir die Mühe gemacht, di ebenötigten Bibliotheken zu kompilieren und das Installationsscript der RPM-PAkete entsprechend zu fixen, sodass man eine Lösung hat, welche nur noch mit einem Shellscript installiert werden muss und dann automatisch mit den richtigen Bibliotheken out-of-the-box läuft.
Das Paket kann man sich hier herunterladen.
Sofern man rpm benutzen kann, muss man nur install.sh
ausführen und im Besten Fall installiert sich das Paket von selbst. Es ist allerdings darauf zu achten, dass sich das Verzeichnis des PAkets nach der Installation nicht mehr ändern darf, da man die in dne Unterverzeichnissen enthaltenen Bibliotheken zum Laden der jeweiligen Applikation benutzen muss.
Starten kann man eine GTK-Applikation im Textmodus mittels des enthaltenen Shellscripts runcursed.sh
.
Möchte man das Ganze wieder deinstallieren, so muss man nur uninstall.sh
ausführen.
Hat man Debian und damit kein RPM zur Verfügung, kann man sich mit ein wenig Bastelei mit alien
behelfen.
Auf jeden Fall ist das Ganze nach wie vor eine interessante Bibliothek. Bleibt nur zu hoffen, dass sich irgendwann wieder um dieses verwaiste Projekt annimmt.