München 15.11.2007: Visual C++ 2008 und danach, Ask the Experts

Visual C++ 2008 und danach, Ask the Experts

Das ganze findet am 15.11.07 im Hilton Park Hotel München statt. Die Veranstaltung beginnt um 18:00 Uhr. Die Anschrift vom Veranstaltungsort ist
Hilton Park Hotel
Am Tucherpark 7
80538 München

Eine Veranstaltung zu der ich gerne gehen würde. Aber ich habe leider keine Zeit und die Strecke ist zu weit.
Die Redner haben was zu sagen, denn Sie kommen direkt von der TechEd Barcelona und dort haben sie die Decke über einiges neues in der Zukunft von nativem C++ gelüftet.

Ich kann nur jedem raten dieses Event zu besuchen, sofern er in der Nähe ist oder es erreichen kann. Die Alternative ist natürlich die TechED 2007 in Barcelona…

Ich werde ab dem 07.11.2007 auch dazu bloggen, denn dann bindet mich auch kein „Schweigegelübde“ mehr. 😉

Quote aus der Ankündigung:
Es wird Brezeln und Getränke geben und das Beste an der Sache ist:. Es ist absolut kostenfrei!

Registrieren kann man sich per Email. In die Registrierung bitte unbedingt Anschrift, Telefonnummer und primäre Email Adresse eintragen.

Wichtig: Der Veranstaltungsort ist auf 50 Plätze limitiert. First come, first serve!

Noch gilt: Noch Plätze frei! Visual C++ 2008 and Beyond

Mal ganz schnell sich selbst reingelegt mit Excel und CRecordset::optimizeBulkAdd

Um eine größere Datenmenge in Excel über eine ODBC Verbindung zu erzeugen habe ich einen entsprechenden Recordset geöffnet und Daten massenweise hineingepumpt. Die Tabelle (das Worksheet), das erzeugt wurde enthielt Spalten mit Text-, Numerischen-, und Zeitdaten. Einige der Daten sollten wurden mit NULL Werten erzeugt werden.

Entsprechend den Tipps in der Doku habe ich die zusätzliche Option CRecordset::optimizeBulkAdd zu CRecordset::appendOnly verwendet um das zu beschleunigen.

m_recordSet.Open(CRecordset::snapshot,
                NULL,
                CRecordset::appendOnly|CRecordset::optimizeBulkAdd);

Laut der Doku sollte das erste Statement entsprechend für alle weiteren Operationen, den Dirty-Status setzten. Fein (dachte ich), alle Daten des Recordsets werden gesetzt, entweder auf den entsprechenden Wert oder NULL.

Alle Tests waren „erstmal“ positiv. Die Daten wurden in guter Geschwindigkeit erzeugt.
Leider stellten Kunden dann fest (allerdings Wochen später), dass unter bestimmten Umständen ganze Spalten leer waren. Sobald in der ersten Zeile einmal in einer Spalte NULL ausgegeben wurde, dann wurde auch in allen folgenden Spalten kein Wert eingetragen. Das Ganze, obwohl die Daten korrekt gebunden und als dirty markiert wurden. An das Open Statement dachte ich nicht…

Nach langem Testen nahm ich schon an, einen Bug in dem ODBC Treiber gefunden zu haben. Stutzig wurde ich aber, dass alles prima klappte, wenn ich pure SQL INSERT-Statements mit ExecuteSQL  direkt auslöste, ohne den Recordset zu bemühen. Was nun?

Relativ ratlos wollte ich schon einen Supportfall bei Microsoft öffnen, bis ich den Code noch einmal Schritt für Schritt durchsah und wieder über das das Open Statement stolperte.

Bingo: Sobald ich dieses Flag entfernte arbeitete alles korrekt. Nach meinem Verständnis müsste auch ein NULL Wert ein Wert sei, der gültig ist, solange ich alle Spalten beim ersten INSERT/AddNew angebe. Ich nahm an, dass auch ein Einfügen eines NULL Wertes ein „Dirty“ für die entsprechende Spalte auslöst. Das ist aber nicht so.

