Nachlese zum ClickOnce - TechTalk

Endlich, ich bin wieder zurück im Büo. Zwar nur kurz, denn der Launch Event nächste Woche in Karlsruhe wartet schon. Der TechTalk - 10 Städte quer durch die Republik - ist vorbei. Es hat Spaß gemacht. Ich hoffe, Ihnen auch. Auch wenn für manchen Entwickler etwas zu viel "Verwaltungskram" dabei gewesen sein mag. Sicherheit im IT-Bereich kommt nicht von allein. Und es sind manchmal mehr organisatorische als technische Hürden zu nehmen.

Um Sie nicht warten zu lassen, hier ist die Präsentation: Deployment mit ClickOnce

Für die Aufzeichnung der Demos werden wir noch etwas Zeit brauchen, Ende des Monats sollte es dann soweit sein. Ebenso wird es noch etwas dauern, alle Fragen, die so auf verschiedenen Kanälen nachträglich in meinen Postkasten geflattert sind zu beantworten. Ich bitte um ein wenig Geduld.

Hier noch der SourceCode für das Beispiel Download on Demand (Plug-In-Modell, Bildung von Feature-Gruppen und Nachladen derselben, wenn eine Assembly der entsprechenden Gruppe nicht aufgelöst werden konnte), diesmal für die C#-Fans:

private void Form1_Load( object sender, EventArgs e )
{
  currentDomain.AssemblyResolve += new ResolveEventHandler(AsmResolveHandler);
}

private System.Reflection.Assembly AsmResolveHandler( object sender, ResolveEventArgs args )
{
  System.Reflection.Assembly asm = null;
  if (ApplicationDeployment.IsNetworkDeployed)
  {
    ApplicationDeployment appdeploy = ApplicationDeployment.CurrentDeployment;
    string sLib = args.Name.Remove( args.Name.IndexOf( "," ) ) + ".dll";
    string sGroup = "";

    XmlDocument xDoc = new XmlDocument();
    xDoc.Load( Application.ExecutablePath + ".manifest" );
    XmlNodeList NodeList = xDoc.GetElementsByTagName( "dependency" );
    foreach (XmlNode cn in NodeList)
    {
      if (cn.Attributes.Count > 0)
        if (cn.Attributes["optional"].Value == "true")
        {
          XmlNode targetNode = cn["dependentAssembly"];
          if (targetNode.Attributes["codebase"].Value == sLib)
            sGroup = targetNode.Attributes["group"].Value;
        }
    }

    if (!appdeploy.IsFileGroupDownloaded(sGroup) )
      appdeploy.DownloadFileGroup(sGroup);

    asm = System.Reflection.Assembly.LoadFrom(Application.StartupPath + '\\' + sLib);

  }
  return asm;
}

Wir verwenden hier ein XML-Document, um auf die entsprechenden Knoten des Application Manifests zuzugreifen (liegt bekanntlich im gleichen Ordner wie die Anwendung) und die Gruppenzugehörigkeit einer Assembly zu ermitteln. Ein solcher Knoten könnte folgendermaßen aussehen:

  <dependency optional="true">
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="FirstLibrary.dll" size="16384" group="FirstGroup">
<assemblyIdentity name="FirstLibrary" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="https://www.w3.org/2000/09/xmldsig#sha1" />
<dsig:DigestValue>Ar2wudPeX4jeWeSc4bm5otEEIn8=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

Der Algorithmus ist generisch, sollte also für beliebige Gruppen mit beliebig vielen Assemblies funktionieren.

Genutzt wird der AssemblyRessolve Event der aktuellen Application Domain, da dieser gefeuert wird, wenn eine Referenz nicht aufgelöst werden konnten. Somit haben wir immer genau den richtigen Zeitpunkt zum Herunterladen der Gruppe vom server und Nachladen der entsprechenden Assembly in die aktuelle AppDomain.