Der zweite Versuch: Visual C++ 2008 Feature Pack Refresh (MFCNext & TR1)

Nach dem ersten Versuch nun der zweite Versuch. Alle bisher bekannten Installationsprobleme sind gefixed.

Weitere Infos: http://blogs.msdn.com/vcblog/archive/2008/04/22/visual-c-2008-feature-pack-refresh.aspx

Download: http://www.microsoft.com/downloads/details.aspx?FamilyID=d466226b-8dab-445f-a7b4-448b326c48e7&displaylang=en

Das bereits installierte Feature Pack muss deinstalliert werden! Glücklich wer nur die Beta bisher installiert hat, der kann wie bisher einfach drüber installieren. ❗

BTW: Eine Responsezeit von 16 Tagen ist gar nicht soooo schlecht 😉

Visual Studio 2008 Feature Pain…

Hier die bisherige offizielle Bug-Liste des gerade veröffentlichten Feature Packs:

http://blogs.msdn.com/vcblog/archive/2008/04/12/visual-c-2008-feature-pack-setup-deployment-issues.aspx

Mit Ruhm hat sich Microsoft hier wirklich nicht bekleckert.
Vor allem ist das Feature Pack ohne ein funktionierendes Deployment für Vista erstmal wertlos…

In der Haut derjenigen, die das Setup und Deployment zu verantworten haben möchte ich nicht stecken.

Leider ist das Ganze wieder mal nicht ganz vertrauenserweckend. Man kann ja jetzt nur hoffen, dass der Rest des Packs mit nicht ganz so heißer Nadel gestrickt wurde. Die nächste Zeit wird es zeigen.

Visual C++ 2008 Libraries Extension Feature Pack Final Release (MFCNext & TR1)

❗ Es ist da, die finale Version des MFC-Feature Pack für VS-2008 (MFCNext & TR1) ❗

Das Feature Pack ist nur für die Englische VS-Version (ENU) ab Visual Studio 2008 Standard Edition verfügbar.
Andere Sprachversionen werden in Visual Studio 2008 Service Pack 1 enthalten sein.

Visual C++ 2008 Libraries Extension Feature Pack Final Release
Download link, 322.8 MB
Redistributable Package (x86)

Dokumentation:
MSDN MFC Feature Pack for Visual C++ 2008
MSDN TR1 Extensions

❗ Wichtige Anmerkung sofern ein Deutsches OS eingesetzt wird ❗

Bei mir schlug die erste Installation fehl (sowohl auf Deutschem Vista Ultimate SP1 als auch XP SP2). In der Log-Datei im %TMP% Verzeichnis konnte ich herausbekommen, dass die Datei SPInstallerResources.1031.dll vermisst wird:

Unable to load UI satellite DLL SPInstallerResources.1031.dll 

Nach einigem hin und her Tricksen mit Entpacken und Umbenennen der Datei fand ich folgenden Trick um das Feature Pack zu installieren:
Man muss einfach vorher in der Systemsteuerung in den Regions- und Spracheinstellungen, die Einstellungen für die Formate auf „Englisch (USA)“ umstellen. Zusätzlich habe ich auch den Standardort auf „Vereinigte Staaten“ eingestellt. Danach lief die Installation korrekt ab.

Es spielt übrigends keine Rolle ob die Feature Pack Beta Version zuvor installiert war!

Vista Stil bei Tree-Controls und List-Controls

Unter Vista wurden die Tree- und List-Controls noch einmal kräftig aufgemöbelt.
Diesen neuen Look kann man auch in seinen Applikationen nutzen und das mit 2 simplen Codezeilen.

#pragma comment(lib,"uxtheme.lib") 
SetWindowTheme(m_myCtrl.GetSafeHwnd(),L"Explorer",NULL);

Und schon hat man die neue Darstellung des Tree-Controls mit den Dreiecken, die Knotenlinien sind weg.
Beim List-Control hat man nun die hellblaue Färbung mit dem Hover-Effekt.

Vorraussetzung dafür ist, dass auch ein Manifest für die Windows Common Controls Version 6 vorliegt. Dem entsprechend ist SetWindowTheme und die Uxtheme.dll erst ab Windows XP verfügbar. Wer das ganze auch kompatibel mit Windows 2000 haben möchte, der verwendet natürlich das dynamische Laden der Uxtheme.dll und sucht den Einsprungpunkt für SetWindowTheme mit GetProcAddress!

