Mein erster Codeproject Artikel…

Nun habe ich es endlich mal geschafft und meinen ersten kleinen Artikel auf Codeproject geschrieben:

http://www.codeproject.com/cpp/PrivateAssemblyProjects.asp

 Er schließt sich nahtlos an über alles das was ich hier achon über die Manifest-Hölle geschrieben haben…

Konfussion mit einem RTF Control und EM_STREAMIN/EM_STREAMOUT

Beim Umstellen eines größeren Projektes auf Unicode hatte ich beim Testen einen Crash. Nichts ungewöhnliches.

Es betraf das Ein- und Ausstreamen eines RTF Controls. Zum einen wurden RTF Daten bzw. auch Textdaten in einem Stream übergeben bzw. ausgelesen. Erste Überraschung EM_STREAMIN/OUT mit SF_TEXT will nie Unicode und liefert nie Unicode! Und hier war mein Denkfehler.

Ein Test zeigte folgendes Verhalten mit den unterschiedlichen Streammodes:

  • SF_RTF liefert/will immer einen 8bit Daten Stream, den man in einem CStringA Buffer leicht speichern kann.
  • SF_TEXT liefert/will auch immer einen 8bit Datenstream, ganz egal ob das Control CreateWindowW oder mit CreateWindowA erzeugt wurde.
  • SF_TEXT|SF_UNICODE liefert/will auch immer einen Unicode Datenstream, ganz egal ob das Control CreateWindowW oder mit CreateWindowA erzeugt wurde.

Auch etwas irritierend war für mich die Dokumentation, aber durch aus korrekt.
Die EditStreamCallBack Funktion bekommt laut Doku immer eine Byte Zahl für die Buffergröße mitgegeben. Und es sind wirklich immer Bytes! Keine Anzahl von Zeichen/Buchstaben. Wenn man also Daten mit SF_TEXT|SF_UNICODE gestreamed werden, dann bekommen wir die Anzahl der Bytes. Die Anzahl der Buchstaben ist natürlich nur halb so groß wie die Anzahl der übergebenen bzw. zu übergebenden Bytes.
Eine Warnung bzw. ein erklärender Hinweis wäre an dieser Stelle sicherich auch angebracht.

_MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST

In meinem Blog habe ich bereits über Libraries und die Verwendug von _CRT_NOFORCE_MANIFEST geschrieben (siehe Link unten).

Wenn man nun eine Library erzeugt, die die MFC oder die ATL benutzt, sollte man sich auch noch der beiden Defines _MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST bewusst sein. Diese beiden Defines verhindern, dass durch die Verwendung der ATL bzw. MFC Include-Dateien #pragma comment(linker,”/manifestdependency:..”) Statements erzeugt werden.

Werden diese Defines konsequent verwendet, dann hat der Benutzer der Library die volle Kontrolle welche CRT, MFC bzw. ATL Version angebunden wird.

Warum man sich mit diesen Defines beim erzeugen einer Library auseinenadersetzen sollte kann man in diesem Artikel nachlesen: Warum man seine Libraries mit _CRT_NOFORCE_MANIFEST erzeugen sollte!

❗ BTW: Durch diese Defines kann man allerdings nicht verhindern, dass überhaupt Manifest-Einträge erzeugt werden. Selbst wenn man sein Programm mit den entsprechenden Defines kompiliert. Die Objektdateien haben dann zwar keine Manifest-Einträge, aber spätestens in dem Moment, in dem man das Programm linkt werden aus der CRT, MFC bzw. ATL Libraries Objektdateien gezogen die wieder entsprechende #pragma comment(linker,”/manifestdependency:..”) Einträge haben. Entsprechend bekommt das Manifest Tool dann auch Futter. Dazu mehr in einem späteren Artikel 🙂

Wie findet die MFC 8.0 eigentlich die MFC80lll.DLL Dateien?

Es ist vielleicht schon machen aufgefallen, dass auch die sprachabhängigen Ressourcen-Dateien der MFC als Side by Side Assemblies installiert werden durch VCREDIST_X86.EXE.
Schaut man sich an wie ein Programm erzeugt wird, dass die MFC80.DLL/MFC80U.DLL verwendet, dann weiß man wie Manifeste aussehen die für das entsprechende Programm erzeugt werden. Wir finden im Allgemeinen drei Dependency-Einträge für die MFC, die CRT und die ComCtl32 DLL.

Erstaunlicherweise wird kein Eintrag für die Sprachdateien erzeugt und scheinbar auch nicht benötigt. Denn wenn man das Programm im Debugger auf einem Deutschen XP oder Vista startet sieht man in der Debug-Ausgabe, dass die entsprechende Datei MFC80DEU.DLL aus dem Side by Side Storage geladen wird.

Ein Blick in die MFC80(U).DLL zeigt auch ein Manifest. Dieses passt genau auf die Sprachdateien. Allerdings ist dieses Manifest nicht aktiv, denn es hat keine „gültige“ ID (1000), die das Betriebsystem veranlassen würde es zu berücksichtigen. Zudem hätte dieses Manifest auch nur einen Effekt, wenn die Sprach-DLLs implitzit geladen würden.
Diese Dateien werden aber explizit geladen. Der Code der dafür verantwortlich ist wird zuallererst in der DllMain und der Initialisierung der MFC80-DLL und ein zweites mal in CWinApp::InitInstance aufgerufen.

Der entsprechende Code dort, lädt das Manifest aus der DLL, erzeugt einen Aktivierungs-Kontext und führt dann den entsprechenden AfxLoadLibrary Aufruf durch. Anschließend wird der Kontext wieder zerstört. Wird die Sprach-DLL nicht gefunden wird ein zweites Mal versucht die Datei nun aus dem Programmverzeichnis bzw. über die normalen Suchpfade zu laden.

Diese Methode über das eingebaute Manifest lässt sich nicht aushebeln. Verwendet man selbst eine Technik, die die MFC-DLLs als private Assemblies lädt, dann versucht die MFC dennoch die Sprach-DLLs Side by Side zu laden. Das Ganze ist etwas ärgerlich, wenn man private Assemblies pur verwenden möchte. Einziger Trick wäre hier CWinApp::LoadAppLangResourceDLL zu überschreiben, aber das verhindert nicht den ersten Aufruf aus der DllMain der MFC-DLL heraus.

❗ Wer sich das Ganze genauer ansehen will, sollte sich die Datei appcore.cpp mal ansehen.
Entscheidend hier sind die Funktionen: CWinApp::LoadAppLangResourceDLL und AfxLoadLangResourceDLLEx.

❗ Schaut man sich die MFC-DLLs übrigends noch genauer an, dann stellt man fest, dass sie gar keine Manifeste enthalten. Und das ist auch gut so, sonst wäre es ja auch nicht möglich die MFC und die zugehörige CRT als Private Assembly zu laden. Die MFC verwendet also immer die CRT, die im Kontext der EXE geladen wird.

_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