Laden...

[gelöst] Event auf Methoden-Aufruf über Attribute

Erstellt von dr4g0n76 vor 17 Jahren Letzter Beitrag vor 17 Jahren 19.618 Views
dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren
[gelöst] Event auf Methoden-Aufruf über Attribute

Wahrscheinlich hab ich die falschen Worte benutzt, aber ich habe in der Suche hier nichts dazu gefunden.

Ist es möglich - und wenn ja, wie - z.B. folgendes zu machen:

alle Methoden über denen ein Attribut (z.B. hier Log("Calc") angegeben ist, sollen automatisch einen Log-Eintrag über das Attribut bekommen. So könnte man die Log-Logik von dem Rest trennen und das Programm wäre nicht mehr übersät mit einzelnen Log-Aufrufen. Aber ich weiß nicht, ob Dot-Net so einen Mechanismus bietet.

Klar, die Attribute abfragen geht, aber wie bekomme ich ein Event, das zeigt, dass jetzt die Methode mit dem Attribut "Log" aufgerufen wurde?


class Test()
{
     [Log("Calc")]
     public void Calc()
     {
     }
}

Ich denke, dass irgendwo in der Reflection der Weg zur Lösung liegt. Habe aber keine Ahnung, wo ich suchen soll.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

die Attribut-Objekte werden erst erzeugt, wenn man per Reflection darauf zugreift. Also ohne Code in der Methode wirst du leider kein Logging hinbekommen.

Siehe [Artikel] Attributbasierte Programmierung und [Artikel] Attribute zur Prüfung von Properties verwenden

herbivore

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Soll ich es Dir hier präsentieren, falls ich es doch hinbekommen sollte?

Aber bisher sieht es so aus, als hättest Du Recht.
Wie (fast) immer halt. 😉

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

Soll ich es Dir hier präsentieren, falls ich es doch hinbekommen sollte?

klar!

herbivore

S
8.746 Beiträge seit 2005
vor 17 Jahren

Aspektorientierte Programmierung.

http://csharp-source.net/open-source/aspect-oriented-frameworks

Allerdings laufen die i.d.R. nicht über Attribute, sondern über Konfigurationsfiles (ist auch praktisch, weil Logging im Code zu verankern ja irgendwie genau das ist, was man nicht will; und dazu gehört ja auch, ob man überhaupt loggen will, soll ja ggf. schnell umschaltbar sein). Man definiert dort sogenannten Interceptoren. Die Klassen, die den Logging-Code (oder anderen) enthalten heissen Crosscuts.

Etwas anders als die hier angedachte Lösung funktioniert AspectDNG (http://aspectdng.tigris.org/). Hier wird aber der CrossCut per Attribut mit dem Namen der Methode gefüttert, die zu intercepten ist. Also genau umgedreht.

Performance reicht von grauenhaft bis zu "ähnlich wie handgeschriebener Code". Letzteres erfordert aber immer Codemanipulation auf MSIL-Ebene.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Ich denke auf Basis eines Codeausschnittes wie diesem hier


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace TestDebug
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            MethodInfo info = this.GetType().GetMethod("Test");
            MethodBase methodBase = MethodBuilder.GetCurrentMethod();
            //TypeBuilder builder = methodBase.;

            AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();
            AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll", true);
            TypeBuilder typeBuilder = moduleBuilder.DefineType("TestDebug.Form1", TypeAttributes.AutoClass | TypeAttributes.Public | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, typeof(object), new Type[] { typeof(void) });
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Test",MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final,typeof(void),new Type[]{typeof(void)});
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
            ILGenerator methodILGenerator = methodBuilder.GetILGenerator();

            DynamicMethod dynMeth = new DynamicMethod("Test", typeof(void),null,GetType());
            dynMeth.GetILGenerator().EmitCall(OpCodes.Call,GetType().GetMethod("Test"), null);
        }

        public void Test()
        {
            MessageBox.Show("Hallo");
        }
    }
}