Wird – zumindest beim ODBC Treiber für Excel – in dem ersten INSERT eines Datensatzes ein NULL Wert ausgegeben , dann werden auch keine Daten für alle nachfolgenden Zeilen eingefügt. Und das NULL eingefügt wird, geschieht ganz schnell, wenn z.B. ein leeres Textfeld (Länge 0) ausgegeben werden soll. Ob dies auch für Access oder andere Treiber gilt habe ich noch nicht ausprobiert.

Schnelle binäre Algorithmen

Manchmal tauchen in Foren richtig nette Sachen, die einen in Erstaunen setzen, wie schnell und einfach manches zu lösen ist. (siehe Thread)
Zwei interessante Fragen, die mich auch schon öfter beschäftigt haben sind hier mal erwähnt und als C++ Code wiedergegeben. Bisher habe ich diese immer mit einer entsprechenden Schleife und Shift-Operatoren gelöst.

1. Wie kann man schnell prüfen ob ein Integer eine 2er Potenz ist?

Der Algorithmus ist so verblüffend einfach, dass man sich fragen muss, warum man noch nicht von selbst drauf gekommen ist:

bool IsPowerOf2(int n) 
{ 
 return (n & (n - 1))==0; 
}

Tricky: Wenn mehr als zwei Bits gesetzt sind, bleibt bei der gegebenen Arithmetik mindestens das höchste Bit stehen.

❗ Nach diesem Algorithmus ist 0 übrigens auch eine Power of 2!

2. Wie findet man die nächste 2er Potenz einer gegebenen Zahl?

Diese Frage stellt sich für mich immer wieder, wenn ich binäre Suchen durchführe.
Auch hier gibt es eine statische Lösung, die ich hier mal wiedergebe und die sowohl für 32bit als auch für 64bit funktioniert und direkt in entsprechende Assembler Befehle umgesetzt wird.
Der Code vermeidet einen 32bit shift, da das Ergebnis in einem 32bit C++ Compiler undefiniert ist (Warning C4293).

int GetNextPowerOf2(int n) 
{ 
 // Code works for 32bit and 64bit 
 ­­­­-­-n;    
 n |= n >> 1; 
 n |= n >> 2; 
 n |= n >> 4; 
 n |= n >> 8; 
 n |= n >> 16; // Sufficient for 32bit 
 n |= n >> 16; // for a 64bit System we need a new 32bit shift (n>>32) 
 n |= n >> 16; // but this operation is undefined on a 32bit system 
               // so we use 2 16bit shifts. (C4293) 
 return n+1; 
}

Oft genug wird das höchste gesetzte Bit gesucht. In diesem Fall einfach den Startwert oder das Ergebnis um eine Bitposition shiften.

Tricky: Das höchste Bit wird nach der Subtraktion einfach auf alle niederen Bits übertragen.

❗ Auch hier ist das Verhalten bei 0 eigentümlich. Die nächste 2er Potenz auf 0 ist mit diesem Algorithmus 0!

Die entsprechenden Algorithmen sind hier auf dieser Seite beschrieben:
http://en.wikipedia.org/wiki/Power_of_two#Fast_algorithm_to_check_if_a_number_is_a_power_of_two

Die Cx2y Falle…

In einem Programm habe ich diese unscheinbare Schleife für eine spezielle Analyse im Debug-Mode eingebaut.

for (POSITION pos= m_aBind.GetHeadPosition(); pos; )

 S_BIND& b= m_aBind.GetNext(pos);
 TRACE(__FUNCTION__ “  %-32s %2d\n“,
  CT2A(b.sName.GetString()),
  b.GetStatus());
}

Sieht auf den ersten Blick OK aus.
Auf den zweiten Blick wundert einen evtl. die Nutzung von CT2A. Warum nehme ich nicht einfach den Zeiger von GetString wie er ist? Es ist ja ein PCTSTR! Begründung:

  1. Das Projekt ist ein Unicode Projekt. 
  2. CT2A verwende ich hier, weil die TRACE Funktion in dieser Schreibweise nur MBCS Strings verwendet. D.h. %s erwartet einen Zeiger auf einen MBCS String.
  3. Die _T Schreibweise für die Ausgabemaske ist hier nicht geeignet, weil ich gerne den Funktionsnamen auch mit ausgegeben hätte. Und __FUNCTION__ ist nun mal eine normale String-Konstante, und kein wchar_t-String.
  4. T2A verwende ich nicht, weil dies in einer Schleife tödlich ist, weil der Stack immer weiter anwächst.

