Alpha release på IRC robot

by mslot 4. maj 2012 17:29

Jeg har i længere tid sat og rodet med at lave en IRC robot. Jeg sidder selv næsten aldrig på dette chatnetværk, men jeg så alligevel en sjov udfordring i at skrive en robot. Som alle andre udviklere som laver fritidsprojekter, så opsatte jeg nogle mærkelige constraints på mig selv:

  1. Robotten skal være modulær, så den kan udvides med custom commands
  2. Robottens custom commands skal kunne opdateres på runtime, uden at der rebootes
  3. Der skal kunne tilføjes custom commands på runtime uden der rebootes
  4. Robotten skal kunne ponge tilbage uden at halte for andre queries

Med disse krav har jeg været ude i mange sjove hjørner og virkelig dykket ned i bla:

  1. Locking af shared memory
  2. Multithreading
  3. Compile time optimizations

Jeg har også læst op på IRC RFC'en (RFC 1459), så jeg kunne lave en parser. Jeg har forsøgt at lave en form for parser som er nem at udvide. Jeg er ikke lige helt sikker på hvor meget (og hvor godt) arkitektur der er i denne parser, men jeg har forsøgt at lave bundle logikken af parsingen så jeg ikke fik en (eller flere) store switches og if-sætninger, som er svær at udvide. For jeg ved der vil komme udvidelser, da jeg kun har skrevet en parser som lige netop kan det jeg skal bruge på nuværende tidspunkt. Dog har jeg skrevet unit tests til parseren, så jeg nemt kan skifte den og se om den stadig virker. De forskellige tests er ikke særlig dækkende, endnu, men det regner jeg med kommer hen ad vejen.

Hvis du vil hente koden og lege med, og eventuel finde nogle fejl, så kan du hente klone robotten her, https://bitbucket.org/msl0t/ircsharp.

Tags:

C#

Tænk før du paster

by mslot 25. marts 2012 21:57

Der findes mange eksempler rundt omkring på nettet, som beskriver hvordan man skal opbygge en enumerator. En af dem ser sådan her ud:

using System;

using System.Collections;

class sample : IEnumerable
{
    int[] arr = { 10, 11, 12, 13, 14 };

    class myenumerator : IEnumerator
    {
        int c;
        sample s;

        public myenumerator(sample ss)
        {
            c = -1;
            s = ss;

        }

        public object Current
        {
            get
            {
                return s.arr[c];

            }
        }

        public bool MoveNext()
        {
            if (c < s.arr.Length - 1)
            {
                 c++;
                return true;

            }
            else
                return false;

        }

        public void Reset()
        {
            c = -1;
        }

    }

    public IEnumerator GetEnumerator()
    {
        return new myenumerator(this);
    }

}

Koden er noget som jeg har fundet på nettet, så jeg har ikke redigeret det. Koden er ikke pæn, men manden som har lavet den har strikket det som et kodeeksempel, og er derfor fin nok i mine øjne. Det er altså ikke produktionskode vi har at tale om. Det jeg synes er galt med denne enumerator er at man nemt kan skrive en kodestump som gør sådan her:

            foreach (int i in myCollectionThatUsesTheFaultyEnumerator)
            {
                myCollectionThatUsesTheFaultyEnumerator.Add(1);
                Console.WriteLine(i);
            }

Her vil man gå i et undeligt loop, da enumeratoren tillader at der bliver tilføjet objekter under gennemløbet. Dette er ikke godt hvis koden er skrevet til et API som stilles til rådighed overfor en uvidende tredje mand. Dog kan det i mine øjne godt gå at lave, hvis man kun skal bruge koden selv til noget intern, da man derved nok skal bruge koden rigtig. Dog er det stadig grimt synes jeg.

Et andet eksempel er:

public class MyCollection : IEnumerable<string>
{
    private List<string> _values;
    public MyCollection(List<string> values) { _values = new List<string>(values); }

