VS Tipps & Tricks: Export/Import von Einstellungen zwischen VisualStudio 2005 und 2008

In einem meiner ersten Tipps habe ich das Thema Import und Export von Einstellungen schon behandelt schon behandelt: VS-Tipps & Tricks: Einstellungen für Visual Studio mehreren Arbeitsplätzen gleich halten.

Besonders wichtig ist das für mich immer mit den Tastatureinstellungen, die immer anpasse.

Was ich aber jetzt mit positivem Erstaunen feststellen konnte ist folgendes:
Man kann Einstellungen auch aus VS-2005 nach VS-2008 übertragen und umgekehrt ❗

Die XML Struktur des Exportformates ist nicht versioniert. Man kann direkt Einstellungen aus VS-2005 austauschen. Ideal wenn man Legacy Projekte noch behandeln muss und beide Systeme parallel fährt.

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.

DrawText unter Vista gegenüber XP um bis zu Faktor 50 langsamer!

❗ Ja Ihr habt ganz richtig gelesen und ich irre mich nicht ❗

Wir haben in einem unserer C++/MFC Programmen eine komplexe Anzeige von Reports, die alle unterschiedliche Zeilenhöhe haben und in einem speziellen Fenster angezeigt werden (eigene Entwicklung).
Nun stellten wir fest, dass das Rollen in diesem Programmteil auf Vista-Rechnern mit viel Nachlauf funktionierte und auch einige andere Operationen länger als gewohnt dauerten. Bei meinem Suchen, Profilen und Testen kam ich auf ein sehr lahmes Abarbeiten der Funktion DrawText  mit DT_CALCRECT.

Um das Problem zu isolieren schrieb ich ein kleines Testprogramm, das erstaunliches zu Tage brachte.
Hier die Ergebnisse von 4 verschiedenen Rechnern (Durchlauf von 100×100 DrawText Calls):

  • Pentium Quad-Core Q8200 mit Vista: 48.111 msec
  • Pentium Core2Duo T2310 mit Vista: 78.953 msec
  • Pentium Dual Core 3,2Ghz mit XP: 1.516 msec
  • Alter Pentium 3,0Ghz HT mit XP: 1.922 msec

😯 Der Code ist teilweise um den Faktor 50 langsamer auf Windows-Vista im Vergleich zu Windows-XP

Wer Lust hat es selbst zu testen, der findet hier das Beispielprogramm im Sourcecode (VS-2005):  TestDrawText.zip
Wer das Programm als Exe möchte findet es hier: TestDrawTextExe.zip

Beschreibung:
Dieses Beispielprogramm führt gemäß einem angegebenen Zähler je 100mal DrawText mit DT_CALCRECT für einen längeren mehrzeiligen Text aus. Hierbei werden immer zufällige Textlängen verwendet. Der Algorithmus liefert jedoch immer die gleiche Zufallssequenz, damit der Test auch Vergleichbar bleibt.
Der Zufallsmechanismus wurde nur eingebaut, um für für meinen Fall realitätsnahe Daten zu liefern.

Anmerkung:
Eine entsprechende Supportanfrage bei Microsoft läuft ❗
Was dabei herauskommt werde ich berichten…

Zuletzt angemerkt:
Windows 7 soll ja schneller sein als Windows Vista. Wer weiß? Ich habe es noch nicht getestet.
Vieleicht haben die nur diesen Bug gefixed und einen gigantischen Performanceschub erhalten :mrgreen: … SCNR…

Nachtrag (11.07.2009): 
Die Auiflösung zu diesem Problem findet sich hier in diesem Artikel 
http://blog.m-ri.de/index.php/2009/07/11/ausloesung-drawtext-unter-vista-gegenueber-xp-um-bis-zu-faktor-50-langsamer/

Mein Blog ist 2 Jahre alt geworden + Gesegnete Weihnachtszeit + Ein gutes neues Jahr

Im Mitte Dezember 2006 hatte ich mir endlich die Zeit genommen diesen Blog einzurichten. Ich habe mit einigen Providern experimentiert bin aber dann schnell zu einer eigenen WordPress Installation gekommen, die ich ja auch immer noch erfolgreich verwende.

Schnell sind diese 2 Jahre sind vergangen und insgesamt 267 Beiträge wurden geschrieben. Und immer noch steigen die Zugriffszahlen langsam, aber stetig an, wenn ich der WordPress Blog-Statistik glauben darf. Ich hatte nicht gedacht, so viele Leser zu erreichen. Aber diese Bestätigung ist auch gut, denn was nützt ein Blog ohne Leser 😉

Allen regelmäßigen Kommentatoren sei hier auch einmal Dank für die immer guten und konstruktiven Beiträge.

