Ein weiterer interessanter Artikel zum (zukünftigen) Umgang mit c++ bei Microsoft (von Marius Bancila): C++ Renaissance at Microsoft.

Im Microsoft SQLNCli team blog wurde dazu der Artikel Microsoft is Aligning with ODBC for Native Relational Data Access veröffentlicht. Einige der vielen Fragen, die sich nun insbesondere für Ole-DB-Entwickler stellen, werden in dieser FAQ beantwortet.

Nachdem der c++-Standard nun offiziell unter dem Namen c++11 verabschiedet worden ist, stellt sich natürlich die Frage, was wir von diesem Standard schon jetzt verwenden können. Der im Stdcxx Wiki veröffentlichte Artikel C++0xCompilerSupport zeigt, welcher Compiler welche c++11-Feutures bereits unterstützt.

Mit Windows XP führte Microsoft die sogenannte Side-by-Side-Technologie (SxS) ein, durch die es möglich wurde, DLLs in unterschiedlichen Versionen auf einem System zu verwenden. Hierdurch können Konflikte vermieden werden, wenn Anwendungen installiert sind, die unterschiedliche Versionen einer DLL benötigen.
Die entsprechenden DLLs werden im Verzeichnis WinSxS des Windows-Systemordners gespeichert. Um festzustellen, welche Anwendung auf welche Version einer DLL zugreift, kann man das von Microsoft bereitsgestellte Tool sxstrace.exe verwenden. Über die Eingabeaufforderung kann man vor dem Start der Anwendung die Datenaufzeichnung mit
  sxstrace Trace -logfile:test.log
beginnen. Das Tool schreibt alle Daten in die angegebene Logdatei. Nach dem Starten der Anwednung kann man die Aufzeichnung beenden und das Ergebnis über
  sxstrace Parse -logfile:test.log -outfile:test.txt
in eine Textdatei konvertieren.
Führt man diese Anweisung nicht als Administrator aus, dann kann es zu der Fehlermeldung
 sxstrace Trace -logfile:test.log
 StartTrace failed.  Error Message is:
 Unknown error.
kommen. In diesem Fall sollte man die Eingabeaufforderung als Adminstrator öffnen (d.h. mit der rechten Maustaste auf den Befehl “Eingabeaufforderung” klicken, um das Kontextmenü zu öffnen, um dort “Als Administrator ausführen” auswählen zu können):
Eingabeaufforderung als Administrator öffnen

Eingabeaufforderung als Administrator öffnen

Alternativ kann man die Eingabeaufforderung auch über den “Ausführen…”-Dialog öffnen, den man in diesem Fall mit der Tastenkombination [Windows-Taste]+R aufruft. In dem mit “Öffnen” beschrifteten Eingabefeld gibt man dann “cmd” ein und bestätigt diese Eingabe mit OK. Daraufhin sollte sich die Eingabeaufforderung mit Administratorrechten zeigen.
Detailliertere Informationen zu diesem Thema sind in diesem Artikel zu finden.

Die neuen (Fenster-) Klassen der MFC, die zum MFC Feature Pack gehören, bieten von Haus aus die Möglichkeit, die aktuellen Zustände der Fenster einer Anwendung (Größe, Position etc.) nach dem Beenden eines Programms in die Registry zu schreiben. Wird die Anwendung zu einem späteren Zeitpunkt wieder gestartet, dann können die alten Fensterzustände mit diesen Registry-Einträgen wiederhergestellt werden. Was aber tun, wenn die Registry hierfür nicht verwendet werden soll?

In den Methoden

  BOOL LoadState(LPCTSTR lpszProfileName, int nIndex, UINT uiID);
  BOOL SaveState(LPCTSTR lpszProfileName, int nIndex, UINT uiID);

der Klasse CBasePane nimmt die Serialisierung der Zustände ihren Lauf. Dabei ist ein Objekt vom Typ CSettingsStore für das Speichern der Daten (in die Registry) verantwortlich:

  CSettingsStoreSP regSP;
  CSettingsStore& reg = regSP.Create(FALSE, FALSE);