der natürlich noch nicht funktioniert,
sollte es möglich sein, so etwas zu schaffen.

Ich weiß natürlich noch nicht, ob es da ein unüberwindbares Hinernis dazwischen geben könnte,
aber wenn man es schafft, nur für eine Methode einen Wrapper zu machen, dann sollte es möglich sein.

d.h. ungefähre Vorgehensweise:

Am Anfang Assembly durchgehen: Methode im IL-Code neu generieren mit neuem Aufruf, z.B. vorhandene Methode kapseln.
Dann aufrufen. Fertig!!!

nur das sind im kleinen ausgeführt ganz schön viele einzelne Schritte.

Wobei ich auch nicht weiss, ist es möglich eine Assembly zur Laufzeit so zu verändern, dass statt Aufruf von Methode A eine Methode A' generiert wird (Wrapper), die ihrerseits A aufruft?

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

quasi das Gleiche hatte ich auch schon überlegt. Allerdings bin ich bei der Überlegung stecken geblieben, weil ich eben keine einfache Möglichkeit gefunden habe, eine Assembly als Syntaxbaum einzulesen, bis runter zu den Statements bzw. Statementbestandteilen zu traversieren, gezielt an einigen wenigen Stellen zu modifizieren und wieder zu schreiben.

Teile davon gehen einfach, z.B. das Einlesen und das Traversieren bis zur Deklarationsebene (also normales Reflection), aber das ganze Verfahren mit allen Teilen scheint mir leider sehr, sehr aufwändig. Wenn du aber eine Lösung findest, bin ich sehr interessiert. Das können man nämlich nicht nur für Attribute verwenden, sondern für jegliche Instrumentierung des Code, z.B. für Pfadtests.

Wenn man eine solches Verfahren hätte, denke ich nicht, dass es dann zur Laufzeit stattfinden müsste. Ich würde dieses Verfahren eher als Teil des Build-Processes sehen.

herbivore

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Hast Du schon mal weiter in diese Richtung geforscht? @herbivore.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo herbivore,

nein, die Gründe dafür habe ich ja schon angegeben. 🙂

herbivore

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Da ich immer mehr in die Richtung brauchen werde, taste ich mich langsam ran.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

4.207 Beiträge seit 2003
vor 17 Jahren

Spring .NET kann so was ... Stichwort AOP (Aspektorientierte Programmierung)

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

@Golo: Das ist uns klar (ich denke ich kann da auch für Herbivore das Wort mit ergreifen)

Wir möchten aber solche Mechanismen im "normalen" Visual.Net Compiler nutzen können.
Deshalb ist es wichtig, wie er schon sagte, einen Weg zu finden, die Methoden zu traversieren und die Assembly dann - wie oben beschrieben - wieder zurückzuschreiben.

Sprich:

Gesucht wird eine Möglichkeit um z.B. die Methode X aus Klasse A, so zur Laufzeit/Buildprozess umzuschreiben, dass eine Methode X' dann X aufruft.
So können eigene Algorithmen usw. zwischen dem Aufrufen von X' und X eingepflanzt werden. Möglich wäre dann damit z.B. auch sowas:


[Log("Test")] //Automatischen Logeintrag beim Aufrufen dieser methode
public void TestMethode()
{
}

und dies würde nur geschehen, weil z.B. (1 Möglichkeit):


public void TestMethodeStrich()
{
       CDebugLog.WriteLine("Log eintrag vorher");
       TestMethode();
       CDebugLog.WriteLine("Log eintrag nachher");
}

automatisch erzeugt würde.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

noch lieber als die X und X' Variante wäre es mir, wenn man den Body von X einfach ändern oder ergänzen könnte.

herbivore

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

@Herbivore: Für die X' X Möglichkeit sehe ich schon so (gaaannz) langsam eine Lösung

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Original von dr4g0n76
Wir möchten aber solche Mechanismen im "normalen" Visual.Net Compiler nutzen können.

