_UNICODE versus UNICODE und so manches Eigentümliche

Wann nimmt man eigentlich was?
Jedesmal wenn ich Code ansehe finde ich mal einen

#ifdef _UNICODE

dann mal wieder einen

#ifdef UNICODE

Wann eigentlich was?
Die Frage wurde schon oft beantwortet (siehe The Old New Thing : TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE).

In Kurzform:
UNICODE wird in den Windows Header-Dateien verwendet.
_UNICODE in der C-Runtime (CRT).

Interessanterweise gibt es nur den Define _MBCS aber nicht den Define MBCS. Anhand der Namensgebung kann man sehen, dass _MBCS aus/für die CRT nur Einfluss hat. Die Windows API sagt einfach, wenn nicht Unicode dann ist es MBCS!

Daraus ergeben sich einige interessante Abhängigkeiten: 

_T ist abhängig vom Define _UNICODE genauso wie _TEXT (also der CRT)
TEXT ist wiederum abhängig vom Define UNICODE.

Jetzt wird es aber ganz lustig:
Die Datei tchar.h gehört zur CRT. Und TCHAR ist eigentlich kein Define aus tchar.h und der CRT. Dort wird nämlich eigentlich _TCHAR definiert und sollte TCHAR schon definiert sein (durch die windows.h), dann wird TCHAR nicht mehr umdefiniert.

Ist also nur UNICODE definiert und nicht _UNICODE, dann muss der folgende Code einen Compiler Fehler auslösen, wenn windows.h vor der tchar.h inkludiert wird. Erwartungsgemäß meldet der folgende Code einen Compiler-Fehler, obwohl dieser perfekt und korrekt aussieht:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
 const TCHAR szText[] = „“;
  // Must fail if UNICODE is defined and not _UNICODE, and windows.h included first
 _tcslen(szText);
 return 0;
}

In VC6 gab es noch keine Projekteinstellung für Unicode. Man musste also beide Defines setzen, als Präprozessor Definition. Andernfalls hatte man eine lustige Code Mischung und lief schnell mal in ein Problem wie oben.

Der Schalter in VC-2005 und VC-2003 für

  • „Use Unicode Character Set“ setzt beide Defines _UNICODE und UNICODE
  • „Use Multi-Byte Character Set“ setzt nur _MBCS.
  • „Not set“ setzt Erwartungsgemäß keinen der Defines und man kann das obige Beispiel und den Fehler ausprobieren.

Projektintern verwende ich grundsätzlich immer intern nur den define UNICODE um zu prüfen welche Art von Projekt hier läuft. Und ich achte darauf, dass die windows.h bzw. die afxwin.h Datei immer als erstes inkludiert werden. Dann ist TCHAR entsprechend konform für die Windows API gesetzt. Die CRT ist für mich meistens nur Beiwerk und ich vermeide Ihre Verwendung eigentlich wo ich kann.

An kritischen Stellen baue ich auch gerne den folgenden Test ein um ganz auf Nummer sicher zu gehen:

#if defined(UNICODE) ^ defined(_UNICODE)
#error Inconsitent UNICODE and _UNICODE definition
#endif

Mit dem XOR-Operator wird hier ein Fehler ausgelöst wenn nur einer der beiden Defines gesetzt ist.

Siehe auch die MSDN INFO: UNICODE and _UNICODE Needed to Compile for Unicode

Orcas kommt ohne hauseigenes SDK, aber mit einem SDK ;-)

Die neue VC Version wird die SDK Dateien nicht mehr in den eigenen Include Verzeichnissen haben, die unterhalb des entsprechenden Visual Studio liegen..

Wer kennt nicht das Problem, dass man eine Funktion oder eine Konstante aus einem neuen SDK benötigt, aber eigentlich ja alle SDK Dateien in den Include Verzeichnissen des Compilers findet und dieses ist natürlich längst veraltet. Also SDK installieren. Include und Lib Verzeichnisse eintragen…

Damit ist jetzt Schluss. Mit dem neuen Visual Studio wird ein SDK installiert. Und dieses wird dann verwendet. Es gibt also kein „Grund-SDK“ mehr, dass mit dem Visual-Studio Dateien installiert ist, sondern höchstens noch eines, das zusammen mit dem Visual Studio installiert wird. Aber eben in einem separaten Verzeichnis-Baum.
In der Beta steckt das Vista-SDK, das auch mit zu Auslieferung kommen wird.

Wurde aber auch Zeit für diese Änderung!

Der kleine Unterschied zwischen CStatic::SetIcon und CWnd::SetIcon

So schnell legt man sich rein. Da hat man ein CStatic in einem Dialog. Man besorgt sich mit GetDlgItem das entsprechende Fenster Objekt und führt SetIcon durch. Also etwa so

