VS-Tipps&Tricks: Einfache Debug-Ausgabe mit TRACE auch in der Release Version

Wer wollte nicht schon immer mal gerne TRACE (Debug)-Ausgaben in seinem Release Programm haben ohne dafür überall OutputDebugString reinschreiben zu müssen.

Die nachfolgene kleine Klasse macht es möglich, den gewohnten Syntax des MFC TRACE Makros zu verwenden und direkt auf die Debugausgabe umzuleiten:

//    CTraceToOutputDebugString
//        Is a nice replacment class for TRACE
//        Easy to use with:
//            #undef TRACE
//            #define TRACE    CTraceToOutputDebugString()

class CTraceToOutputDebugString
{
public:
    // Non Unicode output helper
    void operator()(PCSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

    // Unicode output helper
    void operator()(PCWSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

private:
    // Non Unicode output helper
    void TraceV(PCSTR pszFormat, va_list args)
    {
        // Format the output buffer
        char szBuffer[1024];
        _vsnprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringA(szBuffer);
    }

    // Unicode output helper
    void TraceV(PCWSTR pszFormat, va_list args)
    {
        wchar_t szBuffer[1024];
        _vsnwprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringW(szBuffer);
    }
};

Durch den obenstehenden Code kann man auch in einer Release Version Trace Ausgaben erzeugen und z.B. mit DebugView.exe (Sysinternals) sichtbar machen, ohne evtl. weitere Anpassungen vornehmen zu müssen:

// Activate my special tracer
#undef TRACE
#define TRACE    CTraceToOutputDebugString()

void Foo()
{
     // Sometime usefull to see the output in a release version too
     TRACE(__FUNCTION__ " called at %d\n", GetTickCount());
}

CMapStringTo… HashKey Implementierung in VC-2003/5/8 ist auch fragwürdig

 Hash-Algorithmen haben es mir irgendwie angetan. 1984 hatte ich das erste mal mit einem miesen Hashverfahren zu tun, das einfach versagte wenn es nur um Ziffern ging. Das ganze betraf das Betriebssystem OASIS (später TheOS) und dessen Index Dateiorganisation. Ich entwickelte hierfür einen Patch in Assembler, der damals exakt in den x-Bytes der Kernels in Z80 Assembler passen musste.
Das Problem bei Strings die nur aus Ziffern bestehen ist das die Veränderung, die immer nur die unteren 4 Bits betrifft und die höheren 4 Bits immer konstant sind mit 3 belegt sind.

Als ich gelesen habe,  dass in VS-2010 die CMap Implementierung nun auch für Strings den selben Algorithmus aus der STL erhalten, habe ich mir auch den mal genauer angesehen. (siehe Beitrag Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung)
Der sieht so aus:

inline UINT CMapStringToString::HashKey(LPCTSTR key) const
{
  UINT nHash = 0;
  if (key == NULL)
  {
    AfxThrowInvalidArgException();
  }
 
  while (*key)
    nHash = (nHash<<5) + nHash + *key++;
  return nHash;
}

Und auch in diesem Fall muss ich sagen, dass der Algorithmus nur scheinbar intelligent ist. Wenn man sich aber die Abstände die hier erzeugt werden etwas genauer anschaut kommt man schnell auf eine Wertekombination, in der der Algorithmus versagt.
Wie sehr er versagt zeigt das folgende Beispiel, in dem ich einfach einen String verwende der aus 5 Ziffern besteht und immer den Abstand 11 verwendet.

CMapStringToPtr myMap;
for (int i=0; i<1000; i+=11)
{
  CString strKey;
  strKey.Format(_T("%05d"),i);
  myMap[strKey] = NULL;
}

Das erschreckende Ergebnis der Verteilung ist bei einem Unicode wie auch MBCS Projekt:

TMyMap<class CMapStringToPtr>::DumpMap
Bucket  0 =  0
Bucket  1 =  0
Bucket  2 =  0
Bucket  3 =  0
Bucket  4 =  0
Bucket  5 =  0
Bucket  6 =  0
Bucket  7 =  0
Bucket  8 = 36
Bucket  9 =  0
Bucket 10 =  0
Bucket 11 =  0
Bucket 12 =  0
Bucket 13 =  0
Bucket 14 = 55
Bucket 15 =  0
Bucket 16 =  0

Wie gut, dass auch die String Variante von HashKey überarbeitet wird.

BTW: Da die internen Strukturen protected sind muss man zu einem kleinen Trick greifen um die Verteilung ausgeben zu können. Ich habe dazu ein template verwendet, dass für alle MFC Maps funktioniert.

template<class TBase> class TMyMap : public TBase
{
public:
  void DumpMap()
  {
    TRACE(__FUNCTION__ "\n");
    if (m_pHashTable)
    {
      for (size_t i=0; i<m_nHashTableSize; ++i)
      {
        int iCount = 0;
        for (CAssoc *p = m_pHashTable[i]; p; p=p->pNext)
          ++iCount;
        TRACE("Bucket %2d = %4d\n", i, iCount);
      }
    }
  }
};

Merke: Wer Hashes verwendet sollte sich über sein Hashverfahren wirklich gedanken machen.

BTW: Sollte es den Leser meines Blogs langsam langweilen weil ich mich permanent mit Hash-Algorithmen aufhalte, der sei getröstet: Dies war vorläufig mein letzter Beitrag zu CMap… :mrgreen:

Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung

Ich hatte die schlechte Hashkey Implementierung ja bereits in VC-2005 als Bug gemeldet (siehe Die HashKey Implementierung in der MFC in VC-2005 und VC-2008). In VC-2008 geschah dies bzgl. wieder nichts. Aber was lange währt wird manchmal gut 😉

Die neue Implementierung wird aus der aktuellen STL übernommen. Diese Implementierung sorgt für Integer und Pointer für eine sehr gute zufällige Verteilung. Und weitere gute Nachricht dazu, auch die Implementierung für Strings wird aus der STL übernommen.

Zitat:

Hello Martin,

Thanks for the report. This issue has been fixed in MFC for Visual Studio 2010. MFC now uses the STL hash functions directly when possible, or uses a new algorithm copied from STL (in the case of strings).

Pat Brenner
Visual C++ Libraries Development
Von Microsoft am 20.07.2009 um 12:15 bereitgestellt

Siehe https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860

Die HashKey Implementierung in der MFC in VC-2005 und VC-2008

Die Maps in der MFC werden ja auch zum Teil gerne verwendet. Auch wenn viele Programmierer eher auf die std::map bzw. std::hash_map verwenden.

Zwei interne Dinge sind jedoch vielen Entwicklern nicht klar.

  1. Die Standard-Maps der MFC werden mit 17 Hash-Buckets erzeugt. Man sollte sich also über die Anzahl der Elemente klar werden, die gespeichert werden sollen.
    Bei großen Datenmengen sollte man evtl. darüber nachdenken, die Anzahl der Buckets zu erhöhen.
  2. Ist die Hash Funktion zu beachten, die hier verwendet wird. Denn deren Effektivität entscheidet ja, wie die Einträge auf die Buckets verteilt werden.

Zu diesem zweiten Punkt macht man eine erstaunliche Entdeckung, wenn man sich ansieht, was Microsoft für eine Funktion vorgesehen hat um den Hask-Key zu erzeugen. Diese Funktion ist in der MFC für primitive Werte vordefiniert, als primitive Werte sehe ich alle numerischen Werte, Zeiger und Handle an.
Man findet eine Implementierung als Template-Funktion in afxtempl.h:

template<class ARG_KEY>
AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)
{
 // default identity hash - works for most primitive values
 return (DWORD)(((DWORD_PTR)key)>>4);
}

Das erstaunliche ist für mich, dass hier die untersten 4 Bits einfach abgeschnitten werden. Für mich hätte für Handles, Zeiger und Integer Werte einfach ein cast gelangt.

Nehmen wir jetzt mal an, wir haben eine einfache Datenmenge, die die Integer 1-100 auf eine Struktur abbilden. Man wird die erstaunliche Entdeckung machen, dass alle Werte nur in den Buckets 0-6 abgespeichert werden. Die Buckets 7 bis 16 werden nicht verwendet.

Der Nachteil ist offensichtlich. Man nutzt das Datenrauschen auf den untersten 4 Bits nicht.
Das macht sich sogar bei Fensterhandles bemerkbar. Bekanntlich sind das immer gerade Werte, aber auch auf den Bits 1-3 finden wir hier Informationen. Auch hier werden nützliche „zufällige“ Informationen nicht genutzt. Dadurch werden mehr Kollisionen in Kauf genommen als notwendig wären.

Ich möchte nicht unerwähnt lassen, dass die MFC Maps wirklich eine Berechtigung haben, weil sie grundsätzlich einen Pool-Allocator verwenden. Häufige Allokationen und Löschungen werden weitaus schneller behandelt, als durch die std::map, oder std::hash_map mit normalen Allocator.

Ich habe diesen Bug (oder miese Verhalten) bereits in der Beta für VC-2008 gemeldet. Es wurde aber nicht mehr geändert. Nun habe ich es an die Produktgruppe erneut  für VC-10 eingereicht.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100772
Nachtrag 22.06.2009: Da der Bug von Microsoft nicht neu geöffnet wird habe ich einen neuen für VS-2010 Beta 1 angelegt.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860
Wer Lust hat, der kann ja abstimmen.

BTW: Die HaskKey Funktion in afxtempl.h ist für andere Maps (z.B. CMapPtrToPtr) direkt in der Klasse definiert. Dieses Template wird nur vom CMap-Template verwendet.

Tipps & Tricks: Wie man einen permanenten ASSERT direkt mit einem Kommentar versieht

Ich verwende gerne ASSERT’s in meinem Code.
Sie sind ein wirksames Mittel Zustände abzufragen und bereist in der Testphase unzulässige Konstellationen oder Funktionsaufrufe zu entdecken.

Nun gibt es ja auch if, else oder switch Blöcke an denen das Programm normalerweise nicht vorbei kommen sollte. So eine Stelle versieht man dann schon mal mit einem

_ASSERT( FALSE );
// oder wer die MFC benutzt eben ASSERT,
// obwohl dies auch nur ein Synonym für den CRT _ASSERT makro ist
ASSERT(FALSE);

Jetzt müsste man noch einen Kommentar davor setzen, damit klar wird was hier schief läuft. Man kann das Ganze aber auch einfach kombinieren und noch einen Zusatznutzen erreichen indem man den etwas unbekannteren Makro _ASSERTE verwendet:

_ASSERTE( !"MyFuncFooHandler: This is not allowed in my special Foo Handler" );

Die Negation macht aus dem Zeiger auf den konstanten String FALSE, und damit schlägt der ASSERT an.
Wenn man jetzt wie hier gezeigt noch den _ASSERTE Makro verwendet, dann wird diese Expression, also der Text, sofort mit anzeigt. Man sieht dann sofort was das Problem ist sobald der ASSERT Dialog angezeigt wird.

AfxOleInit versus CoInitializeEx in MFC Programmen

Immer wieder mal, taucht MFC Code auf und es wird CoInitializeEx verwendet.
Nicht unbedingt ein Fehler, aber ich denke man sollte auch hier die MFC-Art-und-Weise verwenden: AfxOleInit. Und man sollte sich klar sein, welche Apartment Modes man verwenden darf!

AfxOleInit selbst ist ein per Thread Wrapper für ::OleInitialize(NULL);.
Zudem sorgt AfxOleInit dafür, dass bei Beenden des Threads automatisch AfxOleTerm aufgerufen wird.

::OleInitialize(NULL) wiederum ist ein Wrapper für den Aufruf von CoInitializeEx im STA Kontext:
Siehe MSDN http://msdn.microsoft.com/en-us/library/ms690134(VS.85).aspx 

OleInitialize calls CoInitializeEx internally to initialize the COM library on the current apartment. Because OLE operations are not thread-safe, OleInitialize specifies the concurrency model as single-thread apartment.

Die MFC benutzt und verlangt genau diesen STA Modus. Sofern man also nicht wirklich weiß was man mit CoInitializeEx macht, sollte man es in der MFC vermeiden. Zudem man sich eben auch nicht mehr um den Cleanup kümmern muss, da die MFC dies selber macht.

Siehe auch MSDN Doku zu AfxOleInit
http://msdn.microsoft.com/de-de/library/e91aseaz(VS.80).aspx

MFC applications must be initialized as single threaded apartment (STA). If you call CoInitializeEx in your InitInstance override, specify COINIT_APARTMENTTHREADED (rather than COINIT_MULTITHREADED). For more information, see PRB: MFC Application Stops Responding When You Initialize the Application as a Multithreaded Apartment (828643) at http://support.microsoft.com/default.aspx?scid=kb;en-us;828643.

Aber auch hier sei erwähnt, dass man sicherlich auch andere Apartment Modes verwenden kann. Allerdings muss man dann wirklich wissen was man macht und darf sich nicht auf die MFC Funktionen stützen. Für solche Fälle verwende ich hier komplett ATL in meinen MFC Programmen.
Man sollte aber in diesen Fällen wirklich wissen was man macht 😉

BTW: Automatisch aufgeräumt bei einem gestarteten Thread wird nur dann, wenn auch AfxBeginThread verwendet wird. Siehe auch: AfxBeginThread versus _beginthreadex

VS Tipps & Tricks: Der unbekannte CString Konstruktor…

CString nimmt natürlich auch in einem seiner Konstruktoren einen LPCTSTR . Dieser Konstruktor hat aber noch eine versteckt eingebaute Funktion. Man kann CString auch mit MAKEINTRESOURCE(id) verwendet. Aus der Dokumentation geht dies nicht eindeutig hervor, aber zumindest findet man es am Rande erwähnt.
Ist also das High-WORD des LPCTSTR gleich NULL dann wird das LOWORD verwendet um die entsprechende String-Ressource zu laden.
Ist die Ressource nicht vorhanden wird in der Debugausgabe ein entsprechender Trace erzeugt.

Benötigt man also temporär eine String Variable so kann man direkt in einem Statement die entsprechende String-Resource laden:

CString strMyText(MAKEINTRESOURCE(IDS_MY_STRING));
strMyText += strSomeOther;
strMyText += CString(MAKEINTRESOURCE(IDS_MY_TEXT_TO_APPEND));

WM_FLOATSTATUS der nette Helfer für eigene Popup Fenster

Manchmal entwirft man eigene Popup Fenster entwirft, die auch kurzfristig den Fokus bekommen sollen, wie z.B. ein Dropdown Fenster ähnlich einer Combobox. In diesem Fall hat man das Problem, dass das Main Fenster (Parent) als nicht mehr aktiv angezeigt wird, d.h. die Rahmenfarbe ändert sich, obwohl das eigentlich hier unerwünscht ist.

Eine Lösung wäre es WM_NCACTIVATE neu an das Parent zu versenden wenn WM_ACTIVATE eintrifft.
Die MFC hat aber hierfür eine weitaus bessere Technik, die sofort vermeidet, dass das Parent inaktiv angezeigt wird ❗
Man muss dazu nur die interne MFC Fensternachricht WM_FLOATSTATUS behandeln, die auch in der TN024 beschrieben, ist behandeln.

Etwas in dieser Art

...
   ON_MESSAGE(WM_FLOATSTATUS,OnFloatStatus)
...

LRESULT CMyDropDownWnd::OnFloatStatus(WPARAM wParam, LPARAM)
{
    return (wParam & FS_SYNCACTIVE) ? TRUE : FALSE;
}

und man verhindert dadurch, das das Parent inaktiv wird, weil man signalisiert, dass das neue Popup-Fenster synchron aktiv ist (FS_SYNCACTIVE). Die gleiche Technik wird auch innerhalb der Toolbars verwendet, die auch alle zusammen mit dem Parentframe alle aktiv oder alle inaktiv erscheinen.

Der etwas bessere CMemDC

CMemDC aus dem Artikel Flicker free drawing with MFC ist wohl der Klassiker für Doublebuffering und ist wohl jedem MFC Entwickler bekannt.

Er macht was er soll und ich habe ihn selbst über Jahre hinweg unverändert verwendet. Solange man einfache Sachen macht und ausschließlich den MM_TEXT Mappingmode verwendet ist alles OK.
Hat man sich aber was spezielles – zoomfähiges – zusammengebaut mit MM_ANISOTROPIC dann funktioniert das Ganze nicht mehr, weil Keith an den Window Origin Koordinaten dreht und nicht am Viewport.

Meine abgewandelte Variante trägt einer entsprechende Verwendung auch für andere Mappingmodes Rechnung. Es müsste IMHO für jeden funktionieren. Intensiv getestet habe ich es mit MM_ANISOTROPIC.

Will/muss man auch an den Viewport Koordinaten drehen, dann sollten die alten Viewport Koordinaten gelesen werden und dann entsprechend durch ein Offset geändert werden.
Alles anderen Werte wie die Extents können beliebig verändert werden.
Bei der Verwendung sollte MM_TEXT eingeschaltet sein (entsprechende ASSERTs habe ich ergänzt). MM_TEXT ist normalerweise gesetzt, wenn CMemDC direkt nach Instanzierung des CPaintDC zum Einsatz kommt. Der Destruktor setzt dann den Mappingmode auf MM_TEXT zurück.

Dieser neue, modifizierte CMemDC kann einfach die bisherige Implementierung ersetzen.

Have fun & Happy coding 🙂

class CMemDC : public CDC
{
public:
  // constructor sets up the memory DC
  CMemDC(CDC* pDC)
    : CDC()
    , m_pDC(pDC)
    , m_pOldBitmap(NULL)
    , m_bMemDC(!m_pDC->IsPrinting())
  {
    ASSERT(pDC != NULL);

    if (m_bMemDC)
    {
      // Create a Memory DC.
      // At this moment we should have mapping mode text
      ASSERT(m_pDC->GetMapMode()==MM_TEXT);
      ASSERT(m_pDC->GetWindowOrg()==CPoint(0,0));
      ASSERT(m_pDC->GetViewportOrg()==CPoint(0,0));

      // Get the clip box and create bitmap for this size
      m_pDC->GetClipBox(&m_rect);
      CreateCompatibleDC(m_pDC);
      m_bitmap.CreateCompatibleBitmap(m_pDC, 
              m_rect.Width(), m_rect.Height());
      m_pOldBitmap = SelectObject(&m_bitmap);

      // Adjust the view port so that we hit the clip rect.
      SetViewportOrg(-m_rect.left, -m_rect.top);
      IntersectClipRect(m_rect);

      // Fill background in case the user has overridden
      // WM_ERASEBKGND.  We end up with garbage otherwise.
      FillSolidRect(m_rect, m_pDC->GetBkColor());
    }
    else
    {
      // Make a copy of the relevant parts of the current DC for printing
      m_bPrinting = m_pDC->m_bPrinting;
      m_hDC     = m_pDC->m_hDC;
      m_hAttribDC = m_pDC->m_hAttribDC;
    }
  }