Fast alle AOP-Geschichten arbeiten mit dem normalen Compiler. Entweder sie gehen den Weg, der dir vorschwebt (IL-Code-Manipulation), oder es wird die Profiling-API genutzt. Schade nur, dass selbst mit C# 3.0 keine AOP-Features kommen werden.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Svenson: Ja, das ist mir bewusst. Habe mich dazu leider falsch ausgedrückt.

Ich wollte ja einen etwas anderen Weg gehen als von Herbivore angedacht,
nämlich den, dass ja die X' von X - Methode zur Laufzeit geschrieben wird, eben alls - wie du es sagst - IL-Code-Manipulation. Aber das eben auch zur Laufzeit.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

stimmt und ich würde es stattdessen gerne zur Compilezeit haben, u.a. deshalb, um das aufwändige Reflection zur Laufzeit zu sparen. Hintergrund ist eine performante Lösung für [Artikel] Attribute zur Prüfung von Properties verwenden

herbivore

D
386 Beiträge seit 2007
vor 17 Jahren

Ohne Detailverstaendnis: Wuerde Mono.Cecil [1] helfen? Der Entwickler der Library ist recht umgaenglich und die Library sollte auch mit .Net funktionieren..

[1] http://www.mono-project.com/Cecil

Pound for pound, plutonium is about as toxic as caffeine when eaten.

S
709 Beiträge seit 2005
vor 17 Jahren

Hallo zusammen!

Ihr wollt also den erzeugten IL Code nachträglich bearbeiten um die Log Attribute durch einen Methodenaufruf zu ersetzen? (Hab den Thread nur überflogen) Eventuell kann NRefactory helfen.

vg,
Simon

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Also, inzwischen habe ich herausgefunden, dass es 3 Methoden geben müsste:

  1. Profiling-Callbacks (wie auch schon von Svenson genannt), hier muss aber der komplette Profiler so wie es aussieht in C++ oder einer anderen Sprache vorliegen.
    C# geht meines Erachtens nicht.

  2. Abstract IL von Microsoft benutzen (mache gerade Experimente damit). Müsste garantiert funktionieren.

  3. Mit Reflection und IL-Code-Generierung zur Laufzeit (wäre mir das liebste).

EDIT:

  1. es geht auch nachträglich am IL-Code

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Das sollte der Schritt zu einer Lösung nach Methode 3 sein:


   DynamicMethod dm = new DynamicMethod("Calculate'", typeof(void), Type.EmptyTypes, typeof(CCalc), false);
            ILGenerator ilGenerator = dm.GetILGenerator();
            MethodInfo miCalc = m_Calc.GetType().GetMethod("Calculate");
            MethodInfo miLogMessage = m_Calc.GetType().GetMethod("LogMessage");

            ilGenerator.EmitCall(OpCodes.Call, miLogMessage, null);
            ilGenerator.EmitCall(OpCodes.Call, miCalc, null);
            ilGenerator.EmitCall(OpCodes.Call, miLogMessage, null);
            ilGenerator.Emit(OpCodes.Ret);

            MethodBody t = miCalc.GetMethodBody();
            byte[] b = t.GetILAsByteArray();
            DynamicILInfo ilInfo = dm.GetDynamicILInfo();
            ilInfo.SetCode(b, 20);
            miCalc.Invoke(m_Calc,null);

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

und hier noch die Testklasse dazu:


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication7
{
    class CCalc
    {
        public int Sum(int n1, int n2)
        {
            return n1 + n2;
        }

        public static void Calculate()
        {
            LogMessage();
            MessageBox.Show("Calculate");
        }

        public static void LogMessage()
        {
            MessageBox.Show("Nachricht");
        }
    }
}


Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Ich habs jetzt für eine Methode hinbekommen.

Quasi die aus A mach A' und ruf A auf

also aus


public void A()
{
    ... Tue was...
}

wird


public void A'()
{
   A()
   .... Tue was...
   A()
}

Es geht.
Weitere Fortschritte werde ich erst posten, wenn ich mich besser damit auskenne oder jemand Fragen hat.

