Windows API


C++DebuggingMFCVista / Windows 7Windows APIMartin Richter - Sa 24 Aug 2013 12:08

Bei der Entwicklung von neuen Funktionen in einem Modul bekam ich irgendwann einen ASSERT. Diesen ASSERT hatte ich in einem Cache eingebaut. Der ASSERT sprang an, wenn bei Programmende der Cache nicht sauber aufgeräumt wurde. Also eigentlich in der Entwicklungsphase nichts ungewöhnliches. Irgendwo wurde also eine Funktion zum Aufräumen nicht aufgerufen.

Aber was mich in diesem Moment extrem stutzig machte, war, dass in der Debugausgabe meines VisualStudios keine Memory Leaks angezeigt wurden :!: Das machte irritierte mich nun schon sehr. Erster Verdacht. Evtl. hat ja jemand die Leakprüfung für den Cache ausgeschaltet (siehe AfxEnableMemoryTracking). Aber eine kurze Prüfung ergab, dass dem nicht so ist.

Also den Test noch mal ausgeführt. Diesmal wieder der ASSERT und zusätzlich der Dump. “Oha” dachte ich “Hier ist aber was richtig faul!”

Nachdem ich immer wieder andere Testszenarien verwendet habe, erschienen mal die Leaks und mal erschienen sie nicht. Und nach einiger Zeit kaum ich dahinter, dass immer wenn PlaySound in meiner Debug Version der Anwendung verwendet wurde, kein Leak-Check erfolgte. Wurde PlaySound nicht verwendet war alles gut und die Leaks wurden ausgegeben.

Jetzt ging es ans eingemachte. Schnell isolierte ich folgende DLLs die zusätzlich beim ersten PlaySound geladen wurden.

'xyz.exe': Loaded 'C:\Windows\SysWOW64\MMDevAPI.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\propsys.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\wdmaud.drv', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\ksuser.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\avrt.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\setupapi.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\cfgmgr32.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\devobj.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\AudioSes.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\msacm32.drv', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\msacm32.dll', Symbols loaded (source information stripped).
'xyz.exe': Loaded 'C:\Windows\SysWOW64\midimap.dll', Symbols loaded (source information stripped).

Und nach etwas weiter debuggen kam ich dahinter, dass der DllMain Code von setupapi.dll beim DETACH_PROCESS den Prozess sofort terminiert und nicht alle DLLs einen DETACH_PROCESS Aufruf erhalten. Aber der Code für die Leak-Detection der MFC liegt in der MFCx.DLL und wird durch eine statische Variable ausgelöst, die beim Entladen der MFCx.DLL dann zur Ausgabe der Speicherlecks führt. (Siehe PROCESS_LOCAL(_AFX_DEBUG_STATE, afxDebugState) und AfxDiagnosticInit).

Eine genauere Analyse des Stacktraces ergab folgendes Bild:

ntdll.dll!_ZwTerminateProcess@8()  + 0x5 bytes	
ntdll.dll!_RtlpWaitOnCriticalSection@8()  + 0x1d38f bytes	
ntdll.dll!_RtlEnterCriticalSection@4()  + 0x16a38 bytes	
setupapi.dll!_pSetupInitGlobalFlags@4()  + 0x1fc bytes	
setupapi.dll!_pSetupGetGlobalFlags@0()  + 0xe2 bytes	
setupapi.dll!_FlushWVTCache@0()  + 0x2f bytes	
setupapi.dll!_DestroyDeviceInfoSet@8()  + 0x3f bytes	
setupapi.dll!_SetupDiDestroyDeviceInfoList@4()  + 0x44 bytes	
MMDevAPI.dll!CDeviceEnumerator::FinalRelease()  + 0x78 bytes	
MMDevAPI.dll!ATL::CComObjectCached::~CComObjectCached()  + 0x3c bytes	
MMDevAPI.dll!ATL::CComObjectCached::`scalar deleting destructor'()  + 0xd bytes	
MMDevAPI.dll!ATL::CComObjectCached::Release()  + 0xf0c6 bytes	
MMDevAPI.dll!ATL::CComClassFactorySingleton::~CComClassFactorySingleton()  + 0x18 bytes	
MMDevAPI.dll!ATL::CComObjectNoLock<ATL::CComClassFactorySingleton >::`scalar deleting destructor'()  + 0x1a bytes	
MMDevAPI.dll!ATL::CComObjectNoLock<ATL::CComClassFactorySingleton >::Release()  + 0xe0e3 bytes	
MMDevAPI.dll!ATL::CAtlComModule::Term()  + 0x27 bytes	
MMDevAPI.dll!__CRT_INIT@12()  + 0x26e6 bytes	
MMDevAPI.dll!__CRT_INIT@12()  + 0x2588 bytes	
ntdll.dll!_LdrpCallInitRoutine@16()  + 0x14 bytes	
ntdll.dll!_LdrShutdownProcess@0()  + 0x141 bytes	
ntdll.dll!_RtlExitUserProcess@4()  + 0x74 bytes	
kernel32.dll!75eb7a0d() 	
msvcr100d.dll!__crtExitProcess(int status=0)  Line 709	C
msvcr100d.dll!doexit(int code=0, int quick=0, int retcaller=0)  Line 621 + 0x9 bytes	C
msvcr100d.dll!exit(int code=0)  Line 393 + 0xd bytes	C
xyz.exe!__tmainCRTStartup()  Line 568	C

Ursache war, dass MMDevAPI.DLL in seinem DllMain Code ausführt in der setupapi.dll, die aber bereits den DETACH_PROCESS abgearbeitet hat. Das grundsätzliche Problem wird hier in diesem Blog Artikel geschildert: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/23/421024.aspx
Mit eigenen Worten: Die MMDevAPI.dll ruft Funktionen in einer DLL auf, die bereits alle Ihren Speicher und Ressourcen freigegeben hat. Und wie es hier im Code so aussieht, versucht die SetupAPI.DLL wieder Ressourcen zu akquirieren, die eben bereits schon freigegeben wurden, weil eine DLL sie erneut benutzt.

Die Folge ein “Crash” in DllMain, die der Windows-Lader aber abfängt und sofort mit einer Terminierung des Prozesses ahndet. D.h. nun aber, dass nicht alle DLLs, die noch einen DllMain Aufruf mit DETACH_PROCESS erhalten müssten,  auch an die Reihe kommen.

Etwas weitere Recherche ergab, dass dieses Problem auch in den MSDN Foren bereits diskutiert wurde inkl. einer möglichen Lösung.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/8cb1847d-3218-4610-9cb8-6905bd255ff5/no-dllprocessdetach-after-calling-playsound-on-windows-7-64bit

Die Lösung ist erstaunlich einfach: Wenn vor der Benutzung von PlaySound explizit die SetupAPI.DLL geladen wird, dann verläuft der Rest der Deinitialisierung vollkommen normal. SetupAPI.DLL wird nicht entladen, weil die DLL durch den LoadLibrary Aufruf die DLL im Speicher sperrt. MMDevAPI.DLL kann erfolgreich selbst aufräumen und der Code läuft nicht mehr ins Nirvana.

Hier handelt sich offensichtlich um einen Bug in Windows 7 (Windows 8 konnte ich dies bzgl. nicht testen).
Dieser Bug kann natürlich auch empfindlichere Probleme mit sich bringen, wenn Ressourcen betroffen sind, die nicht automatisch mit Prozessende freigegeben werden und wenn diese Ressourcen ausschließlich in der DllMain bei einem DETACH_PROCESS behandelt werden.

C++ProgrammierenWindows APIMartin Richter - Fr 16 Nov 2012 16:21