    public IEnumerator<string> GetEnumerator()
    {
        return new TestOverrideEnumerator(_values);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

    { return GetEnumerator(); }

    protected class TestOverrideEnumerator : IEnumerator<string>
    {
        private List<string> _values;
        private int _currentIndex;
        private int _count;

        public TestOverrideEnumerator(List<string> values)
        {
            _values = values; Reset();
        }

        public string Current { get { return _values[_currentIndex]; } }
        public void Dispose() { }
        object System.Collections.IEnumerator.Current { get { return Current; } }

        public bool MoveNext()
        {
            _currentIndex++; return _currentIndex < _values.Count;
        }
        public void Reset()
        {
            _currentIndex = -1;
        }
    }
}

Som jeg også har fundet på nettet. Det dårlige ved denne enumerator er at den kopierer hele listen. Kopieringen kan godt være krævende hvis man har en meget stor liste af objekter som man vil løbe igennem. Dog kan jeg godt se det gode i denne form for håndtering hvis listen er lille. Det har ikke den store betydning hvis koden bliver brugt i en enkelt trådet applikation, faktisk ville det i mine øjne være lidt synd at bruge koden i en enkelt trådet applikation, da det faktisk vil være lidt af en flaskehals, hvis man har en liste med rigtig mange objekter. Dog har man undgået lidt bøvl hvis man vil bruge enumeratoren i en multitrådet applikation, hvis man ser bort fra den overhead der vil være, hvis der er tale om en stor liste af objekter.

Måden jeg vil gør det på er følgende:

public class MyCollection : IEnumerable<string>
{
    private List<string> _values;
    public MyCollection(List<string> values)
    {
        _values = new List<string>(values);
    }

    public IEnumerator<string> GetEnumerator()
    {
        return new TestOverrideEnumerator(_values, _values.Count);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    protected class TestOverrideEnumerator : IEnumerator<string>
    {
        private List<string> _values;
        private int _currentIndex;
        private int _count;

        public TestOverrideEnumerator(List<string> values, int count)
        {
            _count = count;
            _values = new List<string>(values);
            Reset();
        }

        public string Current
        {
            get
            {
                if (_count == _values.Count)
                {
                    return _values[_currentIndex];
                }
                else
                {
                    throw new Exception("list has been updated");
                }
            }
        }

        public void Dispose()
        {

        }

        object System.Collections.IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public bool MoveNext()
        {
            _currentIndex++;
            return _currentIndex < _values.Count;
        }

        public void Reset()
        {
            _currentIndex = -1;
        }

    }
}

Her sørger jeg for at gemme count på den liste som jeg vil løbe igennem, før jeg kalder MoveNext. Hver gang jeg skal hente Current, laves der et tjek på om den gemte count er lig med den nuværende count. Hvis den ikke er det, og det er den ikke hvis man add'er et objekt under gennemløb, som i det første eksempel, så skal der kastes en Exception. Jeg kaster bare en exception af typen Exception. Dette skal dog lige rettes til, hvis du vælger at kopierer koden og bruge det selv. Vælger mam at kopierer min kode så skal Reset nok også kaldes i Dispose metoden. Dette har jeg dog undladt her.

Jeg synes at vi her ser et klart eksempel på at man ikke bare lige skal tage noget kode fra nettet og genbruge i sit eget projekt, uden lige at overveje om koden faktisk gør det man gerne vil have den til at gøre. Se bare på disse 3 stumper kode, som overordnet set gør det samme, men som har nogle bitte små implementeringsdetaljer som gør at de er gode i nogle henseender, og mindre gode i andre. Eksempel 1 og 2 kan nemt bruges, bare det bliver gjort med omtanke. Faktisk vil jeg bruge eksempel 2 hvis jeg havde gang i noget multitrådet samarbejde over en meget lille liste af objekter, hvor trådene skal have "et nutidsbillede" af listen, at arbejde på, da listen nemt kan opdateres under gennemløbet med enumeratoren. Så er det nemmere at tage en kopi af listen, og det letter også låsningen, da man derved kun behøver at låse når der tages en kopi. Dog er dette valg meget situationsbestemt :) Som alt andet er når talen falder på multitrådet programmering.

Læg mærke til at ingen af de tre enumerator vist her, tager højde for at en anden tråd måske også kunne finde på at arbejde på kollektionen af objekter. Jeg er ved at dykke lidt mere ned i denne problemstilling, så der kommer nok et blogindlæg om dette i næste måned, april 2012.

EDIT 27-03-2012 kl 14:26

Utroligt nok er jeg blevet gjort opmærksom på en fejl fra en læser. Jeg har lavet en rookie mistake i mit eget eksempel. Dette er rettet til så listen som enumeratoren, og MyCollection, tager med ind i constructoren rent faktisk gør det som jeg siger den gør, nemlig at lave en kopi. Jeg burde tænke lidt mere før jeg paster. Eller bare lære at programmere. 

Tags:

.NET | C# | enumeration

Forskellig konfiguration for uploading af store filer på IIS6 og IIS7

by mslot 24. marts 2012 20:18

Der er mange som har stået i den situaion at de vil uploade en fil som er lidt større end det default tilladte af IIS’en. For at ændre det tilladte er der mange som rammer ind i IIS6 konfigurationen. Dette er også godt nok hvis man kører på en sådan, dog bruger stadig flere og flere IIS7, og IIS7 har en helt anden måde at håndtere dette på. Googler man kan man nemt ramme ind i IIS6 konfigurationen som siger at man skal:

    <system.web> 
        <httpRuntime executionTimeout="300" maxRequestLength="512000" /> 
    </system.web>

Det ser egentlig meget rigtig ud. Men dette er forkert hvis man sidder på en IIS7. Desværre er IIS7 konfigurationen stadig lidt skjult i google. Det var den ihvertfald sidst jeg søgte, hvilket vil sige ca. 2 måneder før dette post. Overstående vil ikke virke hvis du kører IIS7. For at konfigurerer det i IIS7 skal du bruge:

<system.webServer> 
   <security > 
      <requestFiltering> 
          <requestLimits maxAllowedContentLength="1024000000" /> 
      </requestFiltering> 
   </security> 
</system.webServer>

Læg mærke til at vi er et helt andet sted, nemlig i system.webServer Smiley Dette skal smides ind i web.config. Jeg har sat maxAllowedContentLength til max. Sørg selv for at sætte denne. For at være helt sikker på at virkningen slår igennem, skal du også ind i applicationHost.config, liggende her: C:\Windows\System32\inetsrv\config\applicationHost.config, på en 32 bit maskine, og sørge for at overrideModeDefault er sat til Allow i denne section:

<section name="requestFiltering" overrideModeDefault="Allow" /> 

Det kan nemt blive hårrivende hvis man ikke lige ved dette.

Tags:

C# | asp.net

Unit test og code coverage

by mslot 25. februar 2012 13:04

Jeg tror faktisk ikke der er særlig mange der følger med, men jeg vil nu alligevel skrive et indlæg om hvordan jeg er begyndt at lave unit tests. Hvad en unit test egentlig er og hvornår man skal lave den vil jeg ikke beskrive i denne artikel. Denne diskussion er hør en helt anden boldgade til. Jeg vil derimod i dette indlæg beskrive hvad for værktøjer jeg bruger og hvorfor jeg gør mig brug af netop disse.

Unittest værktøjet

Jeg gør ikke brug af den indbyggede unit test som bliver shippet med Visual Studio. Dette gør jeg af en simpel grund:

  1. Platforms afhængighed.  Jeg synes den er integreret for tæt med Visual Studio. Som jeg kan se det så skal man faktisk have en Visual Studio installeret for at kunne køre sine tests.

Jeg ved godt at det kan lade sig gøre at køre MSTest “uden” Visual Studio, hvor man kopierer de nødvendige filer over. Dog ser jeg mere dette som et “hack” end en egentlig løsning, da man let kan ramme ind i en som skal køre de opstillede tests, som ikke har filerne, og derfor også Visual Studio.

TIl unit tests bruger jeg NUnit. Den er veldokumenteret, forholdsvis kompakt og let at gå til. Og ja, den er platformsuafhængig.

Kørsel af tests

Jeg har altid haft hooket en visuel runner til Visual Studio som kunne køre mine NUnit tests når jeg “befalede” det. Jeg har prøvet utallige visuelle runners, og kan generelt lide dem alle sammen. De kan det de skal, nemlig at vise om en test gik godt, eller om en test gik dårligt. Alt sammen på min kommando, ved tryk på en knap. Dette har jeg faktisk været godt tilfreds med, dog er jeg på det seneste begyndt at have en lidt anden tilgang til netop denne del: kørsel af Unit tests. Hvilket faktisk var grundlaget for hele dette indlæg. Jeg er begyndt at køre mine tests lidt anderledes, hvilket har gjort mig mere produktiv, uden jeg har tænkt vitterligt meget over det.

Som “runner” bruger jeg i dag, NCrunch. Et CI værktøj som kan installeres som plugin til Visual Studio. Denne runner kræver lidt opsætning, som dog ikke er særlig svært. Man skal fortælle runneren hvordan den skal køre alle tests. Om alle skal køres på en gang eller om der er nogen der skal udelukkes, om alle tests sekventiel eller om den må køre nogen parallel. Den vigtigste egenskab er, efter min øjne, at den kører mens man udvikler, og giver en status på vejen. Og det er selve denne status som har fået mig til at værdsætte runneren så meget. Runneren fortæller selvfølgelig om en test gik godt eller dårlig, men udover det så fortælles der også, med nogle få farvede markers:

  1. hvad for linjer der fejlede
  2. om der er nogle linjer som ikke er dækket ind af en test
  3. hvad for linjer som bestod
  4. hvad for linjer som, i runnerens øjne, har taget “rigtig” lang tid om at køre.

Det fede i mine øjne er at NCrunch giver dig denne “development time” code coverage Smiley

Jeg vil i de efterfølgende afsnit give en lille indsigt hvordan dette ser ud rent visuelt, så I alle kan se HVOR smart det er. Afslutningsvis vil jeg give et lille fif til hvordan man kan gøre brug af NUnits console runner, til kickstarte en debugsession, hvis der er nogle linjer der fejler. Det er netop denne kombination jeg elsker.

NCrunch

NCrunch er et plugin til Visual Studio som giver dig en automatiskeret CI værktøj, som kan køre din unit tests mens du skriver. NCrunch kan findes her. Selve opsætningen er forklaret meget godt på hjemmesiden, og den er lige til at gå til, så den vil jeg ikke dykke mere ned i her.

Måden jeg bygger mine projekter op på er fint vist på billedet her. Jeg har forskellige lag som hver gør del af arbejdet. Jeg har:projekt

  1. et DAL lag som står for at hente data fra en given kilde og smide dem i nogle objekter
  2. et model lag, som repræsenterer forretningslogikken
  3. et repository lag som står for at samle DAL lagene (som oftest er der dog kun en datakilde, så mange gange er dette lag overflødig).
  4. et gui lag som præsenterer data for slutbrugeren på en pæn måde

I dette projekt har jeg dog ikke implementeret DAL, repository og gui lagene. Jeg har dog disse lag med for syns skyld, og for at give et billede af hvordan jeg bygger et projekt op.

det 5. projekt er ikke et lag i systemet. Dette lag, her kaldet Test, er et test projekt som indeholder alle mine unittests. Og det er disse tests som NCrunch kører bag om ryggen på dig.

Jeg har på github.com oprettet projektet som I kan hente og afprøve NCrunch på. Der er ikke noget der virker endnu, men har man installeret og enabled NCrunch kan man se hvordan NCrunch arbejder. Jeg har sat NCrunch til at kører alle tests om ryggen på mig, og man kan se hvordan NCrunch giver dig næsten real time opdatering af hvad dine tests dækker i model koden. Billedet til venstre viser to metoder i Chef.cs. GetWorkers er repæsenteret i en fejlende unit test, og er derfor markeret med rødt, mens RemoveWorkers slet ikke er repræsenteret i en unit test og derfor markeret med sort. Da denne code coverage er på linje niveau kan man komme ud for at en metode både melder rød og sort. Dette er smart for man kan derved se hvad for linjer i en given metode som slet ikke er dækket ind. Går alt godt vil man se en grøn plet. Den grønne plet betyder success Smiley NCrunch kan faktisk også godt tage tid på dine linjer, og den melder tilbage hvis der er noget som tager for lang tid. Denne tilbagemelding kommer i form af en grøn plet med en lille gul klat i midten. Yderst dejligt at se hvor der er mulighed for en optimering!!

Kigger man på selve unit testen kan man se at et lille rødt kryds fortæller at det er her testen fejler. NCrunch er meget god til at give tilbagemeldinger rent visuelt. Praktisk hvis man sidder med noget avanceret forretningslogik, og ikke lige har lyst til at læse tekst. NCrunch giver også en eventuel exception tilbage i den tilhørende konsol ved at klikke på den fejlende test, eller ved at føre musen over krydset. Klikker man på krydset kan man eventuelt se det forventede og det faktiske resultat. Dejligt.

Kombinationen

Jeg har for nylig erfaret at man kan få NCrunch til at starte en debug session ved fejl, dog har jeg ikke haft tid til at udforske dette endnu. Det jeg selv plejer at gøre er at jeg har min solution sat på til hele tiden at starte mit test projekt. Mit test projekt indeholder en test CLI NUnit runner:

   class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            string[] my_args = { Assembly.GetExecutingAssembly().Location };

            int returnCode = NUnit.ConsoleRunner.Runner.Main(my_args);

            if (returnCode != 0)
                Console.Beep();
        }
    }

