Ich habe eine DLL die einiges an Datenbank I/O für ein Produkt übernimmt. Diese DLL sollte in einem Service eingesetzt werden.
Eigentlich ist diese DLL immer nur Teil eines vollen UI Projektes und hat auch einige kleine visuelle Komponenten. Entsprechend prüft eine Init Funktion bestimmte Voraussetzungen, die für das korrekte Arbeiten notwendig sind wie: mindestens MS-XML 3.0, IE ab Version 5.1, Common Control 6.0 (also mit Manifest).
Soweit so gut. Die DLL selbst wurde mit einem Manifest (Typ 2) versehen und in den Service mit eingebaut.
Eigentümlicherweise startete der Service nicht. Die Init Funktion meldete immer, dass die Requirements nicht gegeben wären. Was eigentlich nicht sein kann, weil der selbe Code in einem anderen Kontext eines GUI Programms perfekt funktioniert.
Ein wenig debuggen zeigte, dass die Common Control 6.0 nicht gefunden wurden.
Eigentlich kein Wunder. Der Service hat keine UI, es gibt keinen Code der die Common Control 6.0 verwendet, allerdings hatte meine DLL ja ein entsprechendes Manifest für die Common Control 6.0 DLL.
Aber der Versionstest in der DLL lädt immer die 5er Version, also die Version ohne Manifest.
Es dauerte eine Weile bis es bei mir klingelte und ich verstand worin das Problem lag:
- Wenn man ein Manifest in einer DLL mit dem Code 2 einträgt (ISOLATIONAWARE_MANIFEST_RESOURCE_ID), dann funktioniert dieses Manifest wenn in diesem Moment die ComCtl32.dll implizit geladen werden sollte.
- In meinem Fall aber klappte das nicht. Die DLL wurde nicht implizit geladen. Die Versionkontrolle später führte LoadLibrary direkt durch und führte dann DllGetVersion aus (mit GetProcAddress ermittelt).
- In meinem Fall bedeutet das aber, das LoadLibrary im Aktivierungskontext (Activation Context) der EXE ausgeführt wird, und die hat kein ComCtl32 Manifest für die Version 6.0. Es ist ja auch nur ein Service.
- Damit es funktioniert muss man in jedem Fall auch den folgenden Define setzen.
#define ISOLATION_AWARE_ENABLED 1
- oder man muss dafür sorgen, dass man seinen eigenen Aktivierungskontext nutzt. Sprich seine Manifeste, die man selber möchte.
- Durch diesen define, werden bestimmte Funktionen wie CreateWindow, LoadLibrary, CoCreateInstance, SendMessage und andere durch einen inline-Code ersetzt der einen Aktivierungkontext benutzt und die aktuellen Manifeste der DLL bestimmt und benutzt.
In meinem Fall hieß die Lösung nicht LoadLibrary zu benutzen sondern GetModuleHandle und wenn eben ComCtl32 gar nicht im Speicher ist, also auch nicht benutzt wird auch keinen weiteren Test durchzuführen, aber zur Sicherheit habe ich auch ISOLATION_AWARE_ENABLED gesetzt.
Siehe auch:
http://msdn.microsoft.com/en-us/library/aa376607(VS.85).aspx
http://msdn.microsoft.com/en-us/library/aa375197(VS.85).aspx