Damit wäre jetzt auch möglich, dass A einen EventHandler initialisiert und somit ist also möglich, was im Betreff steht.

Geil, oder?! 😉

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

F
10.010 Beiträge seit 2004
vor 17 Jahren

Nur bevor Du so viel Arbeit da rein steckst ( ist sicher interessant und nützlich ),
such doch mal nach "Castle dynamicproxy" 😉

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

@FZelle und andere: danke an die Hinweise, ich bin noch am recherchieren der ganzen Links und was ich selber gefunden habe.

Bei einem bin ich momentan jedoch steckengeblieben, habe im ganzen Internet noch(!) kein einziges Beispiel dazu gefunden, nur Implementierungsauszüge dieser Klasse, die mir bisher noch nicht allzuviel verraten bzgl. woher ich den Methodenzeiger bekomme


System.Reflection.Emit.MethodRental  	 //Provides a fast way to swap method body implementation given a method of a class.

und mit der statischen Methode "MethodSwapBody" dieser Klasse kann (soweit der Name vermuten läßt)

eine Methode wohl zur Laufzeit ausgetauscht werden.


            byte[] aByteSignatureMethod = this.GetType().GetMethod().GetMethodBody().GetILAsByteArray();

            DynamicILInfo d = new DynamicILInfo();
            d.GetTokenFor(aByteSignatureMethod);
            MethodRental.SwapMethodBody(this.GetType(), nToken,new IntPtr(pointerToMethod),0,0);

Das Beispiel würde wohl funktionieren, aber ich weiß nicht, wie ich für den new IntPtr(pointerToMethod) den PointerToMethod ermitteln soll.

Weiss einer von euch wie das geht?

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Vielleicht ist das richtiger, läßt sich zunmindest übersetzen:


            byte[] aByteSignatureMethod = this.GetType().GetMethod("ChooseFile").GetMethodBody().GetILAsByteArray();

            DynamicILInfo d = new DynamicMethod("Test",typeof(void),null,this.GetType(),false).GetDynamicILInfo();
            int nToken = d.GetTokenFor(aByteSignatureMethod);

            unsafe
            {

                fixed (byte* PtrBuffer = aByteSignatureMethod)
                {
                    MethodRental.SwapMethodBody(this.GetType(), nToken, new IntPtr(PtrBuffer), aByteSignatureMethod.Length, 0);
                }
            }

Ok, Compiler sagt, dass es nur in einem dynamischen Context erlaubt sei.
geht also nicht.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.891 Beiträge seit 2004
vor 17 Jahren

Original von dr4g0n76
Wir möchten aber solche Mechanismen im "normalen" Visual.Net Compiler nutzen können.
Gesucht wird eine Möglichkeit um z.B. die Methode X aus Klasse A, so zur Laufzeit/Buildprozess umzuschreiben, dass eine Methode X' dann X aufruft.
So können eigene Algorithmen usw. zwischen dem Aufrufen von X' und X eingepflanzt werden.

Kleiner Einwurf von mir dazu (da ich mich in letzter Zeit mit AOP beschäftigt habe): Eigentlich kann das, was du möchstest, Rapier-Loom.NET schon. Standardbeispiel für AOP ist Logging 😁 Und in RL kannst du einzelne Aspekte als Attribute an die Zielklassen schreiben. Mehtoden ersetzen bzw. neue hinzufügen geht damit alles.
Und da das ganze so gemacht wird, wie du das andenkst (beim Instanziieren wird dynamisch ein Proxy mittels TypeBuilder und ILGenerator erzeugt).

Gruß
dN!3L

P.S.: Ich hoffe, ich bekomme bald meinen Artikel zu AOP hin, da stünden dann Details zu RL drin.

Edit:

Bei einem bin ich momentan jedoch steckengeblieben, habe im ganzen Internet noch(!) kein einziges Beispiel dazu gefunden... Mal GoogleCodeSearch probiert? (letzter Eintrag) http://www.google.com/codesearch?hl=de&lr=&q=MethodRental+lang%3Ac%23&btnG=Suche

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