  // Destructor copies the contents of the mem DC to the original DC
  ~CMemDC()
  {
    if (m_bMemDC)
    {
      // The mapping mode might effect the BitBlt. So we need to return to
      // MM_TEXT. This makes m_rect.left and m_rect.top again the
      // coordinates of our clipped rectangle. And to make sure that the
      // coordinates are really used we just clear the ViewportOrg and the
      // WindowOrg
      SetMapMode(MM_TEXT);
      SetViewportOrg(0,0);
      SetWindowOrg(0,0);

      // Copy the off screen bitmap onto the screen.
      // For this we just make sure that we really target the rectangle of
      // our temporal bitmap.
      m_pDC->BitBlt(m_rect.left, m_rect.top, 
              m_rect.Width(), m_rect.Height(),
              this, 0, 0, SRCCOPY);

      //Swap back the original bitmap.
      SelectObject(m_pOldBitmap);
    }
    else
    {
      // All we need to do is replace the DC with an illegal value,
      // this keeps us from accidentally deleting the handles associated
      // with the CDC that was passed to the constructor.
      m_hDC = m_hAttribDC = NULL;
    }
  }

  // Allow usage as a pointer
  CMemDC* operator->()  {   return this;  }

  // Allow usage as a pointer
  operator CMemDC*()  {   return this;  }

private:
  CBitmap  m_bitmap;    // Offscreen bitmap
  CBitmap* m_pOldBitmap;  // bitmap originally found in CMemDC
  CDC*   m_pDC;     // Saves CDC passed in constructor
  CRect  m_rect;    // Rectangle of drawing area.
  bool   m_bMemDC;    // true if CDC really is a Memory DC.
};

Aus zwei mach eins: Wie man zwei Cursor kombinieren kann

Wer komplexere UIs baut, der kommt um Drag&Drop oder andere Extrafunktionen mit der Maus nicht herum. Um den Benutzer gut zu unterstützen verwendet man natürlich auch spezielle Cursor.
Auffällig ist aber oft genug der Unterschied zwischen den schönen Standardzeigern im 3D-Stil und den oft flachen Zeigern, die gerade mal zusätzlich ein Plus oder ein Stoppzeichen tragen. Oder ganz und gar der Unterschied , wenn man eigene Cursorstile verwendet, wie z.B. Dirigent oder Hände.

Ich habe mich bei meinem aktuellen Projekt gefragt, warum man eigentlich nicht den aktuellen Standard-Cursor verwendet und diesen zusätzlich mit dem entsprechenden Symbol versieht (Pluszeichen, Stopp, Dragframe etc). Das würde besser aussehen und Arbeit sparen, weil man sich nur noch Gedanken um die Extrasymbolik machen muss und der Benutzer behält seinen eingestellten normalen Cursor.

Schaut man sich den Explorer unter XP+Vista an, dann macht man die Entdeckung, dass der es genauso macht. Der aktuelle Cursor wird beim Drag&Drop Vorgang um ein Stopp- oder Pluszeichen ergänzt.

Warum es also nicht selber auch so machen ❓

Meine Suche im Internet zu diesem Thema: „Wie kombiniert man zwei Cursor zu einem?“, war nicht von Erfolg gekrönt. Beispielcode dazu habe ich nicht gefunden. Aber auch wirklich gar nichts.
Also selbst Hand anlegen.

Bei meinem ersten Versuch wollte ich die Cursor-Bitmaps direkt manipulieren. Bei meiner Untersuchung und dem ersten Democode wurde klar, dass Windows gar keinen Unterschied mehr zwischen Icons und Cursor macht. Erstaunlich ❗ Ob Icon oder Cursor spielt eigentlich keine Rolle. HCURSOR und HICON sind identische Typedefs. Also egal ob toller Farbcursor oder monochromer Cursor, alles gleich.
Mit dieser Entdeckung nahm ich schnell Abstand von BitBlt und Konsorten und verwendete eine Imagelist, die genau diese Funktion des Overlays von Symbolen bereits unterstützt und zudem auch noch perfekt mit Icons umgehen kann.

Herausgekommen ist der folgende Code, der es erlaubt zwei Icons, oder Cursor zu überlagern und ein neues Icon oder einen neuen Cursor zu erzeugen.

Man muss sich aso nur mit LoadCursor, den aktuellen Cursor laden und kann diesen damit ganz einfach modifzieren. Nicht vergessen: Das erzeugte Icon/Cursor muss allerdings mit DestroyIcon auch wieder entsorgt werden. Das Ganze habe ich der Einfachheit halber mit MFC Funktionen geschrieben, aber das Ganze in pure WinAPI zu transformieren dürfte nicht schwer sein. 😉