Wie das genau funktioniert, kann man an den (virtuellen) Read- und Write-Methoden der Klasse CSettingsStore (vgl. afxsettingsstore.h) sehen. Das Framework gestattet es aber auch, über ein CRuntimeClass-Objekt eigene CSettingsStore-Objekte zu verwenden. Definiert man sich also eine von CSettingsStore abgeleitete Klasse (z.B. CMySettingsStore), dann kann man die Kontrolle über das Speichern der Daten (z.B. in Dateien) übernehmen, indem man die Read- und Write-Methoden entsprechend überschreibt. Voraussetzung ist, dass die eigene Klasse zuvor noch mit

  CSettingsStoreSP::SetRuntimeClass( RUNTIME_CLASS( CMySettingsSore ) );

angemeldet wird.

Mit der Funktion „Externe Tools“ im Menü „Extras“ kann man die Entwicklungsumgebung mit externen System recht elegant verknüpfen. Setzt man beispielsweise ein webbasiertes Bug-Tracking-System (wie z.B. BugTracker.Net) ein, dann könnte die Verknüpfung folgendermaßen hergestellt werden. Zuerst öffnet man mit dem Befehl “Externe Tools” im Menü “Extras” den gleichnamigen Dialog:

Betätigt man nun die Schaltfläche “Hinzufügen”, so ist es möglich ein neues externes Tool anzulegen. In diesem konkreten Fall geben wir mal

  • Befehl: C:Program FilesInternet Exploreriexplore.exe.
  • Argumente: http://webtrack/edit_bug.aspx?id=$(CurText). Hierbei steht $(CurText) für den aktuell im Editor des Visual Studios markierten Text.

ein und schließen den Dialog wieder. Wenn man Änderungen und Neuerungen im Quellcode nun immer mit der entsprechenden Bug-ID kennzeichnet, dann kann man zukünftig, durch einfaches markieren der Bug-ID und anschließendem Aufruf des externen Tools direkt zur ausführlichen Beschreibung des Bugs im Bug-Tracking-System springen. Auf ähnliche Art und Weise können dann auch Dokumentationssysteme oder Wikis verknüpft werden.

Um das Ganze noch etwas komfortabler zu machen, können wir noch eine eigene Toolbar (und ein Tastenkürzel) für den Schnellzugriff auf unser neues Externes Tool einrichten. Dazu ruft man zuerst im Menü “Extras” den Befehl “Anpassen…” auf. Es öffnet sich ein Dialog, der über zwei Reiter verfügt. Unter dem reiter “Symbolleisten” richten wir eine neue Symbolleiste ein, die wir mal mit “Meine Tools” bezeichnen:

Anschließend wechseln wir auf den Reiter “Befehle” und wählen dort die soeben angelegte Symbolleiste “Meine Tools” aus. Mit der Schaltfläche “Befehl hinzufügen” fügen wir dann das Externe Tool mit der Nummer 7 hinzu, das in diesem Fall dem zuvor angelegten Aufruf des Bug-Tracking-Systems entspricht:

Wenn man nun alles richtig gemacht hat, dann erscheint im Visual Studio die neu angelegte Symbolleiste mit einer Schaltfläche.

18. May 2011 · Write a comment · Categories: MFC

Sowohl in Kontextmenüs (CMFCPopupMenu) als auch in “normalen Menüs” (CMFCMenuBar) gibt es für jeden Menüeintrag ein Objekt vom Typ CMFCToolBarMenuButton. Handelt es sich bei einem Menüeintrag um ein Untermenü (Submenü), dann kann man über die Methode

  const CObList& CMFCToolBarMenuButton::GetCommands() const;

auf die Menüeinträge des Untermenüs zugreifen. Mit dieser Information lässt sich dann recht einfach eine rekursive Funktion implementieren, die ab einem gegebenen Menüpunkt alle untergeordneten Menüpunkte systematisch durchläuft, z.B.:

	void IterateMenu( CMFCToolBarMenuButton* pButton )
	{
		if( pButton )
		{
			// Irgendetwas mit diesem Menüeintrag anstellen:
			pButton-> ...

			// Hat der Menüpunkt ein Untermenü?
			const CObList& lCmds = pButton->GetCommands();
			for( POSITION pos = lCmds.GetHeadPosition(); pos != NULL; )
			{
				IterateMenu( (CMFCToolBarMenuButton*) lCmds.GetNext( pos ) );
			}
		}
	}