Ja! Das könnte man denken. Strg+V sowie Umschalt+Einfg sind Shortcuts um etwas aus der Zwischenablage einzufügen.
Man könnte weiterhin davon ausgehen, dass Windows in einem Edit-Control beide gleich behandelt. D.h. benutzt der Anwender Strg+V oder Umschalt+Einfg oder das  im Edit-Control, dann wird immer der selbe Vorgang ausgelöst.

Schön wäre es ja :(

Sowohl bei Strg+V, wie auch bei Umschalt+Einfg und über das Kontextmenü wird WM_PASTE an das Edit-Control gesendet. Schön!

Aber hat das Edit-Control den Stil ES_READONLY – ist also als nur lesend definiert -, dann wird bei Eingabe von Strg+V die Nachricht WM_PASTE nicht gesendet. Auch das Kontextmenü blendet den Menüpunkt Einfügen brav aus. Auch das ist gut so und wie erwartet.

Aber was passiert bei Umschalt+Einfg?
Ja. Das unerwartete passiert und in diesem Fall wird doch WM_PASTE gesendet…

Herausgekommen ist das als Bug in unserer Software bei einer speziellen Edit-Control Klasse, die auch bestimmte andere Datenformate aus der Zwischenablage verstehen soll. Ein Kunde stellte letzten Endes fest, dass er über Umschalt+Einfg weiterhin auch in ein Readonly-Control Daten einfügen kann.

Ich war ziemlich überrascht als ich dieses “unlogische” Verhalten im Testfeld nachvollziehen konnte.
Wer hätte es gedacht? Ich nicht…

C++ProgrammierenVista / Windows 7VS 2012Windows APIMartin Richter - Di 16 Okt 2012 19:24

Es ist soweit. Für VS-2012 C++ ist seit einigen Tagen CTP Version verfügbar.
So ist es in einem der letzten Beiträge auf dem VC++ Team Blog zu lesen:

http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx

D.h. mit der entsprechenden Library und den neuen Projekteinstellungen kann man mit VS-2012 wieder C++ Programme erstellen, die auch auf Windows-XP laufen.

ATLC++MFCProgrammierenVS 2012Windows APIMartin Richter - Fr 04 Mai 2012 09:15

Gestern Abend hatten wir hier auf der ADC 2012 für C++ in Ohlstadt bei einem schönen Abendevent eine Q&A. Letztes Jahr fand diese Q&A auf einer Schiffahrt auf dem Chiemsee statt. Dieses Jahr war es ein Fußweg von ca. 20 Minuten vom Konferenzhotel, zu einem großen “Grillplatz”, dort standen Zelte, Fackeln Lagerfeuer und es wurde gut gegessen und wie immer viel “Networking” betrieben.

Wie auch letztes Jahr sollten detailierte Fragen auf diese Q&A zu späterer Stunde vertagt werden.

DIE FRAGE die viele Entwicklern brennend interessierte war:
Kann man mit VS-11 Programme für Windows XP entwickeln oder nicht?

Rede und Antwort stand in diesem Fall Steve Teixeira, als Director of Program Management. Also in diesem Fall jemand, der wirklich etwas sagen und auch mit zu entscheiden hat.

Ich gebe seine Antwort von Steve, auf diese Frage zusammengefasst wie folgt wieder:

  1. Zu dem Zeitpunkt als die Entscheidung für das Fallenlassen vom XP-Support gefällt wurde erschien dies als richtig.
    Jetzt muss man allerdings eingestehen, dass diese Entscheidung ein Fehler von Microsoft war.
  2. Die Benutzerzahlen wurden weiter als sinkend berechnet. Man vermutete, dass zum Zeitpunkt der Veröffentlichung von VS-11 noch maximal 20% XP-Nutzer vorhanden wären. Neue Umfragen gehen aber von einer Verbreitung von mindestens noch 46% Windows XP Installationen aus.
  3. Bei Microsoft wurde auch vermutet, dass es genügt den Entwicklern die neue VS-11 Oberfläche anzubieten aber das für das Compilieren das Toolset von VS-2010 genügen würde. Es wurde unterschätzt wie groß das Interesse an den neuen Compiler Funktionen in VS-11 mit C++11  ist. Was eben auch AMP und neue STL Funktionalität einschließt.
  4. Die Folge ist nun, dass Microsoft die Entscheidung für das Fallenlassen des Windows-XP Support neu überdenkt.
    Allerdings kann dies nicht mehr bis zum RTM geschafft werden.
    (Anmerkung von mir: Aktuell in der Beta wurde der gesamte Code, der die Windows-Vista/7 Funktionen isoliert entfernt und alle DLLs werden implizit geladen).

Möglich ist also, dass es ein Featurepack geben wird, dass nach dem RTM ausgeliefert wird, und in dem es dann doch einen XP Support in allen Bibliotheken und im Compiler gibt.

Die Antwort erschien mir ehrlich und geradeaus und war gewisslich keine Vertröstung ohne echten Hintergrund.
Ich weiß nicht wie groß wirklich die Chancen sind, aber diese Aussage deckt sich auch mit den “Gerüchten”, die ich um 5 Ecken gehört habe, und deckt sich auch mit der internen Diskussion, die mit den MVPs geführt wird.

Meine persönliche Schätzung ist eine 25 prozentige Chance, dass wir doch noch einen Windows-XP Support im VS-11 erhalten werden.

Lassen wir und überraschen. Aber es ist eine gute Nachricht :!:

PS: Ich schreibe dies direkt von der ADC für C++ 2012 in Ohlstadt.

Nachträge und Kommentare habe ich direkt in den Blog-Post übernommen, da diese manchmal übersehen werden:

Kommentar 1 vom 04.05.2012 von Steive Teixeira zur Klarstellung:

Hi Martin,
It was great to see you at ADC C++ this week! Just so that there is no confusion for the readers of your blog, the issue of XP support for C++ in Dev11 is one we’re taking very seriously, and we’re continuing to take customer feedback on Dev11 beta. However, we are not yet prepared to make an announcement on platform support for the RTM version of Dev11. We will be making an announcement on this in the coming weeks.
Thanks!
Steve

Kommentar 2 vom 04.05.2012 von Michael Kühlshammer 

Steve Teixeira schickte mir heute einen Link für einen Workaround und empfahl mir, dass möglichst viele Leute auf dem unten angegebenen Link einen Beitrag dazu schreiben sollen (dafür voten sollen), dass der VC11-Compiler auch Code für Windows XP erzeugt. Hier die Email-Antwort von Steve Teixeira:

“Thanks for your email. We continue to devote resources to and support MFC in Dev11. The Windows XP issue is still unsettled, and I appreciate your feedback on this. You me have seen that I posted some of my thoughts on XP support in the comment thread of this blog entry on the VC++ team blog: http://blogs.msdn.com/b/vcblog/archive/2012/04/18/10295093.aspx.

Thanks again,
Steve

C++MFCProgrammierenWindows APIMartin Richter - Mo 02 Apr 2012 21:46

Wenn man ein COM Objekt erzeugt und dieses im System über die ROT (Running Object Table) sichtbar, dann sollte man normalerweise Weak-Locks benutzen. Das kann man auch in der Doku zu RegisterActiveObject  nachlesen. Ansonsten wird es schwierig zu entscheinden, wann man seine Objekte zerstören kann.

Wenn aber nun eine Anwendung sichtbar gemacht wird, also das Objekt vom Benutzer übernommen wird, dann darf es ja nicht beendet werden, wenn der externe Erzeugende Prozess beendet wird und die letzte Referenz zu dem Objekt beendet wird.

Wie verhindert man das?

Die Lösung ist relativ simpel. Solange die Anwendung sichtbar ist, oder besser, wenn sie sichtbar wird ruft man einmalig CoLockObjectExternal auf! Dadurch wird ein weitere Lock auf das Objekt ausgeführt.
Aber Achtung :!: Hier wird keine Referenzzählung verwendet. Egal wie oft man CoLockObjectExternal aufruft, der Referenzzähler wird nur einmal erhöht.

Beendet der User das Programm entsperrt man das Objekt wieder. Sollten keine weiteren Objekte in der Anwendung benutzt werden, dann terminiert die Anwendung wenn man alles richtig gemacht hat ;)
Man ruft CoLockObjectExternal am Besten entweder auf, wenn die Anwendung sichtbar wird (WM_SHOWWINDOW) und erneut wenn WM_CLOSE aufgerufen wird. Die MFC macht alles fast automatisch richtig, bis eben auf die Aufrufe von CoLockObjectExternal, die man selbst im Code unterbringen muss, wie auch die Registrierung der Objekte in der ROT.
Ist noch eine externe Referenz vorhanden wird die Anwendung nicht terminiert, weil der interne Objektzähler der MFC dies verhindert (Code in CFrameWnd::OnClose). Ist kein externer Lock mehr vorhanden sperrt CoLockObjectExternal die Anwendung vom terminieren weil damit exakt eine Referenz aufrecht erhalten wird. Wird durch den Benutzer das Schließen der Anwednung angefordert wird dann im WM_CLOSE diese letzte Refrenz aufgelöst und die Anwednung kann terminieren auch wnen noch ein Eintrag in der ROT vorhanden ist. Dieser wird dann beim Beenden der Applikation auch entfernt.