Opdager jeg en rød plet i noget kode kan jeg hitte F5 og runneren starter. Runeren vil her køre og tilsidst løbe ind i fejlen og breake. Jeg kan nu inspicere og udforske heapen og stakken, og eventuelt sætte et break point og genstarte hvis jeg vil steppe lidt. Dog vil runneren stoppe ved den første fejl den løber ind i, og dette er ikke altid den fejl som du er ved at rette. Det skal man lige have in mente hvis man vil bruge denne fremgangsmåde.

Konklusion

Jeg har faktisk ikke noget dårligt at sige om dette stykke værktøj, andet end at den nok højest sandsynlig kommer til at koste penge i fremtiden, hvilket jeg må affinde mig med og hoste op når den tid kommer. Det er et lækkert stykke software, hvor den visuelle code coverage præsentation er prikken over i’et som gør at man virkelig føler at man får noget brugbart ud af at bruge det.

Tags:

.NET | C#

Mono.Cecil del 2–inject af funktionskald

by mslot 18. december 2011 18:49

Så fik jeg tid til at skrive lidt om Mono.Cecil igen.  Sidst skrev jeg hvordan man lavede en reference til en anden assembly, og skrev den “modificerede” assembly, med den nye reference, ned på disken. Dog er der ikke meget ved kun at lave referencer til andre assemblies, dette bliver lidt kedelig i længden. Jeg vil i dette indlæg forklare hvordan man kan kalde metoderne i den nylige referencerede assembly.

For at kunne lave et sådanne hack generelt, hvor injecter metode kald ind i andre assemblies, kræves der at man har lidt styr på de mere lavtstående dele af C#, nemlig IL kode. Dog er det minimalt hvor meget der bliver brugt i dette indlæg, så det er lige til overkomme, men vil man lave noget mere avanceret så vil jeg anbefale at man nærstudere MSIL lidt mere. Hvis du vil læse mere om MSIL opkoderne som bliver brugt længere nede, før du læser videre, så kan du tage et kig på

  1. call
  2. ldstr

Disse to opkoder bliver injectet via Mono.Cecils ILProcessor.  Jeg har ikke kunne finde noget dokumentation på hvordan man bruger ILProcessoren, da Mono.Cecils dokumentation er skrevet til en ældre version end den seneste version af Mono.Cecil. Så den eneste dokumentation er her. Hvilket vil sige er lidt af en by i Rusland, hvis man gerne vil have lidt inspiration til hvad det kan bruges til Smiley. Dog kan jeg gå så langt og sige at man nemt kan få skabt sig et overblik via førnævnte FAQ, og lidt tid i tænkeboksen.

Jeg vil bygge lidt videre på min sidste artikel, hvor jeg har udvidet koden lidt,

            Mono.Cecil.TypeDefinition type = lib.MainModule.Types.Where(x => x.Name == "Writer").Single();
            Mono.Cecil.MethodDefinition method = type.Methods.Where(x => x.Name == "WriteLine").Single();

            Mono.Cecil.MethodReference reference = source.MainModule.Import(method);

            Mono.Cecil.TypeDefinition globalType = source.MainModule.Types.Where(x => x.Name == "Global").Single();
            Mono.Cecil.MethodDefinition applicaionErrorMethod = globalType.Methods.Where(x => x.Name == "Application_Error").Single();

            Mono.Cecil.Cil.ILProcessor processor = applicaionErrorMethod.Body.GetILProcessor();
            Mono.Cecil.Cil.Instruction call = processor.Create(Mono.Cecil.Cil.OpCodes.Call, reference);
            Mono.Cecil.Cil.Instruction str = processor.Create(Mono.Cecil.Cil.OpCodes.Ldstr,"h");

            processor.InsertBefore(applicaionErrorMethod.Body.Instructions[0], str);
            processor.InsertAfter(applicaionErrorMethod.Body.Instructions[0], call);

Koden er faktisk lige til, dog har jeg brugt lang tid på at skrive den, da jeg ikke synes at der er meget dokumentation omkring på nettet, som fortæller hvordan man injecter et funktionskald. De fleste af de små snippets/spørgsmål/svar som flyder omkring på nettet, beskriver hvordan man gør dette med de ældre versioner af Mono.Cecil, som gør brug af en CilWorker til at injecte med. Denne metode er blevet byttet ud med førnævnte IlProcessor. Dette er faktisk den største forskel. Dog ligger der ikke et samlet eksempel på nettet på hvordan man laver et inject til en ekstern defineret metode. Jeg vil faktisk våge og påstå at min er det eneste fulde eksempel!! Hvilket jeg synes er lidt ærgeligt, da det er lidt sjovt at sidde og nørkle med.