Leider besitzen die beiden genannten Menüklassen keine gemeinsame Basisklasse, die ein einheitliches Iterieren über alle Elemente der ersten Ebene ermöglichen könnte. Allerdings ist diese Funktionalität durchaus an beiden Klassen vorhanden.

Für ein CMFCPopupMenu-Kontextmenü könnte diese Funktion dann folgendermaßen eingesetzt werden:

	CMFCPopupMenu* pMenu = ...
	for( int i = 0; i < pMFCPopupMenu->GetMenuItemCount(); i++ )
	{
		IterateMenu( (CMFCToolBarMenuButton*) pMenu->GetMenuItem( i ) );
	}

Im Fall eines Menüs vom Typ CMFCMenuBar könnte das dagegen so aussehen:

	CMFCMenuBar* pMenu = ...
	for( int i = 0; i < pMenu->GetCount(); i++ )
	{
		IterateMenu( (CMFCToolBarMenuButton*) pMenu->GetButton( i ) );
	}

Der GULP IT-Projektmarktindex misst seit 1998 kontinuierlich die über den GULP Server zugestellten Projektangebote. Der daraus gewonnene Index ist ein idealer Gradmesser für die Angebots- und Nachfragesituation am IT-Projektmarkt.

Link: GULP IT-Projektmarktindex: Der Gradmesser für die Angebots- und Nachfragesituation am IT.

Möchte man im Hauptmenü einer MFC-basierten Anwendung einen Menübefehl mit einem gesetzten “Häkchen” versehen, so gibt es verschiedene Möglichkeiten, dies zu tun. In diesem Zusammenhang habe ich bereits des Öfteren Code der folgenden Art gefunden:

// ---------------------------------------------
// Klasse.h
// ---------------------------------------------
class CKlasse : public CWnd
{
public:
	...
	afx_msg void OnBefehl( void );
	...
	DECLARE_MESSAGE_MAP()
};

// ---------------------------------------------
// Klasse.cpp
// ---------------------------------------------

BEGIN_MESSAGE_MAP
	ON_COMMAND( ID_BEFEHL, OnBefehl )
END_MESSAGE_MAP

void CKlasse::OnBefehl(void)
{
	// Ist das Häkchen vor dem Menübefehl gesetzt?
	UINT oldstate, checkstate;
	HMENU hMenu = AfxGetMainWnd()->GetMenu()->GetSafeHmenu();
	oldstate = ::GetMenuState( hMenu, ID_BEFEHL, MF_BYCOMMAND );
	checkstate = oldstate;
	checkstate &= MF_CHECKED;
	if ( checkstate == MF_CHECKED )
	{	// Das Häkchen ist gesetzt. Entferne das Häkchen nun.
		oldstate = 0;
		::CheckMenuItem( hMenu, ID_BEFEHL, oldstate );

		...
	}
	else
	{	// Das Häkchen ist nicht gesetzt. Setze es
		oldstate ^= MF_CHECKED;
		::CheckMenuItem ( hMenu, ID_BEFEHL, oldstate);

		...
	}
}

Das ist grundsätzlich nicht falsch und funktioniert ja auch. Insgesamt bietet die MFC dafür aber einen deutlich eleganteren Mechanismus, den ich nachfolgend mal kurz beschreiben möchte.

Möglichkeiten der MFC

Die MFC bietet für die einleitend beschriebene Problemstellung einen Mechanismus an, der mit Hilfe der Message-Map-Makros folgendermaßen implementiert werden kann:

// ---------------------------------------------
// KlasseNeu.h
// ---------------------------------------------
class CKlasseNeu : public CWnd
{
	BOOL m_bBefehlIsChecked;
public:
	...
	afx_msg void OnBefehl( void );
	afx_msg void OnUpdateBefehl( void );
	...
	DECLARE_MESSAGE_MAP()
}; 

// ---------------------------------------------
// KlasseNeu.cpp
// ---------------------------------------------

BEGIN_MESSAGE_MAP(CKlasseNeu, CWnd)
	ON_COMMAND( ID_BEFEHL, OnBefehl )
	ON_UPDATE_COMMAND_UI( ID_BEFEHL, OnUpdateBefehl )
