[C#] AssemblyLoad Event – geladene Assemblies ausgeben

static class Program
{
    /// <summary->
    /// Der Haupteinstiegspunkt für die Anwendung.
    /// </summary->
    [STAThread]
    static void Main()
    {
        // Event anmelden
        AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    /// <summary->
    /// statische Event-Funktion, wird beim Laden einer Assembly aufgerufen
    /// </summary->
    /// <param name=&quot;sender&quot;-></param->
    /// <param name=&quot;args&quot;-></param->
    private static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
    {
        Console.WriteLine(args.LoadedAssembly.FullName);
    }
}

Manifest in ein Projekt einbinden

Häufig ist es notwendig, dass man einer Anwendung Adminrechte gibt, wenn diese zum Beispiel Netzwerkzugriffe, Registryeingriffe usw. tätigen muss. Dazu muss man eine Anwendungsmanifestdatei in die exe-Datei einkompilieren, in welcher die entsprechenden Benutzerrechte für die Windows-UAC eingetragen sind.

Variante 1:

  1. In Visual Studio 2010 besteht die Möglichkeit eine Manifestdatei erzeugen lassen: im Projekt Rechtsklick auf das Projekt->Hinzufügen->Neues Element …->Anwendungsmanifestdatei. Visual Studio erzeugt unten stehende Datei, wobei die entsprechend benötigten Zeilen aus- bzw. einkommentiert werden müssen:
    <?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?->
    <asmv1:assembly manifestVersion=&quot;1.0&quot; xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot; xmlns:asmv1=&quot;urn:schemas-microsoft-com:asm.v1&quot; xmlns:asmv2=&quot;urn:schemas-microsoft-com:asm.v2&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;->
      <assemblyIdentity version=&quot;1.0.0.0&quot; name=&quot;MyApplication.app&quot;/->
      <trustInfo xmlns=&quot;urn:schemas-microsoft-com:asm.v2&quot;->
        <security->
          <requestedPrivileges xmlns=&quot;urn:schemas-microsoft-com:asm.v3&quot;->
            <!-- UAC-Manifestoptionen
              Wenn Sie die Zugangsebene für das Windows-Benutzerkonto ändern möchten, ersetzen Sie den 
              requestedExecutionLevel-Knoten durch eines der folgenden Elemente.
    
            <requestedExecutionLevel  level=&quot;asInvoker&quot; uiAccess=&quot;false&quot; /->
            <requestedExecutionLevel  level=&quot;requireAdministrator&quot; uiAccess=&quot;false&quot; /->
            <requestedExecutionLevel  level=&quot;highestAvailable&quot; uiAccess=&quot;false&quot; /->
    
                Durch Angeben des requestedExecutionLevel-Knotens wird die Datei- und Registrierungsvirtualisierung deaktiviert.
                Wenn Sie Datei- und Registrierungsvirtualisierung für Abwärts- 
                kompatibilität verwenden möchten, löschen Sie den requestedExecutionLevel-Knoten.
            -->
            <requestedExecutionLevel level=&quot;asInvoker&quot; uiAccess=&quot;false&quot; /->
          </requestedPrivileges->
        </security->
      </trustInfo->
      
      <compatibility xmlns=&quot;urn:schemas-microsoft-com:compatibility.v1&quot;->
        <application->
          <!-- Eine Liste aller Windows-Versionen, mit denen die Anwendung kompatibel ist. Windows wählt automatisch die am stärksten kompatible Umgebung aus.-->
    
          <!-- Wenn die Anwendung mit Windows 7 kompatibel ist, heben Sie die Kommentierung des folgenden supportedOS-Knotens auf.-->
          <!--<supportedOS Id=&quot;{35138b9a-5d96-4fbd-8e2d-a2440225f93a}&quot;/->-->
          
        </application->
      </compatibility->
      
      <!-- Designs für allgemeine Windows-Steuerelemente und -Dialogfelder (Windows XP und höher) aktivieren -->
      <!-- <dependency->
        <dependentAssembly->
          <assemblyIdentity
              type=&quot;win32&quot;
              name=&quot;Microsoft.Windows.Common-Controls&quot;
              version=&quot;6.0.0.0&quot;
              processorArchitecture=&quot;*&quot;
              publicKeyToken=&quot;6595b64144ccf1df&quot;
              language=&quot;*&quot;
            /->
        </dependentAssembly->
      </dependency->-->
    
    </asmv1:assembly->
    
  2. eine Anwendungsmanifestdatei für Windows 7 und mit Zugriffsrechten “Admin” sieht beispielhaft so aus:
    <?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?->
    <asmv1:assembly manifestVersion=&quot;1.0&quot; xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot; xmlns:asmv1=&quot;urn:schemas-microsoft-com:asm.v1&quot; xmlns:asmv2=&quot;urn:schemas-microsoft-com:asm.v2&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;->
      <assemblyIdentity version=&quot;1.0.0.0&quot; name=&quot;MyApplication.app&quot;/->
      <trustInfo xmlns=&quot;urn:schemas-microsoft-com:asm.v2&quot;->
        <security->
          <requestedPrivileges xmlns=&quot;urn:schemas-microsoft-com:asm.v3&quot;->
            <requestedExecutionLevel level=&quot;requireAdministrator&quot; uiAccess=&quot;false&quot; /->
          </requestedPrivileges->
        </security->
      </trustInfo->
      
      <compatibility xmlns=&quot;urn:schemas-microsoft-com:compatibility.v1&quot;->
        <application->
          <supportedOS Id=&quot;{35138b9a-5d96-4fbd-8e2d-a2440225f93a}&quot;/->
        </application->
      </compatibility->
      
      <!-- Designs für allgemeine Windows-Steuerelemente und -Dialogfelder (Windows XP und höher) aktivieren -->
      <!-- <dependency->
        <dependentAssembly->
          <assemblyIdentity
              type=&quot;win32&quot;
              name=&quot;Microsoft.Windows.Common-Controls&quot;
              version=&quot;6.0.0.0&quot;
              processorArchitecture=&quot;*&quot;
              publicKeyToken=&quot;6595b64144ccf1df&quot;
              language=&quot;*&quot;
            /->
        </dependentAssembly->
      </dependency->-->
    
    </asmv1:assembly->
    
  3. Die Manifestdatei muss unter Projektbaum->Eigenschaften->Anwendung->Symbol und Manifest->Manifest (Datei “app.manifest”) ausgewählt sein, damit sie von Visual Studio mit in die Anwendung einkompiliert wird.
  4. Die im Ausgabeverzeichnis fertig kompilierte Anwendung sollte nun ein Security-Schild im Icon haben.
  5. Nach dem Start der Anwendung fragt Windows-UAC nun, ob das Programm mit Adminrechten ausgeführt werden darf.

Variante 2:

  1. Diese manuelle Variante funktioniert nur mit VS Standard oder Professional, nicht mit Express, da bei letzterem keine Post Build Events eingestellt werden können.
  2. Manifestdatei in einem Editor erzeugen, Projektnamen bei “name” korrekt eintragen, Prozessorarchitektur anpassen und im Ausgabeverzeichnis des Projektes speichern. Eine Beispieldatei zum Erlangen von Adminrechten siehe oben unter Punkt 2 bei Varante 1.
  3. Mittels mt.exe wird nun die Manifestdatei in die ausführbare Datei als Win32-Resource eingebunden. Die prinzipielle Kommandozeile dazu lautet:
    mt.exe –manifest temp.manifest –outputresource:YourApp.exe;#1
    

    Dazu unter Projektbaum->Projekt->Eigenschaften->Buildereignisse->Befehlszeile für Postbuild-Ereignis folgende Zeile eintragen:

    "$(DevEnvDir)..\..\SDK\v2.0\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest"  –outputresource:"$(TargetDir)$(TargetFileName)";#1
    

    bei anderen VS-Versionen alternativ auch:

    "c:\Programme\Microsoft SDKs\Windows\v7.0A\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest"  –outputresource:"$(TargetDir)$(TargetFileName)";#1
    
  4. Die im Ausgabeverzeichnis fertig kompilierte Anwendung sollte nun ein Security-Schild im Icon haben.
  5. Nach dem Start der Anwendung frag Windows-UAC nun, ob das Programm mit Adminrechten ausgeführt werden darf.

[C#] Assemblyinfo auslesen

using System.IO;
using System.Reflection;

/// <summary>
/// freeware helper class for getting .NET assembly info
/// (W) 2011 by admin of codezentrale.6x.to
/// </summary>
public static class AssemblyInfo
{
    /// <summary>
    /// get assembly title
    /// </summary>
    public static string AssemblyTitle
    {
        get
        {
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);

            if (attributes.Length > 0)
            {
                AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];

                if (titleAttribute.Title != string.Empty) return titleAttribute.Title;
            }

            return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
        }
    }
    /// <summary>
    /// get assembly version
    /// </summary>
    public static string AssemblyVersion
    {
        get
        {
            return Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }
    }
    /// <summary>
    /// get assembly description
    /// </summary>
    public static string AssemblyDescription
    {
        get
        {
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);

            if (attributes.Length == 0) return string.Empty;

            return ((AssemblyDescriptionAttribute)attributes[0]).Description;
        }
    }
    /// <summary>
    /// get assembly product
    /// </summary>
    public static string AssemblyProduct
    {
        get
        {
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);

            if (attributes.Length == 0) return string.Empty;

            return ((AssemblyProductAttribute)attributes[0]).Product;
        }
    }
    /// <summary>
    /// get assembly copyright
    /// </summary>
    public static string AssemblyCopyright
    {
        get
        {
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);

            if (attributes.Length == 0) return string.Empty;

            return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
        }
    }
    /// <summary>
    /// get assembly company
    /// </summary>
    public static string AssemblyCompany
    {
        get
        {
            object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);

            if (attributes.Length == 0) return string.Empty;

            return ((AssemblyCompanyAttribute)attributes[0]).Company;
        }
    }
}