Siehe auch MSDN Doku zu SetWindowTheme

LoadIcon macht es nicht richtig…

… oder sollte man sagen: Nicht so wie erwartet. 

In einer der Micosoft Newsgroup fand sich eine Anfrage, die sich um folgenden Code drehte:

HICON m_hIcon[2]; 
[...] 
m_hIcon[0] = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 
m_hIcon[1] = AfxGetApp()->LoadIcon(IDR_SMALL); 
[...] 
SetIcon(m_hIcon[0], TRUE);   // Großes Symbol verwenden 
SetIcon(m_hIcon[1], FALSE);  // Kleines Symbol verwenden

Das Problem des Posters war, dass er sich Mühe mit dem Layout eine kleines Icons gegeben hat, aber leider immer nur ein verkleinertes 32×32 Icon verwendet wurde.

Das Problem liegt an der Verwendung von LoadIcon! LoadIcon lädt immer das Standard-Format 32×32. Bzw. in dem Format, dass durch das System als Standard-Icon-Größe vorgegeben wird.

Um es richtig zu machen, muss man das Icon wirklich in der gewünschten Größe laden. Dazu verwendet man die Funktion LoadImage. Eine kleine Helper Routine LoadIconEx kann dann wie folgt aussehen:

HICON LoadIconEx(PCTSTR pszIconName, bool bLargeIcon) 
{ 
 HINSTANCE hInstance = ::AfxFindResourceHandle(pszIconName,RT_GROUP_ICON); 
 return reinterpret_cast<HICON>(::LoadImage( 
       hInstance, 
       pszIconName, 
       IMAGE_ICON, 
       GetSystemMetrics(bLargeIcon ? SM_CXICON : SM_CXSMICON), 
       GetSystemMetrics(bLargeIcon ? SM_CYICON : SM_CYSMICON), 
       0)); 
}

VS-Tipps & Tricks: Downgrade für VC-200x Projekte

Kann man ein auf VC-2005 erstelltes Projekt einfach auch unter VC-2003 builden?
Oder ein 2008er Projekt auf VS-2005 laden?

Der Mühsame weg, ist es, das Projekt neu aufzubauen. D.h. ein leeres Projekt anzulegen und die entsprechenden Dateien aus dem alten Projekt in das neue Projekt in der niedrigeren Version aufzunehmen.
Es geht etwas leichter mit einem kleinen Hack ❗

Nehmen wir das Beispiel eines Downgrades von VS-2008 auf VS-2005.

  • Man kopiert einfach die entsprechende VCPROJ Datei unter einen neuen Namen
  • Man öffnet diese Datei mit dem Editor seiner Wahl
  • Man schaut in die Datei und findet den folgenden XML Code

<?xml version="1.0" encoding="Windows-1252"?>
 <VisualStudioProject
  ProjectType="Visual C++"
  Version="9,00"

Alles was man nun ändern muss ist die Zeile 4 mit dem Version Eintrag.
Wie ändern diesen Eintrag auf

 Version="8,00"

für VS-2005. Will man zurück auf die Version VS-2003 dann muss der neue Wert

 Version="7.10"

heißen.

Und schon sind wir fertig. Man glaubt es kaum 🙂

Klar ist, dass natürlich neue Features, wie z.B. Eigenschaften des Manifest Compilers in der vorher gehenden Version, die das z.B. gar nicht kennt untergehen. Diese Dateien bleiben in der Projektdatei, aber werden Dank XML einfach in der alten VS Version ignoriert.

Hotfix für GDI Leaks unter Windows XP-SP2/2003 Server in MFC Applikationen

Unter XP SP2 und Windows 2003 Server kann es bei eingeschalteten Themes zu GDI-Leaks kommen. Speziell wird hier auf MFC Anwendungen hingewiesen (siehe dazu meine Anmerkung unten).

Die Beschreibung und der Download-Link für den Hotfix finden sich hier:
MFC applications leak GDI objects on computers that are running Windows Server 2003 or Windows XP

Anmerkung:

Dies ist ein Hotfix für Theme Handler unter Windows 2003 und Windows XP SP2. Er greift nicht in die MFC ein ❗
Man muss also eigentlich davon ausgehen, dass der Bug im Windows Themes Kern steckt und nichts mit der MFC zu tun hat, sondern eher damit zu tun hat wie in der MFC mit Windows Ressourcen umgegangen wird.
Den Effekt bekommt man ziemlich einfach hin, indem man mit dem Wizard eine MFC-MDI Applikation anlegt. Dann einfach Strg+N festhalten und alle Fenster wieder schließen mit Strg+F4, danach findet man einige Hundert GDI Objekte ausgewiesen im Task-Manager, die vorher nicht da waren.
Ich habe einen Test mit dem MDI-Sample aus dem Petzold gemacht und dieses zeigt diesen Effekt nicht. Evtl. liegt es auch einfach nur an dem Umgang mit den Toolbars. Genaueres konnte ich nicht herausbekommen. 

  • Erstaunlich 1.: Dieser Patch ist mit 2 Jahren doch relativ alt (März 2006) und er ist mir erst durch eine Diskussion in einer Produkt-Gruppe über den Weg gelaufen …
  • Erstaunlich 2.: Dieser Patch wird nicht durch den Windows Update Service installiert, obwohl er in meinen Augen da rein gehört ❗
    Ob nun der Update Service nun den primären Fokus auf Sicherheit hat oder nicht. Ich empfinde dieses Verhalten des Theme Handlers als kritisch!

AfxBeginThread versus _beginthreadex

Es hat sich ja mittlerweile schon herumgesprochen, dass man _beginthread(ex) anstatt CreateThread verwenden sollte, wenn man die CRT verwendet. Die Frage ist wie steht es nun mit _beginthread(ex)  und AfxBeginThread wenn man die MFC verwendet?

Es ist ähnlich wie bei der CRT, es gibt auch für die MFC einen Thread State, der im Thread Local Storage abgelegt wird.  Zu diesem Thread Local Storage gehören z.B. die temporären Maps für die Fensterverwaltung und Maps für GDI-Objekt Verwaltung. Auch für Tooltips und diverse OLE Funktionen werden in diesem Module Thread State Daten abgelegt.

Weiterhin werden einige Hooks gesetzt, die notwendig werden, wenn GUI verwendet wird. Dito Aufräumarbeiten, falls COM verwendet wird… (AfxOleInit)
Dieser Thread Local Storage wird normalerweise beim Zerstören des assoziierten CWinThread wieder freigegeben. Wird die Threadfunktion verlassen oder AfxEndThread aufgerufen, dann wird der Thread Module State mit dem entsprechenden Speicher freigegeben.

Was passiert, wenn man in einem MFC Programm einen Thread mit _beginthreadex startet und anschließend MFC Funktionen verwendet und dann mit _endthread den Thread terminiert?
Was passiert, wenn man mit AfxBeginThread einen Thread startet und ihn mit _endthread terminiert ❓

❗ Dann entsteht ein Leak, und je nach verwendeten Komponenten kein kleines ❗

Sollte die MFC Applikation also dynamisch Threads erzeugen, weil damit zum Beispiel Sockets überwacht werden oder andere nette Workerthreads bedient werden, dann kann man relativ  schnell unerfreuliches erleben.

Es gilt also 2 Dinge zu beachten:

  1. In einem MFC Programm sollte man immer AfxBeginThread verwenden! Man geht sonst das Risiko ein, dass bei der Verwendung der ersten MFC Funktion (AfxGetResourceHandle o.ä.) ein entsprechender Speicherblock angelegt wird und eben sonst nicht mehr freigegeben wird..
  2. Und beim Verlassen einer Threadfunktion kümmert man sich am Besten gar nicht darum, wie der Thread gestartet wurde :mrgreen: . Man muss explizit AfxEndThread gar nicht aufrufen. Man sollte einfach alle Threadfunktionen einfach durch return verlassen. Der Thread wird dann entsprechend der Funktion, die man beim Start gewählt hat auch terminiert. Dann bleibt – auch bei einer Änderung der Umgebung , ob nun mit oder ohne MFC. 

Anmerkung:
Warum werden diese Leaks nicht in der Debugausgabe angezeigt? Ganz einfach, weil diese Allokationen durch die MFC nicht getrackt werden. Das Speichertracking der Debugversion wird explizit für diese Allokationen ausgeschaltet.

Warum man manchmal AFX_MANAGE_STATE auch in seiner eigenen EXE aufrufen muss