Det der tog længst tid for mig at regne ud, var hvordan man brugte den injectede assembly reference. For man skulle ved første øjekast tro at man “bare” importerede den fundne metode og efterfølgende brugte den fundne metode i selve ILProcessoren:

            ...
            Mono.Cecil.MethodDefinition method = type.Methods.Where(x => x.Name == "WriteLine").Single();

            source.MainModule.Import(method);
            ...
            Mono.Cecil.Cil.ILProcessor processor = applicaionErrorMethod.Body.GetILProcessor();
            Mono.Cecil.Cil.Instruction call = processor.Create(Mono.Cecil.Cil.OpCodes.Call, method);
            Mono.Cecil.Cil.Instruction str = processor.Create(Mono.Cecil.Cil.OpCodes.Ldstr,"h");

            processor.InsertBefore(applicaionErrorMethod.Body.Instructions[0], str);
            processor.InsertAfter(applicaionErrorMethod.Body.Instructions[0], call);

Dog skal man bruge den returnerede “Mono.Cecil.MethodReference reference = …” videre i forløbet, ellers ved Mono.Cecil ikke hvad for assembly metoden referer til. Det jeg gjorde i første artikel, var altså bare en side af injectningen, hvor man fandt og tilknyttede referencen til den assembly som man vil modificere. Dog skal man bruge den returnerede reference, for at kompileren ikke brokker sig. Dejligt med compiletime tjek!

Det der sker i koden er at man først finder den metode som man vil injecte

            Mono.Cecil.TypeDefinition type = lib.MainModule.Types.Where(x => x.Name == "Writer").Single();
            Mono.Cecil.MethodDefinition method = type.Methods.Where(x => x.Name == "WriteLine").Single();

            Mono.Cecil.MethodReference reference = source.MainModule.Import(method);

Her finder jeg altså metoden “WriteLine” i den statiske klasse “Writer”. Denne MetodeDefinition bruger jeg til at  Importere hele den tilhørende assembly, og den returnerede MethodReference, gemmer jeg. Denne reference skal jeg bruge når jeg rent faktisk skal injecten.

Injecten har jeg besluttet mig for at lave i web applikationens Global.Application_Error metode. At finde denne metode er simpel

            Mono.Cecil.TypeDefinition globalType = source.MainModule.Types.Where(x => x.Name == "Global").Single();
            Mono.Cecil.MethodDefinition applicaionErrorMethod = globalType.Methods.Where(x => x.Name == "Application_Error").Single();

og dejlig letlæselig også takket være Linq.

Jeg har nu

  1. importeret den assembly som indeholder den metode som jeg vil kalde
  2. fundet metoden som injecten skal ske i

Nu kan jeg begynde at injecte:

            Mono.Cecil.Cil.ILProcessor processor = applicaionErrorMethod.Body.GetILProcessor();
            Mono.Cecil.Cil.Instruction call = processor.Create(Mono.Cecil.Cil.OpCodes.Call, reference);
            Mono.Cecil.Cil.Instruction str = processor.Create(Mono.Cecil.Cil.OpCodes.Ldstr,"h");

            processor.InsertBefore(applicaionErrorMethod.Body.Instructions[0], str);
            processor.InsertAfter(applicaionErrorMethod.Body.Instructions[0], call);

For at injecte fx kald i eksisterende kode, skal man først have fat i den kontekst som man vil injecte i. Vi vil gerne injecte noget kode Application_Error metoden, derfor skal vi have fat i kroppen af denne metode. Kroppen har Mono.Cecil pænt pakket ind til os, og de har endda gjort det nemt for os at injecte kode ind med ILProcessor’en. Jeg frygtede, før jeg gik igang, at man selv skulle til at flytte rundt på IL opkoder, og derved nemt ødelægge det eksisterende, men dette behøves man ikke med ILProcessoren. Den hjælper os nemlig. Både med at injecte, men også med at oprette MSIL instruktioner. Med Create metoden laver man instruktioner som skal injectes, og med InsertBefore, og InsertAfter, injecter man. Dette gør at koden er meget nemmere at læse. Man kan derfor fokusere mere på at skrive god og letlæseligt kode, og mindre på at forstå, og huske hvad MSIL kode der skal konstrueres.

Selve oprettelsen af det IL kode der skal injectes sker altså gennem ILProcessorens Create metode.

            Mono.Cecil.Cil.Instruction call = processor.Create(Mono.Cecil.Cil.OpCodes.Call, reference);
            Mono.Cecil.Cil.Instruction str = processor.Create(Mono.Cecil.Cil.OpCodes.Ldstr,"h");