OK! Soweit dazu, warum der Code aussieht wie er aussieht.

❗ Nur… es kommt nur Schrott dabei heraus!
Die Ausgabe zeigt brav, den Text, den ich haben möchte, nur die Zahlenwerte stimmen in keiner Weise mit den Rückgabewerten von GetStatus überein! Warum? ❓

Was ist passiert? Der Debugger gibt schnell Auskunft:
CT2W erzeugt ein Objekt vom Typ CW2AEX!!! Dieses Objekt ist aber größer als ein PCSTR Zeiger. Dieses Objekt umfasst bei meinen Projekteinstellungen 4 Bytes für einen Zeiger plus einen char-Array der Größe 128. TRACE hätte aber gerne einen 4-Byte Zeiger auf dem Stack und danach für die Ausgabe einen Integer.
Ausgegeben, werden also die 4 ersten Bytes des Puffers von CW2AEX und nicht die gewünschten numerischen Werte für GetStatus!

Das ist übrigens die selbe Falle in die man tritt, wenn man CString direkt ohne CString::GetString in printf/TRACE und Konsorten verwendet. Nur hat man hier das Glück, dass ein CString exakt 4 Bytes groß ist und genau aus einem Zeiger auf einen PTSTR besteht.

Wie macht man es richtig?
Genau… man führt den entsprechenden cast ein. Der cast benutzt nun den cast-Operator der Klasse CW2AEX, und der liefert uns den Zeiger den wir wollten.

for (POSITION pos= m_aBind.GetHeadPosition(); pos; )
{
 S_BIND& b= m_aBind.GetNext(pos);
 TRACE(__FUNCTION__ “ %-32s %2d\n“,
  static_cast<PCSTR>(CT2A(b.sName.GetString())),
  b.GetStatus());
}

Amerkungen:

  • Ich brauche wahrscheinlich nicht zu erwähnen, dass ähnliche Probleme mit CA2W, CA2T etc. auch auftauchen können.
  • An den meisten Stellen an denen, wir diese Konvertierungen, wie z.B. CT2A verwenden wird auch direkt ein PCTSTR erwartet, oder eben ein Zeiger auf den entsprechend konvertieren String. Dadurch wird implizit der Konvertierungs-Operator der Klasse aufgerufen und damit funktioniert ales wie es soll.
  • Auch wenn das Projekt ein MBCS Projekt gewesen wäre, hätte es die selben Probleme verursacht. Denn in diesem Fall wird aus CT2A eine Objekt der Klasse CA2AEX. Im Gegensatz dazu würde T2A einfach zu ener Noop.

Neues für VC++ 2008 Express

Irgendwie habe ich es gar nicht mitbekommen, aber VC++ Express wird mit der 2008er Version endlich ein „brauchbareres“ Produkt auch für Win32-Entwicklung.

Es wird endlich entsprechende Wizards geben für Win32 Projekte wie, DLLs, statische Libraries etc.
Nur scheinbar scheint der Resource-Editor nicht den Weg in die Express Version gefunden. Auch die MFC+ATL wird nicht Bestandteil der Express Versionen.

Da es keinen RC Kandidat geben wird, befürchte ich, dass der Resource-Editor leider aus dem Produkt draußen bleiben wird. Schade!

Nachzulesen hier:
http://blogs.msdn.com/somasegar/archive/2007/08/11/what-s-new-with-visual-studio-2008-express-editions.aspx
http://blogs.msdn.com/nikolad/archive/2007/07/26/what-s-new-in-visual-c-2008-express-for-beta-2-release-of-visual-studio-2008.aspx

Command Routing der MFC bei Kontext Menüs mit TrackPopupMenu

Kontext Menüs  gehören zum State of  the Art.

Es ist auch nicht weiter schwer ein Menü zu laden ünd per TrackPopupMenu auf den Bildschirm zu zaubern. Entsprechender Code sieht dann meistens so aus:

void CMyView::OnContextMenu(CWnd* pWnd, CPoint pt)
{
    // Get the menu
    CMenu popups;
    if (!popups.LoadMenu(_T("AGV-IDR_MACRO_EDITOR_CONTEXT")))
        AfxThrowResourceException();
    CMenu* pSubMenu= popups.GetSubMenu(0);
    ASSERT(pSubMenu);
    if (!pSubMenu)
        AfxThrowResourceException();
    pSubMenu->TrackPopupMenu(0,pt.x,pt.y,this);
}