AFX_MANAGE_STATE ist jedem bekannt, der mit DLLs hantiert. Es garantiert bei Verwendung der MFC DLLs, dass die entsprechenden Ressourcen bei den verschiedenen Ladeoperationen, wie z.B. CString::LoadString gefunden werden.

Warum ❓
AfxSetResourceHandle und AfxGetResourceHandle bedienen, eine globale Variable, die in der MFCn.DLL liegt.
Genau genommen ist es keine prozessglobale Variable, dies wird klar wenn man an Threads denkt. Die Variable wird threadlokal gespeichert.  Die HINSTANCE für AfxGetResourceHandle liegt in einer Struktur, die AFX_MODULE_STATE heißt. In dieser Struktur werden noch einige andere wichtige threadlokale Daten gespeichert. Das sind z.B. für die Interaktion mit der Managed-World, der Activation Context. Einen Blick auf den Inhalt dieser Struktur zu werfen lohnt sich.
Durch diese Struktur AFX_MOUDLE_STATE hat das Makro AFX_MANAGE_STATE seinen Namen.

Gesetzt den Fall wir haben EXE und DLLs (egal ob Standard oder Extensions DLLs), die alle die MFC DLLs dynamisch binden, dann wird AFX_MANAGE_STATE wichtig. Ruft eine EXE also eine Funktion aus einer DLL auf, hat natürlich die EXE das entsprechende Handle an AfxSetResourceHandle übergeben. Damit nun seinerseits die DLL eigene Ressourcen laden kann, wird durch AFX_MANAGE_STATE die alte AFX_MODULE_STATE Struktur gesichert (genau genommen der Zeiger darauf), und ein Zeiger auf die neue aktuelle AFX_MODULE_STATE Struktur gesetzt. CString::LoadString und CDialog::DoModal finden nun die richtigen Ressourcen in der DLL.
Der Destruktor sorgt nun am Ende der Funktion, dass der alte Zeiger der ursprünglichen AFX_MODULE_STATE Struktur zurückgesetzt wird, auf den Wert vor dem Aufruf.

Soweit mal die Theorie 🙂
Was passiert aber nun unter den folgenden Gegebenheiten:

  • Eine DLL ruft einen Dialog oder eine MessageBox auf. In der EXE existieren Fenster, die einen Timer gesetzt haben?
  • Oder eine DLL ruft einen Dialog auf, und die EXE hat einige COM-Objekte veröffentlicht, die nun von extern angesprochen werden können.
  • Die DLL ruft über einen Mechanismus eine Callback Funktion in der EXE auf.

Das Alles ist kein Problem, solange nicht ihrerseits die Funktionen aus der EXE auf die Idee kommen eine Ressource zu laden. Was würde dann passieren?

Klar, die DLL hat mit AFX_MANAGE_STATE den AFX_MODULE_STATE umgesetzt. Würde die EXE in der Callback-Funktion oder Timer-Funktion nun selbst auf die Idee kommen einen Dialog zu laden oder nur einfach CString::LoadString auszuführen, dann wird evtl. ein String oder Dialog geladen, aber vermutlich nicht der, den man erwartet.
Verwendet man nun Formatierungsfunktionen, wie z.B. Format, wird man manchmal böse Wunder erleben. Oder man lädt einen Dialog mit DoModal. Wenn man Glück hat ist der Dialog mit dieser ID nicht da und DoModal schlägt fehl. Wenn man Pech hat wird der Dialog geladen, aber die entsprechenden Controls die gebunden werden oder mit GetDlgItem gesucht werden sind nicht vorhanden. Und da die meisten Entwickler keine Prüfung auf NULL durchführen (z.B. hier bei GetDlgItem(IDC_MYITEM)->EnableWindow(FALSE)) kracht es an den absonderlichsten Stellen.
Auch in der EXE ist man gut beraten in OnTimer Handlern AFX_MANAGE_STATE zu verwenden, wenn der Timer auch Ressourcen verwendet. Gleiches gilt in COM Interfaces oder IDispatch Interfaces. Glücklicherweise sorgen hier die Wizards für korrektes Verhalten.

Mehr noch: Unglücklicherweise kann theoretisch jeder Windows Handler zu diesem Problem führen, wenn diese Fensterfunktion direkt über die modale Nachrichtenschleife aus einer DLL aufgerufen wird.
Und auch bei mancher Funktion, die als Callback aus einer DLL verwendet wird, kann ein zusätzliches AFX_MANAGE_STATE nicht schaden.