GetDlgItem(IDC_MYSTATIC)->SetIcon(hIcon);

Ergebnis: 🙄  Nichts passiert.

Nach etwas Debuggen wird der Fall klar. SetIcon ist nicht virtuell und wie bei allen CWnd und anderen Fenstern nichts anderes als eine Abkürzung/Wrapper für ein SendMessage. Mit dem kleinen Unterschied, dass CWnd:SetIcon WM_SETICON sendet, während CStatic::SetIcon ein STM_SETICON versendet.

GetDlgItem liefert aber (solange kein Subclass durchgeführt wird) immer einen CWnd-Zeiger. Also wird statt der erwarteten STM_SETICON Nachricht eine WM_SETICON Nachricht verwendet, die aber CStatic nicht interessiert.
SetIcon wird nur von Top-Level Fenstern verarbeitet.

BTW: Das Gleiche kann einem auch mit CButton::SetIcon passieren. Nein BM_SETICON gibt es nicht, aber es gibt die Nachricht BM_SETIMAGE, und der kann man eine Bitmap (Wrappername CButton::SetBitmap) oder eben ein Icon übergeben (Wrappername CButton::SetIcon). Auch hier lauert die gleiche Falle!

❗ Ein typischer Designfehler in einer Library! Hier wurde offensichtlich die konsistente Namensgebung in der MFC über das Verwechselungsproblem gestellt. In einer abgeleiteten Klasse sollte niemals eine Funktion gleichen Namen mit gleicher Signatur und unterschiedlicher Funktion existieren.

MFC unter Orcas arbeitet nur mit WINVER>=0x0501, d.h. XP

Das kann hoffentlich nur ein Bug oder Vergesslichkeit sein.
Wenn man WINVER auf einen Wert <=0x0500, d.h. einschließlich Windows 2000 setzt, dann bekommt man einen gemeinen Fehler beim kompilieren, wenn man die afximpl.h verwendet:

…\src\mfc\afximpl.h(629) : error C2059: syntax error : ‚<L_TYPE_raw>‘

Die Zeile 629 der afximpl.h verwendet den Typ HRAWINPUT, diesen gibt es aber in den SDK-Headern erst ab Windows XP, also mit WINVER>=0x0501! Das kann ja wohl nicht sein. Orcas Programme sollen als unterstes Target Windows 2000 haben.

Andererseits ist das Problem hier hausgemacht, weil das Programm eben diese interne afximpl.h benutzt. Sie ist ja intern, obwohl deren Definitionen eher public sein sollten.

Windows XP wäre ja als niedrigstes Betriebssystem wirklich ein Hammer! 😮

Report hier https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=277982

Nun evtl. bekommt man es auch etwas einfacher hin. ein simpler

#define HRAWINPUT DWORD

tut es auch, wenn WINVER eben nur mit 0x0500 definiert ist.

MS-SQL abfragen ob es noch genug Plattenplatz gibt (Teil 2)

Es gibt noch ein paar nette Tabellen und Abfragen die einem Informationen über die physikalische Größe eine SQL-Datenbank liefern. Denn sowohl die Daten-Datei als auch die Protokolldatei können über nicht allokierten Speicherbereich verfügen. Wenn man also wissen will, wie viel Platz wirklich noch verfügbar ist, muss auch dieser noch verfügbare Platz mit einbezogen werden.

Es sind also noch zu ergänzen:

  1. Sehr einfach ist die dbo.sysfiles zu verwenden. Auch über diese Tabelle erhält man schnell die Größe der Protokoll- und Datendatei in 8kb Blöcken.
  2. sp_spaceused liefert einem die komplette Größe der Datendatei und die Größe des nicht verwendeten Bereiches. sp_spaceused liefert übrigends zwei Resultsets wenn man es ohne Objektnamen aufruft! Nicht wundern.
  3. Und last but not least DBCC SQLPERF(LOGSPACE), dass die selbe Information für die Protokolldatei liefert. D.h. Größe der Protokolldatei in MB und den prozentual benutzten Speicherbereich. 

PS: Wie man so etwas herausbekommt. ❓
Nun man kann die MSDN lesen, aber man kann es sich auch einfacher machen. 😉
Ich habe einfach den SQL Profiler angeworfen, dann den SQL Server Enterprise Manager gestartet. Im Enterprise Manager habe ich die Taskpad Ansicht gewählt und einfach mal F5 gedrückt. Der Profiler hat mir dann gezeigt was der Enterprise Mangager so abfragt um die relevanten Daten zu ermitteln.

MS-SQL abfragen ob es noch genug Plattenplatz gibt

Kann man den MS-SQL Server abfragen ob es noch genug Plattenplatz gibt?