Oft sind in dem Menü einfache IDs von Commands verwendet worden, die irgendwo im View, Dokument, Frame oder der Applikation implementiert sind. Der Programmierer wundert sich nun warum seine Menüpunkte nicht ausgegraut werden. Alle Menüpunkte für alle IDs sind aktiv. Und man wundert sich noch mehr. Manche Commands (außerhalb des Views) werden nicht ausgeführt.

Was läuft hier falsch?

❗ Die Antwort liegt einzig und alleine im Aufruf von TrackPopupMenu! Der letzte Parameter gibt an, an wen die entsprechenden Nachrichten (WM_COMMAND, WM_INITPOPUPMENU und andere) gesendet werden. Naheliegender Weise gibt der Entwickler hier das Fenster an, in dem er das Popup Menü anzeigen will. Hier liegt der Hase im Pfeffer.
Ein normales Fenster und ein View hat keine Verwendung (Handler) für WM_INITPOPUPMENU. Solch ein Handler wäre aber für das Command Routing der MFC notwendig um die Menu-Items ein- und auszuschalten.

Das CMainFrame hat solch einen Handler, und kann damit umgehen. Also einfach das CMainFrame als Zeiger übergeben und … es funktioniert wie erwartet. Warum auch nicht. Die IDs aus dem normalen Menü werden ja auch über genau den selben Weg des Command Routings behandelt.
Die entscheidende Codezeile sieht dann so aus:

pSubMenu->TrackPopupMenu(0,pt.x,pt.y,AfxGetMainWnd());

RTM für VS-2008 Orcas doch zum Ende des Jahres

Sorry für meinen etwas schnellen Blog-Eintrag:
http://blog.m-ri.de/index.php/2007/07/16/vs-2008orcas-kommt-jetzt-doch-erst-im-februar-2008/

Ich habe Launch und RTM (Release to Market) durcheinandergebracht. Sorry!

Wie es aussieht ist es doch schon Ende des Jahres soweit: 

http://blogs.msdn.com/dseven/archive/2007/07/10/windows-server-2008-visual-studio-2008-and-microsoft-sql-server-2008-joint-launch-announced.aspx

„While the launch events are scheduled to kick off on February 27, 2008, Visual Studio 2008 will be released before the end of the year.“

http://blogs.msdn.com/somasegar/archive/2007/07/13/it-all-begins-february-27th.aspx

„While we will be launching our products together in February, we are still aiming to release Visual Studio 2008 and .NET FX 3.5 by the end of this year based on your feedbackso far.“

VS-2008/Orcas kommt jetzt doch erst im Februar 2008

KORREKTUR zu diesem Artikel hier
http://blog.m-ri.de/index.php/2007/07/17/rtm-fuer-vs-2008-orcas-doch-zum-ende-des-jahres/
Ich habe Launch und RTM durcheinander gebracht. Sorry!

„VS-2008/Orcas kommt jetzt doch erst im Februar 2008“ weiterlesen

Erstaunlich: const BYTE * != const LPBYTE

Erstaunlich! Wenn man die folgende Funktion kompiliert erhält man keine Fehlermeldung: 

void foo(const LPBYTE data)
{
    *data = 0; 
}

Man könnte denken, dass diese Funktion identisch wäre zu: 

void foo(const BYTE * data)
{
    *data = 0;        // Compiler Error
}

Ist es aber nicht! Die Funktion

void foo(const LPBYTE data);

ist identisch zu

void foo(BYTE * const data);

und eben nicht zu

void foo(const BYTE * data);

Der typedef darf selbst kein Zeiger sein! Er muss ein Basis Typ sein, nur dann führt const X * zum gewünschten Effekt.
Das ist das eigentliche Problem. Das entspricht auch mit dem Sample im Kapitel 5.4 der Stroustrup „C++ Bibel“.

Siehe auch Diskussion hier:
http://groups.google.de/group/microsoft.public.de.vc/browse_thread/thread/2568531dcf1c1d7e/215ead7f070e23d0