Mathematische Ausdrücke evaluieren, C#-Code zur Laufzeit compilieren

Einleitung

Das .NET-Framework bietet über das CodeDomProvider-Objekt eine Möglichkeit zur Laufzeit Sourcecode zu kompilieren und diesen auszuführen. Es kann somit auf elegante Art und Weise beliebiger Code, u.A. mathematische Ausdrücke im C#-Syntax, evaluiert werden. Die Klasse CCodeCompiler kompiliert eine zur Laufzeit in C# “geschriebene” Funktion (sbCode) und gibt deren Referenz für die Ausführung zurück. Die genaue Anwendung ist im unten stehenden Anwendungsbeispiel beschrieben.

Code

using Microsoft.CSharp;
using System.Reflection;
using System.CodeDom.Compiler;

/// <summary>
/// helper class for compiling C#-code at runtime
/// free code (W) 2010 by admin of codezentrale.de
/// </summary>
public class CCodeCompiler
{
    private Assembly _assCompiledAssembly = null;
    private MethodInfo _miMethodInfo = null;
    private Type _tyContainerType = null;
    private bool _bCompiled = false;
    private string _sClassName = string.Empty;
    private CompilerErrorCollection _cecCompilerErrors = null;
    /// <summary>
    /// reference to the compiled assembly
    /// </summary>
    public Assembly CompiledAssembly
    {
        get { return CompiledAssembly; }
    }
    /// <summary>
    /// reference to the compiled method
    /// </summary>
    public MethodInfo MethodInfo
    {
        get { return _miMethodInfo; }
    }
    /// <summary>
    /// type of the compiled method
    /// </summary>
    public Type ContainerType
    {
        get { return _tyContainerType; }
    }
    /// <summary>
    /// flag, if compiling was successfully
    /// </summary>
    public bool IsCompiled
    {
        get { return _bCompiled; }
    }
    /// <summary>
    /// classname of the compiled method
    /// </summary>
    public string ClassName
    {
        get { return _sClassName; }
    }
    /// <summary>
    /// a string with all the errors stored in the CompilerErrorCollection
    /// </summary>
    public string CompilerErrors
    {
        get
        {
            string sRetVal = string.Empty;

            if (_cecCompilerErrors != null)
            {
                foreach (CompilerError ce in _cecCompilerErrors)
                {
                    sRetVal += ce.ToString() + Environment.NewLine;
                }
            }

            return sRetVal;
        }
    }
    /// <summary>
    /// creates a virtual method from given sourcecode
    /// </summary>
    /// <param name="sSourceCode">the complete sourcecode of the method</param>
    /// <param name="sMethodName">the name of the method</param>
    /// <param name="bIncludeDebugInfo">include debuginfo into the compiled assembly</param>
    /// <param name="psaReferences">some referenced assmblies (.NET DLLs) beiing used for working the code, full pathname needed</param>
    /// <returns>true if the compilation was successfull, else false</returns>
    public bool CreateMethodRef(string sSourceCode, string sMethodName, bool bIncludeDebugInfo, params string[] psaReferences)
    {
        _assCompiledAssembly = null;
        _miMethodInfo = null;
        _tyContainerType = null;
        _bCompiled = false;
        _sClassName = sMethodName + "Container";
        _cecCompilerErrors = null;

        // the compiler object
        CodeDomProvider cdpCompilerObject = CodeDomProvider.CreateProvider("CSharp");

        // build options
        CompilerParameters cpParams = new CompilerParameters();
        cpParams.GenerateInMemory = true;
        cpParams.GenerateExecutable = false;
        cpParams.IncludeDebugInformation = bIncludeDebugInfo;
        cpParams.ReferencedAssemblies.AddRange(psaReferences);

        // string for the used namespace
        string sNameSpace = cpParams.OutputAssembly;
        if (sNameSpace == null) sNameSpace = "Evaluated";

        // build the complete code
        StringBuilder sbCode = new StringBuilder();

        // include the needed assemblies
        foreach (string sAssemblies in cpParams.ReferencedAssemblies)
        {
            sbCode.Append("using " + Assembly.LoadFrom(sAssemblies).GetName().Name + ";" + Environment.NewLine);
        }

        // include namespace
        sbCode.Append("namespace " + sNameSpace + Environment.NewLine);
        sbCode.Append("{" + Environment.NewLine);
        // include the class
        sbCode.Append("    public class " + _sClassName + Environment.NewLine);
        sbCode.Append("    {" + Environment.NewLine);
        // include the given sourcecode-part
        sbCode.Append(sSourceCode);
        sbCode.Append("    }" + Environment.NewLine);
        sbCode.Append("}" + Environment.NewLine);

        // compile the sourcecode
        CompilerResults crResults = cdpCompilerObject.CompileAssemblyFromSource(cpParams, sbCode.ToString());

        // some errors?
        if (crResults.Errors.HasErrors)
        {
            _cecCompilerErrors = crResults.Errors;
        }
        else
        {
            // when everything was fine, set some variables
            // assembly
            _assCompiledAssembly = crResults.CompiledAssembly;
            // type of method
            _tyContainerType = _assCompiledAssembly.GetType(sNameSpace + "." + _sClassName, true);
            // the method
            _miMethodInfo = _tyContainerType.GetMethod(sMethodName);
            
            _bCompiled = true;
        }

        return _bCompiled;
    }
    /// <summary>
    /// creates a virtual method from given sourcecode
    /// </summary>
    /// <param name="sSourceCode">the complete sourcecode of the method</param>
    /// <param name="sNameSpace">a namespace where the source is compiled</param>
    /// <param name="sMethodName">the name of the method</param>
    /// <param name="bIncludeDebugInfo">include debuginfo into the compiled assembly</param>
    /// <returns>true if the compilation was successfull, else false</returns>
    public bool CreateMethodSimple(string sSourceCode, string sNameSpace, string sMethodName, bool bIncludeDebugInfo)
    {
        _assCompiledAssembly = null;
        _miMethodInfo = null;
        _tyContainerType = null;
        _bCompiled = false;
        _sClassName = sMethodName + "Container";
        _cecCompilerErrors = null;

        // the compiler object
        CodeDomProvider cdpCompilerObject = CodeDomProvider.CreateProvider("CSharp");

        // build options
        CompilerParameters cpParams = new CompilerParameters();
        cpParams.GenerateInMemory = true;
        cpParams.GenerateExecutable = false;
        cpParams.IncludeDebugInformation = bIncludeDebugInfo;

        // add (reference) all global available assemblies
        foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            cpParams.ReferencedAssemblies.Add(asm.Location);
        }