Ich wünsche allen eine gesegnete Weihnachtszeit und ein gutes neues Jahr!

PS: In meinem Sidebar habe ich jetzt auch eine zusätzliche Combobox eingebaut, die es erlaubt weiter zurück in die Vergangenheit zu gehen als nur die bisherigen 18 Monate.

Was ist falsch mit diesem Code? (1)

Ein Kollege hatte ein größeres Makro in VBScript geschrieben und ein interessantes Problem dabei entdeckt. Ich mache mal ein Ratespiel daraus 😉

Was ist faul mit diesem VBScript-Code?

On Error Resume Next
If MyFunction() Then
    MsgBox "MyFunction succedded"
Else
    MsgBox "MyFunction failed"
End If

MsgBox "... continue execution ..."

Function MyFunction
    MsgBox "Start MyFunction"
    On Error Goto 0
...
    MsgBox "End MyFunction"
    Test = true
End Function

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.
};

Externe und interne COM Zugriffe unterscheiden

Ich benutze als interne Makrosprache gerne VBScript. D.h. ich hoste VBScript.
Ich biete dann bestimmte COM-Objekte (IDisptach) an, die es dem Nutzer erlauben mein  Programm anzupassen oder intern zu steuern.

Nun macht es aber unter Umständen einen gravierenden Unterschied, ob ein COM-Objekt von einem programminternen Nutzer, oder von extern angesprochen wird. Intern möchte ich zum Beispiel mehr  oder andere Funktionen erlauben als durch einen externen Zugriff von einem anderen Programm oder Skript.

Wie kann man aber unterscheiden von wo ein COM Zugriff erfolgt ❓

Netterweise hat COM die Funktion CoGetCallContext, die mir genau diese Info liefert. Liefert CoGetCallContext S_OK und damit einen Interfacezeiger auf IServerSecurity, dann wurde der COM Zugriff von einem externen Prozess abgesetzt. Wenn CoGetCallObject E_NOINTERFACE zurück gibt haben wir einen internen Aufruf aus dem eigenen Prozess heraus.

bool IsExternalComCall()
{
 // get context
 CComPtr<IServerSecurity> spSecurity;
 HRESULT hr = ::CoGetCallContext(IID_IServerSecurity,
                      reinterpret_cast<void**>(&spSecurity));
 return SUCCEEDED(hr);
}

Herzlichen Dank an René König für diesen Ansatz, den er mir in microsoft.public.de.vc gab.

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);
}

MSVSMON: Visual Studio Remote Debugging Monitor will not be able to debug applications because…

Da schreibe ich einen netten Artikel über Remote Debugging und schon geht es doch nicht so einfach wie ich schreibe.

Manch ein Entwickler wird von der folgenden Nachricht begrüßt, wenn er MSVSMON.EXE startet:

Visual Studio Remote Debugging Monitor will not be able to debug applications because the ‚Network access: Sharing and security model for local accounts‘ local security policy option is set to ‚Guest only – local users authenticate as Guest‘. This can be configured with the ‚Local Security Settings‘ administration tool.
Do you want to continue?    (Use /noguestonlywarn to suppress this warning)

Nun auch keine Tragik. Dann kommen wir aber um einen Neustart nach Änderungen der Lokalen Richtlinien nicht herum. Aber man muss erst mal wissen was mit dieser Meldung gemeint ist, wenn man ein deutsches OS benutzt:

  1. Systemsteuerung starten.
  2. Leistung und Wartung -> Verwaltung -> Lokale Sicherheitsrichtlinie
  3. In den Lokalen Sichereinstellungen auf
    Lokale Richlinien -> Sicherheitsoptionen
  4. Dort dann Netzwerkzugriff: Modell für gemeinsame Nutzung und Sicherheitsmodell für lokale Knoten auswählen.
  5. Umstellen auf: Klassisch – lokale Benutzer authentifizieren sich als sie selbst
  6. Reboot

HTH
Siehe auch:
How to: Set Up Remote Debugging
How to debug on computers that are running Windows XP and that are in the same Workgroup in Visual Studio .NET or in Visual Studio 2005

VS Tipps & Tricks: Wie schließt man ein Tool Window?

Die ganzen netten Tool-Windows (Memory Window , Watch Window, Find Result, Document Outline, Output, Pending Changes, etc.) kann man sich mit einer netten Tastenkombination auf den Monitor holen. Aber wie schließt man sie nun wieder?

Indem man mit der Maus rechts oben auf das rote X klickt…  ❓ 😕

Nein ❗ Es geht auch (zum Glück) über die Tastatur. Mit Umschalt+Escape kann man jedes Tool Window sofort auf Hide setzen und schließen. Der entsprechende Makro Befehl lautet Window.CloseToolWindow.