Læg mærke til at jeg bruger WriteLine metodens reference, og ikke selve dens MethodDefinition. Jeg opretter to instruktioner. En som sørger for at kalde metoden, og en som sørger for at lægge metodens paramteren på stakken. For at injecte kaldet indsætter jeg disse to instruktioner i Application_Error’ens krop. Jeg vil gerne indsætte kaldet først i metoden

            processor.InsertBefore(applicaionErrorMethod.Body.Instructions[0], str);
            processor.InsertAfter(applicaionErrorMethod.Body.Instructions[0], call);

Her smider jeg strengen, “h”, på stakken og laver et fald til min WriteLine metode.

Rækkefølgen af indsættelsen er ikke hel ligegyldig. Hvis du ikke er inde i hvordan metodekald sker i de “mere primitive” sprog, så skal man, før man kalder metoden, smide alle metodens parametre på stakken, og derefter kalde metoden. Call koden sørger derefter for at tage de argumenter som den skal bruge fra stakken og oprette en ny stak, som den bruger til at udføre sin handling. Efter endt kald rykkes dens  kaldstak ned, og en eventuel returværdi smides på kalderens stak, som kalderen kan vælge at bruge i dens videre forløb.

Tilsidst skal vi have gemt den modificerede assembly

            source.MainModule.Write("WebApplicationModified.dll");

Der er nu gemt en modificeret assembly med Writer.WriteLine(“h”) kaldet i Application_Error metoden. Du kan se dette ved at inspicere den gemte dll med ILSpy. For at sikre sig at man ikke ødelagt og lavet en korrupt dll, kan man køre PEVerify på den. PEVerify tjekker om alt er som det skal være i den nyoprettede assembly. Har man injectet noget galt og usammenhængede MSIL kode, skal PEVerify nok fortælle dig det. Dette kunne fx være at man havde glemt at smide en parameter på stakken før man kalder en given metode.

Det var alt for nu.

Tags:

.NET | C# | Mono.Cecil

Hvordan henter man XML’en ud i en string fra XmlTextWriter?

by mslot 14. december 2011 15:36
Svaret er meget simpelt:
   1:  using (System.IO.StringWriter stringWriter = new System.IO.StringWriter())
   2:              {
   3:                  using (XmlTextWriter doc = new XmlTextWriter(stringWriter))
   4:                  {
   5:                      string s = stringWriter.ToString();
   6:                  }
   7:              }

Man giver bare en StringWriter til XmlTextWriter’en. Derefter kan man kalde ToString på StringWriter’en, og man har nu en string repræsentation af det xml som er blevet skrevet med XmlTextWriter’en.

Tags:

.NET | C# | XML

Mono.Cecil

by mslot 8. december 2011 23:12

Mono.Cecil er smart. Det er et bibliotek som kan bruges til manipulere managed dll filer efter de er kompileret. Det er en meget god værktøj hvis du fx står med en dll, som du ikke kan debugge, da du ikke source filen, eller måske ikke kan installere windbg på serveren. Her kan man hente dll’en ned, og smide trace information ind de steder som skal kigges efter i sømmene.

Jeg er lige begyndt at kigge på Mono.Cecil, men jeg er allerede benovet over dens styrke. Jeg har fx lavet en lille WriterLibrary, som jeg gerne vil bruge i en anden dll, kaldet WebApplication.dll. Min lille tankespil går ud på at WebApplication.dll er min, ikke source kode defineret, ikke windbg-able, applikation, som jeg gerne vil trace. WriterLibrary er min “store” debug og trace bibliotek som jeg gerne vil bruge til at kigge WebApplication efter i sømmene.

Der er ikke meget dokumentation omkring Mono.Cecil på nettet, dog er det meget simpelt at komme igang med. For at komme igang vil jeg springe lige ud i det. Jeg har lavet et konsol program som ser sådan her ud

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CecilTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Mono.Cecil.AssemblyDefinition source =
                Mono.Cecil.AssemblyDefinition.ReadAssembly(@"C:sti\til\\WebApplication.dll");

            Mono.Cecil.AssemblyDefinition lib =
                Mono.Cecil.AssemblyDefinition.ReadAssembly(@"C:\sti\til\WriterLibrary.dll");

            source.MainModule.Import(lib.MainModule.Types[1]);
            source.MainModule.Write("WebApplicationModified.dll");
        }
    }
}

