Assemblies nur einen Mausklick entfernt von COM+
Mein kleines Tool zur Vereinfachung des Umgangs mit dem GAC hat mich nicht in Ruhe gelassen. Ein paar Kleinigkeiten wollte ich noch polieren - aber vor allem wollte ich eine automatisches Registrierung von Serviced Components. Jetzt habe ich endlich Zeit gefunden, alles einzubauen. Der komplette Quelltext ist unten zu finden.
Verbesserungen/Veränderungen:
-Nach einem Aufruf eine externen Programms wie gacutil.exe wird jetzt auf dessen Prozessende gewartet. Die gesamte Logik des Tools hier läuft damit sequenziell ab; deshalb ist auch am Ende ein Schlussmeldung und ein Warten auf Tastendruck möglich. Zum Warten auf ein Prozessende siehe auch meinen Blog-Eintrag "Hürde beim Warten auf ein Prozessende".
-Ich habe den Code etwas refaktorisiert, indem ich Teile rausgezogen habe in eigene Routinen (Affect...()). Die Main()-Methode ist dadurch übersichtlicher geworden und Code zu einem Thema wie GAC-Registrierung usw. steht nur noch an einem Ort. Das Buch "Refactoring. Wie Sie das Design vorhandener Software verbessern" von Martin Fowler sei an dieser Stelle nochmals empfohlen.
Neuerungen:
-Die zu behandelnde Assembly wird daraufhin geprüft, ob sie einen .NET Enterprise Services (ES) Dienst darstellt. Mein Kriterium ist das Assembly-Attribut ApplicationActivation. Ist es vorhanden, wird im Menü die Option geboten, die Assembly bei den ES zu (de)registrieren. Ich denke, dass diese Annahme ausreichend ist, denn zur automatischen Registrierung muss regsvcs.exe bekannt gemacht werden, in welcher COm+ Applikation die DLL registriert werden soll. Da die meisten Komponenten applikationsspezifisch sein werden, ist es allemal während der Entwicklung bequem, ihre Applikationszugehörigkeit in ihnen mit dem Attribut zu verdrahten.
-Wenn die Assembly bei COM+ (de)registriert werden soll, dann ruft das Tool regsvcs.exe auf. Solange Sie COM+ Library-Applikationen entwickeln, ist eine solche explizite Registrierung nicht immer nötig. VS.NET macht das für sie. Bei Server-Applikationen allerdings schlägt der Automatismus fehl. Und Server-Apps sind z.B. für Queued Components zwingend notwendig. Hier leistet das Tool wertvolle Hilfe, wenn man neben VS.NET noch einen Windows Explorer mit dem Verzeichnis der gerade in Entwicklung befindlichen Anwendung offen hat:
1. Anwendung inkl. Serviced Component Assembly übersetzen.
2. Im Windows Explorer das Tool auf der Serviced Component Assembly aufrufen und "14" eingeben (registrieren der Assembly im GAC und bei COM+).
3. Anwendung in VS.NET starten und testen.
4. Vor weiterarbeit an der Serviced Component die Assembly mit dem Tool und "24" aus dem GAC nehmen und bei COM+ deregistrieren.
Der mangelnde Automatismus von VS.NET fällt da fast gar nicht mehr auf ;-)
Enjoy!
imports Microsoft.Win32
Module Module1
Sub Main(args() as string)
Dim shellKey as RegistryKey = Registry.ClassesRoot.CreateSubKey("dllfile\Shell\GacUtils\Command")
shellKey.SetValue("", string.Format("{0} ""%1""", Reflection.Assembly.GetExecutingAssembly.Location))
shellKey = Registry.ClassesRoot.CreateSubKey("dllfile\Shell\ILdasm\Command")
shellKey.SetValue("", string.Format("{0} ""%1""", "ildasm.exe"))
shellKey = Registry.ClassesRoot.CreateSubKey("exefile\Shell\ILdasm\Command")
shellKey.SetValue("", string.Format("{0} ""%1""", "ildasm.exe"))
if args.Length <> 1 then
msgbox("Usage: GacUtils <DLL assembly filename>", MsgBoxStyle.Information)
exit sub
End If
dim ass as Reflection.Assembly = Reflection.Assembly.LoadFrom(args(0))
dim assName as String = ass.FullName.Split(",")(0)
dim isServicedComponent as Boolean
isServicedComponent = ass.GetCustomAttributes(gettype(System.EnterpriseServices.ApplicationActivationAttribute), false).Length > 0
dim menu as String = "1=Register in GAC,2=Unreg from GAC,3=Affect 'Add references' dialog"
if isServicedComponent then menu &= ",4=Affect COM+ catalog"
Console.WriteLine(menu.Replace(",", vbcrlf))
Console.Write(" Multiple commands allowed: ")
dim cmds as String = console.ReadLine
dim p as Process
if cmds.IndexOf("2") >= 0 then
if cmds.IndexOf("4") >= 0 and isServicedComponent then AffectComPlus(false, assName)
AffectGAC(false, assName)
if cmds.IndexOf("3") >= 0 then AffectAddRefListing(assName)
End If
if cmds.IndexOf("1") >= 0 then
AffectGAC(True, assName)
if cmds.IndexOf("4") >= 0 and isServicedComponent then AffectComPlus(True, assName)
if cmds.IndexOf("3") >= 0 then AffectAddRefListing(assName, IO.Path.GetDirectoryName(args(0)))
End If
if cmds = "3" then AffectAddRefListing(assName, IO.Path.GetDirectoryName(args(0)))
if cmds = "4" and isServicedComponent then AffectComPlus(True, assName)
console.WriteLine
console.Write("<enter> to quit... ")
console.readline
End Sub
private sub AffectGAC(register as Boolean, assemblyName as String)
dim fmt as String = iif(register, "/i {0}.dll", "/u {0}")
dim psi as New ProcessStartInfo("gacutil.exe", string.Format(fmt, assemblyName))
psi.UseShellExecute = false
dim p as Process = Process.Start(psi)
p.WaitForExit
End Sub
private sub AffectComPlus(register as Boolean, assemblyName as String)
dim fmt as String = iif(register, "{0}.dll", "/u {0}.dll")
dim psi as New ProcessStartInfo("regsvcs.exe", string.Format(fmt, assemblyName))
psi.UseShellExecute = false
dim p as Process = Process.Start(psi)
p.WaitForExit
End Sub
private sub AffectAddRefListing(assemblyName as String, optional path as string = "")
console.WriteLine
if path <> "" then
console.WriteLine("Registering directory {0} for inclusion in 'Add references'", path)
dim refKey as RegistryKey = Registry.LocalMachine.CreateSubKey("Software\Microsoft\.NETFramework\AssemblyFolders\" & assemblyName)
refKey.SetValue("", path)
else
console.WriteLine("Unregistering directory {0} from 'Add references'", path)
Registry.LocalMachine.DeleteSubKey("Software\Microsoft\.NETFramework\AssemblyFolders\" & assemblyName)
End If
End Sub
End Module