END_MESSAGE_MAP()

void CKlasseNeu::OnBefehl(void)
{
	m_bBefehlIsChecked = !m_bBefehlIsChecked;

	if( m_bBefehlIsChecked )
	{
		...
	}
 	else
 	{
 		...
 	}
}

void CKlasseNeu::OnUpdateBefehl( CCmdUI* pCmdUI )
{
	pCmdUI->SetCheck( m_bBefehlIsChecked );
}

Das ist – wie man sehen kann – nicht nur viel einfacher zu lesen, sondern vom Coding her auch wesentlich eleganter, da man auf diese Weise gar nicht direkt auf die UI-Elemente zugreifen muss.

Weitere Möglichkeiten der MFC

Der Command- und Update-Mechachnismus bietet aber noch weitere Möglichkeiten. Zum Beispiel kann man mit dem ON_UPDATE_COMMAND_UI eine einzige Methode für mehrerer Update-Aktionen verwenden. Dies hat den Vorteil, dass die man die Klassendefinitionen in vielen Fällen stark reduzieren kann, weil man nicht mehr so viele Methoden benötigt. Beispiel:

// ---------------------------------------------
// KlasseNeu.cpp
// ---------------------------------------------

BEGIN_MESSAGE_MAP( CKlasseNeu, CWnd )
	ON_UPDATE_COMMAND_UI( ID_BEFEHL1, OnUpdateMenu )
	ON_UPDATE_COMMAND_UI( ID_BEFEHL2, OnUpdateMenu )
	ON_UPDATE_COMMAND_UI( ID_BEFEHL3, OnUpdateMenu )
END_MESSAGE_MAP()

void CKlasse::OnUpdateBefehl( CCmdUI* pCmdUI )
{
	switch( pCmdUI->m_nID )
	{
		case ID_BEFEHL1:
			pCmdUI->Enable( ... )
			pCmdUI->SetCheck( ... )
			break;
		case ID_BEFEHL2:
			pCmdUI->Enable( ... )
			...
			break;
		case ID_BEFEHL3:
			...
			break;
		default:
			// Unbekannter Befehl
			break;
	}
}

Es sollte auch berücksichtigt werden, dass in einem ON_UPDATE_COMMAND_UI-Handler nicht zu komplexe und berechnungsintensive Aktionen verwendet werden, da diese Handler sehr häufig aufgerufen werden.

In Analogie zum ON_UPDATE_COMMAND_UI-Handler gibt es auch einen ähnlich aufgebauten ON_COMMAND-Handler mit dem Namen ON_COMMAND_EX, der z.B. so eingesetzt werden kann:

ON_COMMAND_EX( ID_BEFEHL1, OnBefehl );
ON_COMMAND_EX( ID_BEFEHL2, OnBefehl );
ON_COMMAND_EX( ID_BEFEHL3, OnBefehl );

Mit der Methode

afx_msg BOOL OnBefehl( UINT nCmdUI );

kann man dann vollkommen analog zu den ON_UPDATE_COMMAND_UI-Methoden das hier machen:

BOOL CKlasseNeu::OnBefehl( UINT nCmdUI )
{
	switch( nCmdUI )
	{
		case ID_BEFEHL1:
			...
			break;
		case ID_BEFEHL2:
			...
			break;
		case ID_BEFEHL3:
			...
			break;
		default:
			// Unbekannter Befehl
			break;
	}
}

Insbesondere bei sehr übersichtlichen und miteinander verwandten Befehlen (z.B. Befehle aus dem Datei-Menü etc.), bietet sich eine solche Implementierung durchaus an.

Mit Version 9.0 sind die Microsoft Foundation Classes (MFC) um das MFC Feature Pack erweitert worden. Einen Überblick über alle Klassen geben die als XPS und PDF (Download) vorliegenden MFC Hierarchy Charts:

  • MFC Hierarchy Chart Part 1 of 3: Classes Derived From CObject (pdf, xps).
  • MFC Hierarchy Chart Part 2 of 3: Classes That Derive From CCmdTarget Or CWnd (pdf, xps).
  • MFC Hierarchy Chart Part 3 of 3: Classes Not Derived From CObject (pdf, xps).