C++ProgrammierenWindows APIMartin Richter - Mo 12 Mrz 2012 20:58

Es ist einfach ärgerlich, dass die Änderungen, die an der API mit den Common-Control 6.0 so mies dokumentiert sind.

Wir haben eine neu programmierte Standardklasse in ein selten benutzes (uraltes) Tool übernommen.
Auf einmal funktionierten Teile der UI nicht mehr richtig, die in unseren Produkten bisher fehlerfrei gearbeitet haben. Speziell hatten wir Probleme mit der Anzeige von Comboboxen.

Die Ursache war schnell gefunden:
GetComboBoxInfo liefert für hwndItem immer NULL, wenn die Applikation kein Common-Control 6.0 benutzt.
Es wäre alles viel schneller entdeckt worden, wären passende ASSERTs eingebaut worden an den Stellen, an denen man auch etwas bestimmtest erwartet. Eben hier, dass hWndItem nicht NULL ist. Aber vermutlich dachte der Programmierer: Wenn ich schon eine Information bekomme, dann ist diese Information bestimmt auch richtig und vollständig.
Pustekuchen … :( … aber wer will ihm das übel nehmen.

Das steht in der Doku zu COMBOBOXINFO natürlich nirgends drin (bzw. jetzt natürlich schon, weil ich eine entsprechende Community Addition gemacht habe).
http://msdn.microsoft.com/en-us/library/windows/desktop/bb775798(v=vs.85).aspx

C++ProgrammierenWindows APIMartin Richter - Mo 22 Aug 2011 19:20

Ein Stück Code in unserer Software behandelt Datenimport aus fremden Dateien. Nicht wenige alter DOS Programme erzeugen ja Daten im “OEM”-Zeichensatz (DBase etc.).
Wir haben in der Vergangenheit immer streng die T-Notation verwendet und eigentlich sollte dies einen vor vielen Überraschungen bewahren. Aber Pustekuchen.

Folgender Code sollte Daten aus einem OEM-Stream in einen CString umwandeln und eigentlich bin ich davon ausgegangen, dass das Ergebnis für Tabulatoren (‘\t’) und Zeilenschaltungen (“\r\n”) ergebnisneutral ist. D.h. in anderen Worten ich erwartete in dem String genauso viele Tabulatoren und Zeilenschaltungen vor wie nach der Konvertierung.
Die Umwandlung erfolgte mit solch einem Stück Code:

CString strText;
::OemToCharBuff(szText,CStrBuf(srText,len),len);

Sieht eigentlich harmlos aus. Allerdings musste ich feststellen, dass hier OemToCharBuffA und OemToCharBuffW ganz und gar nicht korrespondierende Ergebnisse liefern.
In der Unicode Version, also in der Version die OemToCharBuffW verwendet, wurden Tabulatoren zu 0x25cb L’○’ wchar_t  und Zeilenschaltungen zu 0x266a L’♪’ wchar_t und 0x25d9 L’◙’ wchar_t!

Führt man jedoch zuerst eine Kovertierung in den “ANSI/Windows/8bit”-Zeichensatz durch und konvertiert anschließenend diesen ANSI-String nach Unicode, dann ist alles gut und so wie man es erwartet.

Wer Lust hat das nachzubauen kann das mit dem folgenden Code. Wichtig sind eigentlich nicht die OEM-Umlaute sondern nur die Tab- und Zeilenschaltungen:

const char szText[] = "Dies ist ein OEM TestString\r\n"
       "mit Zeilenschaltungen\tund Tabs\r\n"
       "und Umlauten:\r\n"
       "Ž=AE, ™=OE, š=šE\r\n"
       "„=ae, ”=oe, =ue, á=ss";
const size_t len = sizeof(szText);
 
CStringW strOut1;
::OemToCharBuffW(szText,CStrBufW(strOut1,len),len);
CStringA strOut2;
::OemToCharBuffA(szText,CStrBufA(strOut2,len),len);
CStringW strOut3(strOut2);
 
_ASSERT(strOut1.Compare(strOut3)==0); // Das sollte eigentlich gleich sein

PS: Getestet habe ich das auf einem Windows 7 64bit und 32bit OS.

Siehe folgende Links zu den Begriffen OEM/ANSI:
http://blogs.msdn.com/b/oldnewthing/archive/2005/10/27/485595.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2005/03/08/389527.aspx

C++ProgrammierenWindows APIMartin Richter - Sa 25 Jun 2011 18:46

Wir haben ein Stück Code, dass verhindern soll, dass ein Programm zweimal gestartet werden kann.
Dieser basiert auf einem Mutex und einer Memory Mapped File, mit der man sich auch das Fenster-Handle einer bereits gestarteten Instanz besorgen kann.

Nun gelang es einem unserer Händler aber dennoch dieses Programm zweimal in einer Session zu starten und zwar auf folgendem Weg:

  1. Er startet die Software mit dem normalen Link auf dem Desktop, der durch das Installationsprogramm angelegt wurde.
  2. Er öffnen eine Console mit CMD.EXE und wechselt in das Verzeichnis, gibt den Programmnamen ein und das Programm startet erneut. :eek:

Die Ursache ist war wie folgt:

  1. Der Mutex den wir intern verwendet haben nutzte den Dateinamen der EXE. Der Name des Mutex wird unter Anderem auch durch GetModuleFileName ermittelt.
  2. Der Dateiname der EXE, wenn sie als Verknüpfung gestartet wird ist “XYZ.exe” (so wie die Datei auch auf der Festplatte heißt) und das liefert auch GetModuleFileName als Ergebnis.
  3. Der Dateiname, den GetModuleFileName liefert, wenn man das Programm aus CMD.EXE startest ist exakt so wie man es eintippt, also z.B. “xyz.exe”. Erstaunlich.
  4. Da der Mutex einen Namen case sensitiv behandelt (was ich nicht vermutet hätte und erst mit staunenden Augen nachgelesen habe), wurde das bereits gestartete Programm nicht erkannt und eine zweite Instanz gestartet.

Was schreiben wir uns also hinter die Löffel für die Zukunft:
a) GetModuleFileName liefert nicht den “exakten” Dateinamen (obwohl ich es anders erwartet hätte)!
b) Mutexe sind case sensitiv wie auch Events (obwohl ich hier eine Behandlung wie bei einem Dateinamen erwartet habe)!
c) Manche Erwartungen trügen… ;)