hallo dN!3L.

Ein Artikel zu AOP wäre hier drin sicherlich auch mal interessant. da bin ich schon mal gespannt.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Anbei eine Lösung die funktioniert.

Was wird dabei gemacht?

Nun, Class1 ist von Interceptor abgeleitet, Interceptor wiederum von ContextBoundObject, dass es uns dann ermöglicht sogenannte MessageSinks zu benutzen, diesen gesamten Mechanismus machen wir uns dann zunutze um Logdateien zu schreiben. Was natürlich nur 1 der Anwendungsmöglichkeiten ist.


   [AttributeSpy("Interceptor")]
    class Interceptor : ContextBoundObject
    {
    }

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

871 Beiträge seit 2005
vor 17 Jahren

Hallo dr4g0n76,

ein interessantes Thema muss ich sagen! Hab bei Codeproject so was ähnliches gefunden, vielleicht kennst Du es noch nicht:

Klick mich

Macht im Prinzip das was Du willst (Loggen von aufgerufenen Methoden welche mit einem Attribut markiert sind)

Grüsse, Egon

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

@egrath: Doch, hab ich schon abgegrast. 😉 Allerdings erst hinterher.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

IMHO der beste Ansatz ist:

Erweitern Sie Ihre Assemblies nach dem Kompilieren

