Wir alle benutzen sie mehr oder weniger oft, doch kaum einer nimmt Notiz von ihr; die Fusion-API. Egal ob mit gacutil.exe eine Assembly dem GAC hinzugefügt wird oder ein MSI-Paket eine Assembly im GAC installiert, es ist immer die Fusion-API die das erledigt. Selbst der Assembly Cache Viewer des Explorer, die shfusion.dll, verwendet die Fusion-API.

Ich stand immer wieder vor dem Problem verschiedene Assemblies im GAC zu installieren auf Maschinen auf denen kein .NET SDK installiert ist, also auch keine gacutil.exe. Der Assembly Cache Viewer ist zu umständlich da, soweit ich es weiß, er nicht gescriptet werden kann. Jedesmal ein Setup-Projekt erstellen; das kann es doch nicht sein! Also begann ich eine kleine Konsolenanwendung um mir die Aufgabe ein wenig zu erleichtern. Dachte ich!

Mir fiel sehr schnell eine Eigenart der CLR auf: wenn eine Assembly geladen wurde um mittels Reflection die Attribute der Assembly auszulesen, kann diese Assembly weder verschoben noch gelöscht werden. Nach einigen weniger Erfolgreichen Recherchen, stieß ich auf den Sample Managed GAC API Wrappers Artikel von Junfeng Zhang. Nachdem ich den Ansatz gefunden hatte, sah ich mir die Fusion (Referenz zur nicht verwalteten API) einmal genauer an. Heureka! Das ist es.

Als Basis für meinen GacWrapper habe ich den oben erwähnten Artikel von Junfeng Zhang verwendet. Doch mein erstes Ziel, das Auslesen der Assembly-Attribute unter Angabe des Dateinamens, war mit dieser Beispiel-Implementierung nicht zu lösen. Mit der Methode GetAttribute der Schnittstelle IReferenceIdentity könnte dieses Problem jedoch gelöst werden. Die Methode gibt in der Original-Implementierung ein HRESULT zurück und liefert den benötigten Wert mittels eines out-Parameter. Da out-Parameter vermieden werden sollten, habe ich die Methode so geändert, dass der Wert direkt als string zurückgegeben wird. Hier die Schnittstelle im Code:

Jetzt wird noch eine Methode benötigt die der Schnittstelle mitteilt, welche Assembly behandelt werden soll. Die Fusion-API bietet hier die statische Methode GetAssemblyIdentityFromFile an, welche als Rückgabewert einen Zeiger auf ein IUnknown-Objekt liefert. Da die Implementierung der IReferenceIdentity als ComInterfaceTyp InterfaceIsUnKnown verfügbar ist, sollten hier keine Probleme auftauchen. Die statische Methode erwartet den Pfad zur Assembly und die Guid der verwendeten Schnittstelle. Da ich, wenn möglich, Zeiger im verwalteten Code vermeide habe ich die Implementierung der statischen Methode so geändert, dass ein Object zurückgegeben wird. Das Rückgabe-Objekt kann dann in ein Objekt vom Typ IReferenceIdentity geparst werden. Natürlich muss hier das zurückgegebene Objekt in den Typ IUnknown gemarshallt werden. Auch muss unbedingt beachtet werden, dass im DllImport-Attribute der Zeichensatz Unicode beim marshalling verwendet wird. Die Umsetzung in Code sollte dann etwa so aussehen:

Soweit zur Implementierung des nicht verwalteten Codes. Als nächstes habe ich eine Klasse gebildet in der auf die Assembly-Attribute lesend zugegriffen werden kann. Da die Möglichkeit zur Iteration durch die Attribute gegeben werden soll, verwende ich ein StringDictionary um die ermittelten Attribute in einer Klasse vor zuhalten. Hier ein Auszug daraus:

Bis jetzt ist die Möglichkeit geschaffen die Attribute einer Assembly, nur anhand des Dateinamens, auszulesen. Die Assembly wurde dabei nicht von der CLR geladen, kann also verschoben, gelöscht oder wie auch immer gehandhabt werden.

Die Fusion-API bietet deutlich mehr als diese eine Möglichkeit. Ich werde nach und nach, auch soweit ich Zeit dazu finde, diese Serie erweitern.

Technorati-Tags:  |  | 
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitte "kicken" sie ihn.
kick it on dotnet-kicks.de