ProgrammierenVista / Windows 7Windows APIMartin Richter - Mo 25 Apr 2011 20:34

Ich habe für ein Projekt Manifeste zur Verwendung von Registration-Free COM Module gebaut.

Diese COM-Module wurden über ein Manifest in einer EXE geladen. Natürlich hatte jedes der COM Module wieder ein eigenes Manifest, die ich entsprechend angepasst habe. Jede COM-Class, die verwendet wird, muss ja in dem Manifest der DLL-Assembly aufgeführt werden.

Eigentümlich war, dass ich nach Änderungen der Manifeste und auch nachdem das COM Modul komplett neu gelinkt wurde, dennoch die EXE ihr Ladeverhalten nicht geändert hat. Manche COM-Klassen wurden nicht gefunden. Sobald ich aber den Rechner neu gestartet hatte funktionierte ab dann alles wie gewünscht und das geänderte Manifest schien nun wirksam zu sein.

Da gibt es einen Cache dachte ich mir. Und nach einiger Recherche im Internet stieß ich auf den folgenden interessanten Artikel von Junfeng Zang:
Windows Vista Sxs Activation Context Cache

Wie man lesen kann, wird bei jedem erfolgreichen Einlesen einer Anwendung und dem erfolgreichen Laden aller Manifeste und DLLs, die ganzen ermittelten Daten in einen Cache gespeichert. Da ich die EXE aber nicht geändert habe, wurden auch die untergeordneten Manifeste nicht neu gelesen, auch wenn diese geändert wurden.