        // compile the sourcecode
        CompilerResults crResults = cdpCompilerObject.CompileAssemblyFromSource(cpParams, sSourceCode);

        // some errors?
        if (crResults.Errors.HasErrors)
        {
            _cecCompilerErrors = crResults.Errors;
        }
        else
        {
            // when everything was fine, set some variables
            // assembly
            _assCompiledAssembly = crResults.CompiledAssembly;
            // type of method
            _tyContainerType = _assCompiledAssembly.GetType(sNameSpace + "." + _sClassName, true);
            // the method
            _miMethodInfo = _tyContainerType.GetMethod(sMethodName);

            _bCompiled = true;
        }

        return _bCompiled;
    }
}

Anwendungsbeispiel

Nachfolgend sind zwei Beispiele für die Anwendung von CCodeCompiler aufgezeigt. Folgender C#-Code wird dabei zur Laufzeit kompiliert und ausgeführt:

using System;
namespace Evaluated
{
    public class CalcContainer
    {
        public double Calc(double[] x)
        {
            double dResult = 0.0;
            dResult = x[0] + x[1];
            return dResult;
        }
    }
}

Im Beispiel 1 werden explizit die zu referenzierenden Assemblies mit vollständigem Pfad und Namen angegeben, im Beispiel 2 dagegen intern von der Funktion alle verfügbaren Assemblies automatisch eingeladen.