denn dann können wir so ziemlich alles machen,

  • IL-Code traversieren, um einen IL-Baum aufzubauen
  • über .line Zeile:Spalte `d:\Pfad\SourceDateiname´ kann dann sogar dem Compiler die "richtige" (neue bzw. veränderte) Source-Code-Version untergejubelt werden.

Was ist damit möglich?

z.B. automatische Persistenz von Objekten,
AOP-Implementierungen (aspektorientierte Programmierung)
automatische Trace oder Debug-Informationsgenerierung

Wenn dazu dann die Tools ILDasm und ASM benutzt werden, wie der Autor es vorschlägt, wird einem schon sehr viel Arbeit abgenommen.

Den Rest (die Traversierung, Codegeneration und Rekompilierung und weiteres)
muss man aber trotzdem "alleine" machen.

Ausserdem hat man den Vorteil, dass alles schon nach dem Build-Prozess besteht.
Keine Reflection in diesem Sinne.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 17 Jahren

Hier ein erster kleiner Gehversuch in diese Richtung:

wir entwickeln uns eine Klasse, die die verschiedenen Methoden aus einem IL-File herauszieht.
Um dies zu vereinfachen kann dieses Programm aber nur mit einer Assembly und einer Klasse arbeiten.

Die Klasse Method stellt die Methode dar.
Sowohl der Name, der Modifier als auch die Signatur (Methodenrumpf) werden ausgelesen.


using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Rewrite
{
    class Method
    {
        List<string> m_aLine = new List<string>();
        List<LineElem> m_aLineElem = new List<LineElem>();

        string m_sSignature = string.Empty;
        string m_sName = string.Empty;

        string m_sModifier = string.Empty;

        bool m_bIsEntryPoint = false;

        public Method()
        {
        }

        public string Signature
        {
            get { return this.m_sSignature; }
        }

        public string Modifier
        {
            get { return this.m_sModifier; }
        }

        public string Name
        {
            get { return this.m_sName; }
        }

        public int ReadToEnd(string[] asLine,int _nIndex)
        {
            this.m_sSignature = asLine[_nIndex].Trim();
            if (!this.m_sSignature.Contains("cil managed"))
            {
                this.m_sSignature += asLine[_nIndex + 1];
            }
            this.m_sModifier = this.m_sSignature.Split(' ')[1];
            string[] asElem = this.m_sSignature.Split('(')[0].Split(' ');
            this.m_sName = asElem[asElem.Length - 1];

            do
            {
                string sLine = asLine[_nIndex].Trim();
                if (sLine.StartsWith("IL_"))
                {
                    string[] asLineElem = sLine.Split(':');
                    string sLabel = asLineElem[0].Trim();
                    string sInstruction = asLineElem[1].Trim();
                    this.m_aLineElem.Add(new LineElem(sLabel, sInstruction));
                }
                this.Lines.Add(asLine[_nIndex]);
            } while(!asLine[_nIndex++].Trim().StartsWith("}"));
            return _nIndex;
        }

        public int FindStackLine()
        {
            int nCount =0 ;
            foreach (string s in this.m_aLine)
            {
                if (s.Contains(".maxstack"))
                {
                    return nCount;
                }
                nCount++;
            }
            return -1;
        }

        public List<string> Lines
        {
            get { return this.m_aLine; }
        }

        public List<LineElem> Code
        {
            get { return this.m_aLineElem; }
        }

        public override string ToString()
        {
            return string.Join(Environment.NewLine,(string[])this.Lines.ToArray());
        }
    }
}


und dieser kleine Abschnitt reicht dann aus, um in jede Methode automatisch einen Aufruf einzufügen, der den Namen der Methode ausgibt:

Was wird hier gemacht?
Die Methoden werden bestimmt und an der Stelle, ob


     static void Main(string[] args)
        {
            StreamReader reader = new StreamReader(@"c:\test.il");
            StreamWriter writer = new StreamWriter(@"c:\test2.il");
            string[] asLines = new Regex(Environment.NewLine).Split(reader.ReadToEnd());

            Method method = null;
            List<Method> aMethod = new List<Method>();
            for (int i = 0; i < asLines.Length;i++)
            {
                string sTemp = asLines[i].Trim();
                if (sTemp.StartsWith(".method"))
                {
                    method = new Method();
                    i = method.ReadToEnd(asLines, i);
                    int nStackLine = method.FindStackLine();
                    if (nStackLine != -1)
                    {
                        method.Lines.Insert(nStackLine+1, "ldstr \"" + method.Name + "\"");
                        method.Lines.Insert(nStackLine+2, "call void [mscorlib] System.Console::WriteLine(string)");
                    }
                    WriteLine(writer,method.Lines[0]);
                    WriteLine(writer, method.Lines[1]);
                    aMethod.Add(method);
                    WriteLine(writer, method.ToString());       
                }
                else
                {
                    i++;
                }
            }
            Console.ReadKey();
        }

        public static void WriteLine(StreamWriter writer, string sLine)
        {
            Console.WriteLine(sLine);
            writer.WriteLine(sLine);
        }

In der Konsolenausgabe seht ihr was der Code macht.

Wenn ihr jetzt noch den ILDasm entsprechend ins Visual Studio einbindet, dann könnt ihr für das aktuelle Projekt den IL-Code generieren lassen und mit diesem Code-ausschnitt automatisch erweitern lassen.

Der Rewriter (bei mir heißt das kleine Programm zu dem der Code gehört so) kann ist dann Teil des Buildprozesses.

Es gilt dann noch das ganze zu automatisieren.

Bei mir geht das schon teilweise.

Jetzt müssen wir nur noch sehen, wie wir den Code für ein Event bekommen.
Dann ist das Problem gelöst.

dazu müssen wir dann nur statt den oberen beiden Zeilen


ldstr methodName 
call void [mscorlib] System.Console::WriteLine(string)

diese hier einfügen:


ldfld      class [mscorlib]System.EventHandler ConsoleApplication1.Program::Call
ldarg.0
newobj     instance void [mscorlib]System.EventArgs::.ctor()
callvirt   instance void [mscorlib]System.EventHandler::Invoke(object,
                                                                           class [mscorlib]System.EventArgs)

und in den Konstruktor der Klasse:


stfld      class [mscorlib]System.EventHandler ConsoleApplication1.Program::Call
ldarg.0

und schon bekommen wir bei jedem Methodenaufruf ein event.

Achtung zwei Sachen fehlen natürlich noch: der Event-Handler muss angelegt werden und es sollte jeweils überprüft, werden ob der EventHandler != null ist.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.