Durch das Ändern des Datums der EXE werden die Cacheeinträge ungültig, und danach werden alle Aktivierungskontexte der Anwednung und aller anderen Assemblies neu geladen.

C++ProgrammierenVista / Windows 7VS 2010Windows APIMartin Richter - Sa 26 Mrz 2011 20:51

Bei der Umstellung eines Projektes von VS-2005 auf VS-2010 lief unsere Software im Testfeld ohne Probleme, bis auf einem Rechner, auf dem gezielt immer wieder der selbe Fehler auftrat.

Der Stackdump zeigte aber immer wieder eine unterschiedliche Absturzadresse. Glücklicherweise konnten wir durch Crash-Dumps einigermaßen lokalisieren was passierte. Eigentümlicherweise zeigte sich der Crash immer beimAufruf einer bestimmten Windows-Prozedur:

Eigentümlicherweise lief das selbe Programm mit VS-2005 kompiliert auf allen Maschinen. Also vermutete ich einen Compilerfehler! Aber wie kann ein Compilerfehler nur auf einer Maschine zu einem Problem werden?

Aber ich lag komplett daneben.
Nach der Analyse des Codes stellte sich heraus das DEP die Ursache war, und das der Crash nur auf der einizgen Maschine auftrat, auf der DEP unterstützt wurde und eingeschaltet war.

Die anderen Rechner im Testfeld unterstützen DEP nicht:

Und auf diesen trat der Fehler nicht auf.

Weitere Analyse zeigte, dass ein spezieller Code aus einer Fensterklasse, die ich von einer Libary übernommen hatte ein spezielles Windows Subclassing mit einem Thunking ähnlich wie ATL machte, jedoch wurde der Speicher nicht korrekt als ausführbar markierte.
Folgerichtig krachte es. D.h. die paar Codezeilen, die gerade mal einen JMP und das laden eines Registers durchführten wurden von DEP als illegal betrachtet und es kam zum Crash.

Und das eigentümliche, dass der Code mit VS-2005 kompiliert lief, war auch schnell erklärt.
Als das Projekt von VS-2005 in VS-2010 übernommen wurde, wurden auch die Optionen für DEP (/NXCOMPAT) und auch ASLR (/DYNAMICBASE) eingeschaltet :!:

Das ist eigentlich nicht nett, denn es hat schon einige weitreichende Konsequenzen für die Software.
Also aufgepast bei der Konvertierung von Programmen und genau darüber nachgedacht ob man DEP und ASLR wirklich für seine Software will :!:

Nächste Seite »