Manche Operationen kosten sehr viel Platz, besonders wenn man Datenbanken umstrukturiert. Da kann es leicht sein, dass gleich 100% mehr Platz benötigt wird. Zum einen weil die Protokolldatei evtl. immens wächst und zum zweiten weil einfach die Tabellen stark anwachsen weil zum Beispiel neue Indexe hinzukommen oder Spalten auf Unicode umgestellt werden.

Kann man also irgendwie den freien, zur Verfügung stehenden Platz ermitteln und entsprechende Berechnungen anstellen?

Ja! Es geht.

  1. Ausgangspunkt ist hier zum einen die dokumentierte System Tabelle sysdatabases, die den Namen der Datenbank, und den Namen physikalischen mdf-Datei liefert.
  2. Nun benötigen wie noch die aktuelle Größe, die wir ohne Probleme mit der Storedprocedure sp_databases ermitteln können, die Namen, die Größe in KB und eine NULL Spalte zu dieser Datenbank liefert.
  3. Den dritten und letzten Baustein für die Aufgabe liefert die nicht dokumentierte Funktion master..xp_fixeddrives, die in der ersten Spalte den Laufwerkbuchstaben liefert und in der zweiten den freien Speicherplatz in MB.

Wie man jetzt errechnet ob eine Größenänderung um 75% noch abgedeckt wird, bleibt dem Leser überlassen. Nett nicht! 🙂

❗ BTW: Diese Funktionen laufen verifiziert auf MS-SQL 2000 und auch auf MS-SQL 2005 Servern inkl. der Express-Edition.

Link Useful undocumented extended stored procedures

Selbstgemachter Manifest-Ärger…

In dem C++.de Forum kam dieser Thread Sehr spezielles Problem – (DLL + Manifest) auf!

Man muss den Thread nicht ganz lesen, ich will hier eine kurze Übersicht geben, wie man ein Projekt verbiegen kann, dass es nur noch Ärger macht, den man nur schwer (oder nach langem Suchen) in den Griff bekommt.

Ursprüngliches Problem:
Es ging mal wieder darum eine EXE so zu schreiben, dass Sie nicht von einer VC-2005 Runtime Library Installation (CRT+MFC) abhängig ist. Es ging um ein spezielles Plugin (also eine DLL).

Die Empfehlung war, wie so oft bei Manifest Problemen:
Linke sowohl CRT als auch MFC statisch.

Begründung:
Die DLL benötigt keine weiteren DLLs oder Third Party Libs. Ist also mehr oder weniger standalone. Also warum noch zusätzliche Abhängigkeiten schaffen. Auch wenn dies etwas mehr Hauptspeicher kostet, wenn andere Applikationen die 8.0 Runtime-Libs auch gleichzeitig verwenden können.
Es erübrigt die Installation der VCRedist_x86.exe oder auch andere, komplexere Tricks wie die applikationslokale Installation der Runtime-Libs.

Folgeproblem:
Sowohl für die MFC als auch für die CRT wurde nun statisches Linken im Projekt eingestellt. Aber dennoch wurde weiterhin ein Manifest erzeugt, das die CRT anforderte.
Das erzeugte doch nun einiges Rätselraten bei mir. 😕
Auch die Projektdatei, die mir zugesandt wurde brachte auf den ersten Blick keinen Aufschluss über das Problem.
Weitere Prüfung mit DUMPBIN ergab, dass die Objekt Dateien alle mit einem Manifest Eintrag kompiliert wurden, oder in anderen Worten, das im Sourcecode ein #pragma comment manifest Eintrag drin steht.

Lösung:
Einze kurze Recherche in der crtdefs.h ergab, dass die #pragma Einträge für die Manifeste nur erzeugt werden wenn, _DLL definiert wird. Die Doku sagt klar:

  1. Das ist eine interne, vordefinierte Präprozessor Variable
  2. Diese ist nur bei den Kompileroptionen /MD und /MDd definiert

Derjenige, der dieses Projekt erzeugt hat, hat auch die Präprozessor Variable _DLL vordefiniert. Das Resultat war natürlich, dass Manifest-Einträge erzeugt wurden und weiterhin die CRT-DLLs genutzt wurden.

Merke: Pfusche nie mit internen Nachrichten, Präprozessor-Variablen rum, die einen nichts angehen. Oft genug sind schnelle Workarrounds die man mit so etwas erreicht ein Schuss ins eigene Knie. 🙂

Ach ja! Man hätte übrigens noch weiter Pfuschen können und _CRT_NOFORCE_MANIFEST definieren können :mrgreen: ! Dann hätten wir den einen Pfusch mit einem anderen behoben!

Erste (durchaus positive) Kontakte mit Orcas Beta1 VPC