using Microsoft.CSharp;
using System.Reflection;
using System.CodeDom.Compiler;

// example 1
private void btnEvaluateRef_Click(object sender, System.EventArgs e)
{
    // the methodname
    string sMethodName = "Calc";

    // example code
    StringBuilder sbCode = new StringBuilder();
    sbCode.Append("        public double " + sMethodName + "(double[] x)" + Environment.NewLine);
    sbCode.Append("        {" + Environment.NewLine);
    sbCode.Append("            double dResult = 0.0;" + Environment.NewLine);
    sbCode.Append("            dResult = x[0] + x[1];" + Environment.NewLine);
    sbCode.Append("            return dResult;" + Environment.NewLine);
    sbCode.Append("        }" + Environment.NewLine);

    // reference one needed assembly for "Math" called "System"
    string[] saReferenceAssemblyDLLs = new string[1];
    saReferenceAssemblyDLLs[0] = @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll"; // for "using System;"

    try
    {
        // create a codecompiler object
        CCodeCompiler rtc = new CCodeCompiler();

        // compile and create the Calc-Method
        if (rtc.CreateMethodRef(sbCode.ToString(), sMethodName, false, saReferenceAssemblyDLLs))
        {
            // reference to the method
            MethodInfo miMethod = rtc.MethodInfo;

            // some input values for test calculation
            double[] daX = new double[2];
            daX[0] = 1.2;
            daX[1] = 2.0;

            // call the compiled method with input values
            string sResult = miMethod.Invoke(Activator.CreateInstance(rtc.ContainerType), new object[] { daX } ).ToString();
        }
        else
        {
            // show compiler errors
            MessageBox.Show(rtc.CompilerErrors);
        }
    }
    catch (Exception ce)
    {
        // if there is a exception during execution, show it
        MessageBox.Show(ce.Message + Environment.NewLine + ce.StackTrace);
    }
}