For at alt dette virker skal vi også have fat i Mono.Cecil, men dette er let. Man henter Mono.Cecil med NuGet. Åbn Package Manager Console og kør “Install-Package Mono.Cecil”. Kommandoen henter de dll’er der skal til for man kan gøre brug af Mono.Cecil. Min WriterLibrary er her blot en simpel class library som ser sådan her ud

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WriterLibrary
{
    public class Writer
    {
        public void WriteLine(string s)
        {
            Console.WriteLine("wrote:" + s);
        }
    }
}

Når man laver en Import på en hvilken som helst type fra WriterLibrary til WebApplication dll’en, så sørger Mono.Cecil også for at lave en reference til WriterLibrary dll’en. Du kan selv tjekke efter ved at inspicere WebApplication dll’en før konsol applikationen er kørt, og efter. Da man skal betale for Reflector, vil jeg råde alle til at bruge IlSpy. Google den, og hent programmet. Den kan ikke alt det som Reflector kan, men den er gratis, og man kan kigge på dll’er. Meget praktisk Smiley

Ved at inspicere WebApplication dll’en efter konsol applikationen er kørt, kan man se at referencen er tilføjet. Meget smart. Man kan nu bruge WriterLibrary til at lave trace på WebApplication. Jeg har endnu ikke lavet noget injection, men det er noget jeg vil skrive om efterfølgende, da jeg selv lige skal lære at bruge Mono.Cecil. Min vision med dette eksempel er at jeg vil prøve at injecte WriteLine funktionskaldet ind i WebApplication.dll’ens Global.Application_Error, da jeg ved at WebApplication gør brug af en Global.asax.

Jeg vil opdatere denne artikel med links efterfølgende når jeg skriver mere om hvordan man injecter funktionskald.

Håber at der er nogen der kan bruge mit lille eksempel til noget.

Tags:

C# | Mono.Cecil | .NET

ASP.NET Panel og jquery

by mslot 28. november 2011 19:05

En panel i asp.net bliver expandet til en div. Vil du have fat i denne div via id’et, kan du ikke bare tilgå den ved at skrive

$('#PanelId')

da en id’et på panelet bliver sammensat af flere forskellige faktorer, fx. dens parent osv. For at bruge overstående er man derfor nød til at kende navngivningsreglerne, eller se på kildekoden som IIS’en smider tilbage i hovedet på browseren, dog kan man med jquery gøre noget meget smart. Man kan nemlig søge DOM’en efter div id’er indeholdende en tekst, meget lig SQL’s LIKE statement.

Har vi defineret en panel sådan her:

<asp:Panel ID="panelInfoBox" Style="border: 1px solid black;" runat="server"></asp:Panel>

Kan vi få fat i fx teksten, ved at bruge lidt jquery magi:

$("div[id$=panelInfoBox]").text("hello world");

Her sætter vi teksten i panelet. Dette er meget nemmere at læse, og virker hvis man omrokerer hele html’en.

Tags:

indspark | jquery | asp.net

Googlemaps

by mslot 24. november 2011 14:33

Lat og lon til googlemaps, skal separeres med et punktum, og ikke et komma. Dette har nok højest sandsynlig noget at gøre med hvordan et decimal tal repræsenteres i Danmark i forhold til “resten” af verden (bare rolig der er andre lande som også bruger komma). Læs mere om decimal tal her. Er du interesseret i at læse mere om radix punktets historie, så kan det gøres her.

Tags:

googlemaps | indspark

Blog

by mslot 19. november 2011 19:16

Jeg har i kort tid haft mit eget hjemmesyede system anvendt her på domænet, men jeg har dog besluttet mig for at skifte, da jeg ikke gad lave al funktionaliteten selv. Dette kan ses som dovnskab, dog har jeg valgt at fokusere på at bruge min fritid på at udgive indlæg, og ikke udvikle (selvom jeg ææælsker at udvikle). Jeg har valgt blogengine.net, da den har alt det jeg skal bruge:

  1. Man kan udgive via Livewriter
  2. Den understøtter reCaptcha
  3. Både Livewriter og blogengine.net understøtte hilight af kode
  4. Man kan sammenkæde artikler

På den tekniske side kan der nævnes at selve motoren er baseret på .NET 4.0 som man kan udvide via extensions. Det synes Martin Slot fanma er najs!!

Jeg vil via denne blog udgive artikler omkring debugging, C# og T-SQL. Mit mål er at udgive 5 små indspark, og 1 dybdegående artikel om måneden.

Jeg vil holde sproget på dansk.

Tags:

Om

Martin Slot. C#. Javascript og T-SQL.

Indlæg