 Hier nun der Code. Have fun!

HICON CombineIcons(HICON hIcon1, HICON hIcon2)
{
 // Remember that HCURSOR and HICON are identical!
 // hIcon1 is overlayed by hIcon2.
 // hIcon2 isn't adjusted in size or position.
 // It just overlays hIcon1
 // Get bitmaps of icon 1
 ICONINFO iconInfo;
 ::ZeroMemory(&iconInfo,sizeof(iconInfo));
 if (!GetIconInfo(hIcon1,&iconInfo))
  return NULL;

 // Attach the bitmaps to get them automatically freed
 // upon error.
 CBitmap bitmap, mask;
 bitmap.Attach(iconInfo.hbmColor);
 mask.Attach(iconInfo.hbmMask);

 // Get size and width
 BITMAP bm;
 if (bitmap.m_hObject)
  bitmap.GetObject(sizeof(bm),&bm);
 else
  mask.GetObject(sizeof(bm),&bm);

 // Get the color depth from the icon and create an image list
 // Remember we need a
 UINT flags = 0;
 switch (bm.bmBitsPixel)
 {
 case 4:  flags = ILC_COLOR4;  break;
 case 8:  flags = ILC_COLOR8;  break;
 case 16: flags = ILC_COLOR16; break;
 case 24: flags = ILC_COLOR24; break;
 case 32: flags = ILC_COLOR32; break;
 default: flags = ILC_COLOR4;  break;  
 }
 CImageList il;
 // be ware that the monochrom cursor bitmap is twice the height
 if (!il.Create(bm.bmWidth,
      bm.bmHeight/(iconInfo.hbmColor!=NULL ? 1 : 2),
      ILC_MASK|flags,2,2))
  return NULL;

 // Load the both icons into the image list
 il.Add(hIcon1);
 il.Add(hIcon2);

 // Define the second icon as an overlay image
 il.SetOverlayImage(1,1);

 // Get a new icon with icon 2 overlayed
 HICON hCombined = ImageList_GetIcon(il.m_hImageList,0,
              ILD_NORMAL|INDEXTOOVERLAYMASK(1));
 if (!hCombined)
  return NULL;

 // Need the icon infos for this new icon
 ICONINFO iconInfoCombined;
 ::ZeroMemory(&iconInfoCombined,sizeof(iconInfo));
 if (!GetIconInfo(hCombined,&iconInfoCombined))
  return NULL;

 // Destroy the combined icon, we just have the bitmap and the mask
 ::DestroyIcon(hCombined);

 // Get the bitmaps into objects to get them automatically freed
 CBitmap bitmapCombined, maskCombined;
 bitmapCombined.Attach(iconInfo.hbmColor);
 maskCombined.Attach(iconInfo.hbmMask);

 // Get the hotspotinto and cursor data from
 // the ICONINFO of hCursor1
 iconInfoCombined.fIcon = iconInfo.fIcon;
 iconInfoCombined.xHotspot = iconInfo.xHotspot;
 iconInfoCombined.yHotspot = iconInfo.yHotspot;

 // OK we have can create a new Cursor out of the target
 // Don't forget to use DestroyIcon for the new Cursor/Icon
 return ::CreateIconIndirect(&iconInfoCombined);
}