Ich habe mir mal die Orcas Beta 1 für VPC heruntergeladen und versuche gerade mal, ein paar meiner größeren Projekte damit zu kompilieren.

1. Erfahrung: Ich habe ein VC-2005 Projekt geöffnet um es zu konvertieren. Orcas stürzt ohne Fehlermeldung ab. Erst beim zweiten Versuch klappt es. Gleicher Effekt bei einem anderen Projekt. Scheint irgendwie an den bestehenden temporären Projektdateien zu liegen. Bugreport wurde gesendet. Dem muss ich nochmal nachgehen.

2. Erfahrung: Der Platform SDK Include-Pfad ist in den VS-Einstellungen nicht definiert.
Resultat: Die Datei msdaguid.h wurde nicht gefunden bei einem Programm, dass die ATL OLE-DB Client Templates verwendet. Scheint ein Known-Bug des verwendeten Vista SDKs zu sein. Also am Besten die entsprechenden Einträge konform wie in VS-2005 erzeugen. Man sollte auch gleich das BIN und LIB Verzeichnis kontrollieren. Sonst wundert man sich beim Linken und wenn andere SDK Tools – wie mc.exe – verwendet werden sollen.

3. Erfahrung: Ein Breaking Change für mich. CWnd::GetMenu war bisher nicht virtuell. Jetzt wurde diese Funktion in der neuen MFC 9.0 virtuell. Die Folge: Eine meiner Fensterklassen definierte selbst GetMenu mit HMENU als Returnwert. Ja so was soll man nicht machen, aber man kann es, solange die Funktion nicht virtuell ist. Das Projekt kompiliert nicht mehr.

4. Erfahrung: Ein mittelgroßes Projekt das von VC-2005 umgestellt wurde (ca. 400 Source und Header-Dateien) kompilierte ohne weiteren Fehler. Das macht doch Hoffnung 🙂

Download der Beta1 hier: Visual Studio Code Name „Orcas“ Downloads

MSI.LIB fehlt in VC-2005 Installation

Das verstehe mal wieder wer will 😕 ich verstehe es auf jeden Fall mal nicht.

Die MSI.H Include-Datei  findet sich in der im $(VCInstallDir)PlatformSDK\include Verzeichnis. Die dazu passende MSI.LIB Datei dagegen ist dagegen in $(VCInstallDir)PlatformSDK\lib nicht zu finden. Da spielt es auch keine Rolle ob man VS-2005 RTM oder SP1 hat.

Man muss sich diese Datei aus einem aktuellen SDK besorgen.

Alle Version ab VC6, über VS.NET 2002+2003 verfügen sowohl über Header als auch Lib-Datei.

Heath Stewart beschreibt das zwar in seinem Blog, aber warum das so ist steht dort nicht.

Wie einen Prozess unter Vista mit gehobenen Rechten starten?

Was macht man eigentlich wenn man einen Prozess unter Vista hat, der mit normalen Rechten arbeitet, nun aber einen Prozess starten möchte, der angehobene Rechte (elevated) benutzen soll? Also quasi eine Abkürzung für „rechter Mausklick -> Als Administrator ausführen“.

Das ist ausgesprochen einfach! Man verwendet ShellExecute oder ShellExecuteEx mit dem neuen Verb „runas“.

Der nachfolgende Code startet die Systemsteuerung mit angehobenen (administrativen/elevated) Rechten. Vorher erfolgt natürlich die allseits bekannte UAC Nachricht. Ist der User kein Administrator bekommt er eine Meldung, dass er sich entsprechend anmelden soll. Ansonsten muss er dem Zugriff zustimmen. Bricht der User den Dialog ab wird nichts ausgeführt.

int _tmain(int argc, _TCHAR* argv[])
{
  SHELLEXECUTEINFO shExecInfo;
  
  shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
  shExecInfo.fMask = NULL;
  shExecInfo.hwnd = NULL;
  shExecInfo.lpVerb = _T(„runas“);
  shExecInfo.lpFile = _T(„control.exe“);
  shExecInfo.lpParameters = NULL;
  shExecInfo.lpDirectory = NULL;
  shExecInfo.nShow = SW_SHOW;
  shExecInfo.hInstApp = NULL;
  
  ShellExecuteEx(&shExecInfo);
  return 0;
}

❗ PS: Übrigens versteht auch Windows 2003 Server und Windows XP das „runas“ Verb. In diesem Fall wird der entsprechende Anmeldedialog gezeigt, der es erlaubt den aktuellen Benutzer zu verwenden (mit evtl. eingeschränkten Rechten), oder eben einen anderen Account. Nett! 🙂
Nur scheinbar auch ein Feature, dass wieder mal nicht dokumentiert ist.