Late Binding und schwache Performance durch GetIDsOfNames

Immer wieder sehe ich Entwickler, die über Late Binding COM Komponenten ansprechen. Das ist an sich nur zu unterstützen, denn DISPIDs können sich schnell mal ändern, wenn sich ein Interface ändert. Gerade bei den rasant an Verbreitung zunehmenden .NET Komponenten, die man mal schnell in die eigene Anwendung per COM einbindet, kann das Binding über die Interfaces und DISPIDs schnell zum Frust werden. Gleiches gilt für Office Komponenten, bei denen man sich nicht zwingend an ein Interface binden will. Zudem ist es der von Microsoft empfohlene Weg die Office-Automation zu benutzen (siehe auch hier).

OK! Also man nimmt Late Binding und verwendet z.B. die aktuellen Wrapper, die netterweise von der ATL zur Verfügung gestellt werden,  z.B. die netten Funktionen aus der CComDispatchDriver Klasse. GetPropertyByName, PutPropertyByName und auch die Funktionen Invoke0, Invoke1 und InvokeN haben entsprechende Überladungen, die die Verwendung von Funktions-/Eigenschaftsnamen direkt erlauben.

Der Nachteil liegt nicht gleich auf der Hand. Immer wenn solch eine Funktion aufgerufen wird, wird nicht durch der IDispatch::Involke ausgeführt, sondern auch ein Aufruf von IDispatch::GetIDsOfNames. Bei einem Out-Of-Process-Server kann dieser Roundtrip einiges an Performance kosten. Dabei ist es so einfach es besser zu machen.

Die Doku von IDispatch::GetIDsOfNames sagt folgendes:  

The member and parameter DISPIDs must remain constant for the lifetime of the object. This allows a client to obtain the DISPIDs once, and cache them for later use.

Und wer den oben genannten KB-Artikel aufmerksam gelesen hat, der hat auch was von DISPID-Caching mitbekommen.

Ich benutze gerne die die Wrapper, die in der MFC automatisch erzeugt werden, wenn man einen Type-Library über den Class View importiert. Hier werden die Funktionen auch in eine nette kleine COleDispatchDriver-Klasse verpackt und man bekommt netterweise ein gutes Exception Handling den Fehlerfall geliefert. Leider werden hier aber auch wieder nur DISPIDs verwendet.
Aber mit einem kleinen Trick, kann man diese Klassen genial einfach für Late Binding nutzen. Ich gehe wie folgt vor:

  • Ich importiere die Type-Library (tlb) mit dem Visual Studio Class View.
  • D.h. ich habe jetzt alle Wrapper mit DISPIDs, Was ich eigentlich für Late Binding vermeiden will.
  • Jetzt passe ich einfach die einzelnen Wrapper Funktionen in der folgenden Art und Weise an, ich ersetze die DISPID durch eine statische Variable:
void CMyWrapper::DoSomething(){
  static CDispIdHolder dispid(m_lpDispatch,L"DoSomething");
  InvokeHelper(dispid, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
  • Die kleine Klasse die ich hier verwende macht nun den Rest und besorgt die DISPID und cached sie damit weil die Variable statisch definiert ist:
class CDispIdHolder
{
public:
 CDispIdHolder(IDispatch *pDispatch,LPCOLESTR pName)
  : m_dispid(DISPID_UNKNOWN)
  {
   HRESULT hr = pDispatch->GetIDsOfNames(
         IID_NULL,
         &const_cast<LPOLESTR>(pName),
         1,
         LOCALE_SYSTEM_DEFAULT,
         &m_dispid);
   if (FAILED(hr))
    AfxThrowOleException(hr);
  }
  operator DISPID() { return m_dispid; }
private:
 DISPID m_dispid;
};

Die Exception, die man verwendet ist natürlich Implementierungsfrage.

❗ Der Performance Gain ist zum Teil beträchtlich, besonders wnen bestimmte Funktionen sehr oft aufgerufen werden müssen!

Anmerkung (für alle die es ganz genau nehmen):
Wenn man die Doku genau liest, heißt es natürlich hier, dass die Implementierung nur für die Lebenszeit des Objektes konstante DISPIDs garantiert. Wenn man allerdings bei Early Binding schon DISPIDs als konstant annimmt, ist meine Methode für Late Binding sicherlich vertretbar.