// example 2
private void btnEvaluateSimple_Click(object sender, System.EventArgs e)
{
    // the namespace
    string sNameSpace = "Evaluated";
    // the methodname
    string sMethodName = "Calc";

    // example code
    StringBuilder sbCode = new StringBuilder();
    // some assembly references
    sbCode.Append("using System;" + Environment.NewLine);
    // include namespace
    sbCode.Append("namespace " + sNameSpace + Environment.NewLine);
    sbCode.Append("{" + Environment.NewLine);
    // include the class
    sbCode.Append("    public class " + sMethodName + "Container" + Environment.NewLine);
    sbCode.Append("    {" + Environment.NewLine);
    sbCode.Append("        public double " + sMethodName + "(double[] x)" + Environment.NewLine);
    sbCode.Append("        {" + Environment.NewLine);
    sbCode.Append("            double dResult = 0.0;" + Environment.NewLine);
    sbCode.Append("            dResult = x[0] + x[1];" + Environment.NewLine);
    sbCode.Append("            return dResult;" + Environment.NewLine);
    sbCode.Append("        }" + Environment.NewLine);
    sbCode.Append("    }" + Environment.NewLine);
    sbCode.Append("}" + Environment.NewLine);

    try
    {
        // create a codecompiler object
        CCodeCompiler rtc = new CCodeCompiler();

        // compile and create the Calc-Method without any assembly
        // these will be referenced all internally
        if (rtc.CreateMethodSimple(sbCode.ToString(), sNameSpace, sMethodName, false))
        {
            // reference to the method
            MethodInfo miMethod = rtc.MethodInfo;

            // some input values for test calculation
            double[] daX = new double[2];
            daX[0] = 1.2;
            daX[1] = 2.0;

            // call the compiled method with input values
            string sResult = miMethod.Invoke(Activator.CreateInstance(rtc.ContainerType), new object[] { daX } ).ToString();
        }
        else
        {
            // show compiler errors
            MessageBox.Show(rtc.CompilerErrors);
        }
    }
    catch (Exception ce)
    {
        // if there is a exception during execution, show it
        MessageBox.Show(ce.Message + Environment.NewLine + ce.StackTrace);
    }
}

Versionsinformationen auslesen

using System.Reflection;

Version v = Assembly.GetExecutingAssembly().GetName().Version;

string sVersion = v.Major.ToString() + "." + v.Minor.ToString() + "." + v.Build.ToString() + "." + v.Revision.ToString();