Laden...
Avatar #avatar-2617.png
Benutzerbeschreibung

Forenbeiträge von zommi Ingesamt 1.361 Beiträge

26.05.2011 - 19:32 Uhr

Hallo UZI,

nochmal die Fehlermeldung studieren:> Fehlermeldung:

PInvokeStackImbalance wurde erkannt.
Message: Ein Aufruf an die PInvoke-Funktion "Test!Test.Form1::NvCplGetThermalSettings" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein. Überprüfen Sie, ob die **++Aufrufkonvention ++**und die **++Parameter ++**der PInvoke-Signatur mit der nicht verwalteten Zielsignatur übereinstimmen.

die Parameter sind richtig, aber die Aufrufkonvention ist noch die falsche.

Laut der NVidia-Signatur muss die Methode mittels cdecl aufgerufen werden. Jedoch ist

Zitat von: msdn (System.Runtime.InteropServices.CallingConvention)
The default value for the CallingConvention field is Winapi, which in turn defaults to StdCall convention.

Und da Cdecl != StdCall, musst du das speziell angeben:

[DllImport("nvcpl.dll", CallingConvention=CallingConvention.Cdecl)]

beste Grüße
zommi

22.05.2011 - 13:10 Uhr

Hi,

vollständigkeitshalber noch das, woran Flotse wohl eher dachte:

    [StructLayout(LayoutKind.Explicit)]
    struct SomeStruct
    {
        [FieldOffset(0)]
        public IntPtrContainer A;

        [FieldOffset(0)]
        public CharacterContainer B;
    }

    
    [StructLayout(LayoutKind.Sequential)]
    class IntPtrContainer
    {
        public IntPtr IntPtr;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    class CharacterContainer
    {
        public SingleCharacter SingleCharacter; 
    }
    
    class SingleCharacter
    {
        public char Char;
    }

    private static void WriteToBuffer(IntPtr buffer, string ShouldBe)
    {
        for (int i = 0; i < ShouldBe.Length; i++)
        {
            WriteCharacter(buffer + i * sizeof(char), ShouldBe[i]);
        }
        WriteCharacter(buffer + ShouldBe.Length * sizeof(char), '\u0000');
    }

    private static void WriteCharacter(IntPtr buffer, char character)
    {
        SomeStruct s = new SomeStruct();
        s.A = new IntPtrContainer() {IntPtr = buffer - IntPtr.Size}; // skip SyncBlock of object
        s.B.SingleCharacter.Char = character;
    }

abermals beste Grüße
zommi

22.05.2011 - 12:51 Uhr

Hi Floste,

du hast zwar wahrscheinlich an etwas komplett anderes gedacht 😉
Aber hier meine Lösung, die statt Marshal.Copy einfach Bitmap und SetPixel nutzt:

    [StructLayout(LayoutKind.Explicit)]
    struct SomeStruct
    {
        //TODO
    }

    //TODO

    private static void WriteToBuffer(IntPtr buffer, string ShouldBe)
    {
        byte[] shouldBeBytes = System.Text.Encoding.Unicode.GetBytes(ShouldBe);
        int pixels = shouldBeBytes.Length / 4 + 1; // +1 for some trailing EndOfString-Zeroes
        int[] colorValues = new int[pixels];
        Buffer.BlockCopy(shouldBeBytes, 0, colorValues, 0, shouldBeBytes.Length);

        using (var b = new System.Drawing.Bitmap(pixels, 1, pixels * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, buffer))
        {
            for (int i = 0; i < pixels; i++)
            {
                b.SetPixel(i, 0, System.Drawing.Color.FromArgb(colorValues[i]));
            }
        }
    }

beste Grüße
zommi

17.05.2011 - 13:05 Uhr

Hi mirrowwinger,

Wie schon erwähnt wurde, gibt es kein inline assembler für c#.
Aber niemand hindert dich daran mit Microsofts asm-Compiler "MASM" (im Windows sdk enthalten, vielleicht sogar bei visual studio selbst, bin mir grad nicht sicher) eigene assembler Programme zu schreiben.

Ja du kannst sogar asm Files in dein visual studio Projekt reinhauen, für diese Dateien nen speziellen build-Schritt definieren, ne dll draus bauen lassen und aus c# die Funktionalität mittels PInvoke aufrufen...

Was im übrigen auch cool ist: .Net-IL-assembler Programme selber schreiben. Ist ähnlich, etwas "hochsprachennäher" und komfortabler (da stack maschine statt Register). Das kompilierst du mit ILAsm und heraus kommen fertige .Net assemblies. Auch sehr zu empfehlen, wenn man etwas tieferen wissen über .Net erlangen möchte.

Beste grüße
Zommi

16.05.2011 - 14:21 Uhr

Hi Ayke,

dafür kannst und solltest du Mono.Cecil nutzen.

beste Grüße
zommi

11.05.2011 - 23:21 Uhr

Hallo,

XmlSerializer.Serialize hat zahlreiche Überladungen. Bei denen, die einen Stream erwarten, kannst du einen MemoryStream angeben. Ja du kannst sogar die Überladung mit TextWriter nutzen und den StringWriter übergeben und hast am Ende direkt einen String, ...

beste Grüße
zommi

06.05.2011 - 10:58 Uhr

Hi thomers,
1.Wie genau erzeugst du denn "zwei instanzen aber vom gleichen Bild" ? 1.Wenn es zwei separat erzeugte bilder sind, können durchaus alle Pixel identisch sein, aber die Bytes des gesamten zugewiesenen Byte-Arrays anders.

Dein Code ist ziemlich "hässlich" 😉 Das solltest du auf jeden Fall überarbeiten. Zu viel in einer Methode, zu unterschiedliches Abstraktionsniveau, seltsames Interface...

Aber zu dem potentiellen Problem von Punkt 2. Die Zeilen eines Bildes sind im Speicher 4-byte-aligned. Da du anscheinend nur RGB-Bilder vergleichen kannst (3 bytes pro pixel, die du hard codiert nutzt), kommst du genau in die Problematik, dass ohne ein Padding die Byte-Länge einer Bild-Zeile gerade nicht durch 4, sondern nur durch 3 teilbar ist.

Deshalb wird jede Bildzeile um "sinnlose" Bytes ergänzt, sodass sie durch 4 teilbar ist. Diese gesamte Zeilenbreite nennt .NET hier Stride. Und Stride ist eben durch 4 teilbar, aber muss nicht mit 3*Width übereinstimmen!

Insbesondere ist nicht definiert, was in diesen Padding-Bytes drinne steht. Und das kann von Bild zu Bild irgendwelche zufälligen Bytes sein, daher ein potentieller Unterschied zwischen den Bildern.
Denn du berücksichtigst nicht, dass die die Padding-Bytes nicht vergleichen darfst !

Du darfst also nicht stumpfsinnig einfach in dreier Schritten durch das gesamte Array laufen, sondern nur zeilenweise!

Nochmal zur Veranschaulichung hier das Speicherabbild eines RGB-Bildes der Größe 2*2. (Da die Bytebreite hier nur 6 wäre, wird jede Zeiel mit 2 Bytes auf 8 erweitert)


[pre]  B  G  R  B  G  R  X  X
  B  G  R  B  G  R  X  X
[/pre]

Beim deinem durchlaufen:


[pre] [B  G  R][B  G  R][X  X
  B][G  R  B][G  R  X] X
[/pre]

Und damit vergleichst du dann auch die X-Padding-Bytes. Und generell ist das auch ziemlich strange 😉

Das wäre zumindest eine Fehlerquelle.

beste Grüße
zommi

01.05.2011 - 16:44 Uhr

Der übliche Workflow wird immer so beschrieben wie auf dem Diagramm auf der Seite: Agile-Data - What is TDD

Jup, so kenne ich das auch. Und es ist der letzte Punkt ganz unten in der FlussGrafik: "Development stops", der mich stutzig macht. Als ob die Entwicklung jemals stoppt. Ich muss die Klasse garantiert noch einmal anfassn. Eben weil sie in ihrer jetzigen Form vielleicht nicht in eine überarbeitete Architektur passt. Sie behält ihre Aufgabe, behält vielleicht sogar die Verantwortlichkeit - muss sich aber dennoch anders verhalten - anders interagieren. Wo steig ich dann ein? Wo setz ich an? Darauf scheint mir TDD keine Antwort zu geben.

beste Grüße
zommi

01.05.2011 - 16:40 Uhr

Hallo FZelle, danke für die Antwort.

Ich verteidige dennoch die anfängliche Enum-Lösung: Nimm dir ein Kugel-Eis mit drei Geschmacksrichtungen, die du im System abbilden musst. Erst einmal unterscheiden die sich nicht in ihrem "Verhalten" - es sind nur "dumme" Daten-Eis-Objekten. Wie bilde ich die Geschmacksrichtungen ab? Ganz pragmatisch als Eigenschaft des KugelEises - und das mit einem Enum. Daran ist zunächst nichts unvernünftig und auch nichts schlecht testbar. (Dennoch waren unsere Tests selbst schlecht geschrieben, da sie eben direkt davon abhängig waren, dass das Kugeleis enum-basiert ist)

Nun kam später noch die Anforderung an eine Fertig-Eis-Am-Stilsorte. Die ist nun einmal wirklich kein Kugeleis, daher der Wunsch nach einer Vererbungshierarchie. Und in diesem Zuge haben wir dann auch die drei Eissorten in eigene Klassen aufgeteilt und ihnen etwas spezifischere Eigenschaften (wie kako-gehalt, etc. gegeben) gegeben, was die Vererbungshierarchie rechtfertigt.

Aber ich möchte dennoch zum "großen" Problem zurückkommen:

Sondern TDD bedeutet das du deine Architektur dahingehend anpasst, das Du eine vernünftig testbare Architektur hast, die sich unter normalen Umständen nicht durch hinzufügen von kleinen Features aus dem Gleichgewicht bringen lässt.

Das klingt nach einer stabilen Architektur. Aber dennoch ist keine Architektur unerschütterlich. Wenn man vorher genau wüsste, wo die Reise hingeht, wie die Zielarchitektur aussieht, die alles ermöglicht, dann hätte man wohl keine Probleme. Aber manchmal kommt eine so radikale Idee (Kundenwunsch) auf, der ein Umdenken, ein Ändern der Architektur notwendig macht! In diesem Sinne wird die Architektur ganz natürlich aus dem Gleichgewicht gebracht, davor kann man sich nicht schützen, nicht durch längeres Planen, nicht durch Clean-Code, und auch nicht durch TDD. Und ganz im Sinne der Agilität heiße ich doch auch solche Änderungen stets willkommen.

Und so nützlich Unit-Tests für das Refactoring im kleinen sind - sie schaffen die Freiheit es ohne Angst zu tun - so hinderlich scheinen sie für mich beim Refactoring im Großen:*Dank "Rename Method" in meiner IDE kann das Umbenennen von Methoden ohne Aufwand geschene und so mache ich oft meine Methodennamen klarer und besserer. Das Auslagern von Verantwortlichkeiten in eine andere Klasse (Extract class) ist ebenso sinnvoll, aber die "alten" Unit-Tests muss ich manuell editieren, anpassen, verschieben. Und Copy-Paste ist mein einziges Tool - ein böses Tool, dass ich sonst zu vermeiden versuche. Das ist so aufwendig, dass man sich das zweimal überlegt, ob man dafür gerade Zeit hat. *Ich führe irgendwo den Double-Dispatch Mechanismus ein. Eventuell ein Visitor-Pattern, weil die nach und nach hinzugekommenen Klassen diese "Architektur" begünstigen. Aber auch hier müssen zig Unit-Tests angepasst werden. Dann lass ich doch das Refactoring lieber ganz sein, und lass die alte "unpassende" Architektur. *...

Also wie mache ich meine Tests "Architecture-Change-Aware" ?
Uncle Bob sagt in "Clean Code" nur lapidar

Test code is just as important as production code. It is not a second-class citizen. It requires thought, design, and care. It must be kept as clean as production code.

Das klingt vernünftig. Aber wie gestalte ich meine Test-Architektur, sodass meine Tests nicht durch System-Architektur-Änderungen zusammenbricht.
Meine Erfahrung mit Unit-Tests bisher ist nur wie folgt:
Wenn mein System ein Haus ist, dann sind Unit-Tests der Mörtel, der in allen Ritzen liegt. Erst durch ihn habe ich die Lücken der "Ungewissheit" ausgefüllt, erst durch sie weiß ich, dass das Haus stabil ist und sich so verhält wie es soll, dass es stehen bleibt, egal von wo der Wind kommt. Bevor ein neuer Stein hinzukommt, wird erst der Mörtel genutzt, nicht einfach Stein auf blanken Stein, das bräche früher oder später zusammen - stattdessen eben Häuslebauen nach TDD-Manier.
Doch ist der Mörtel einmal fest, ist es aufwändiger eine Wand zu versetzen, als hätte ich sie einfach lose gebaut! So muss ich erst alles aus den Ritzen kratzen, Steine versetzen und dann wieder neu verkleben. An manchen Steinen lass ich den alten Mörtel noch etwas dran, der ist ja noch gut, aber ist danach in allen Ritzen wirklich wieder alles dicht? Wer hilft mir dabei? Gibt es passendes Werkzeug dafür? Aber mit den puren Händen ist das Wand-Verschieben verdammt aufwendig! Klar hätte ich vorher schon die Wände perfekt planen können - meine Schuld, dass ich "fehlgeplant" hab. Aber lieber schonmal irgendein Haus als gar keins! Und dass ins Kinderzimmer nun Zwillinge statt einem einzigen neuen Baby müssen, war eben nicht abzusehen...
Aber noch als Klarstellung: Ich würde kein Haus ohne Mörtel bauen - das wäre dumm, aber das Wändeverschieben ist sooo aufwändig.

Ich hoffe, die Metapher hat gefallen 😉

Und wie macht ihr das? Wie geht ihr damit um? Ich finde es frustrierend. Habt ihr Tools? Tipps, Gedanken, weitere Gegenmeinungen?

beste Grüße
zommi

01.05.2011 - 14:27 Uhr

Hallo an alle,
zur Zeit verzweifle ich etwas am Refactoring eines Systems, das mit zahlreichen Tests abgedeckt ist. Das Refactoring ist relativ umfassend, sodass viele Komponenten indirekt davon betroffen waren und noch schlimmer: all deren Tests!

Vielleicht ist es ein allgemeines Problem, weshalb ich auf eure Hilfe und Ideen hoffe.1.Aber nun zu meinem konkreten (aber etwas abgewandelten Szenario):
Wir haben eine Datenfluss-ähnliche Architektur. Eine Quelle für Daten, einige Senken und viele Filter dazwischen, die ein ganzen Netz ergeben. Die Daten die da durchflossen waren sehr ähnlich, weshalb wir ein Objekt verwendeten, was unterschiedliche "Zustände" haben konnte, oder nennen wir es "Geschmacksrichtungen":

enum Flavour { Vanilla, Chocolate, Strawberry }

class Data
{
	Flavour DataFlavour;
	/* other data */
}

Die einzelnen Filter arbeiten also mit solchen Objekten und tun auch abhängig von der Geschmacksrichtung unterschiedliches. Diese Filter haben natürlich Unit-Tests, nicht zu wenig. Die sahen für einen Filter "MyFilterXY" klassisch so aus:

void TestMyFilterXY()
{
	var chocolateObject = new Data(Flavour.Chocolate);
	// further setup
	
	var outputObject = MyFilterXY.Process(chocolateObject);
	
	Assert.AreEqual(...) // etc.	
}

Nun kam ein neuer Feature Request rein, der einen zusätzlichen Datentyp erforderte. Da dieser zwar grob zu den bisherigen DataObjects passte, aber sich nicht in die bestehende Flavour-Liste einfach integrieren ließ (weil es nicht wie Vanille, Schokolade oder Erbeere ist, sondern mehr wie "Luft") haben wie auf eine Vererbungshierarchie dieser DataObjects umgestellt.


[pre]                           BasicData
                              |
                  ---------------------------
                  |                         |
             FlavouredData               AirData
                  |
       --------------------------
       |          |             |
VanillaData ChocolateData StrawberryData
[/pre]

Die Filter mussten nur wenig bis gar nicht angepasst werden. Gleichzeitig wurde manch Filter aufgeteilt und andere wurden überflüssig.
Die "bloße Implementierung" war in knapp 2 h zu erledigen. Das Anpassen, Neuschreiben, Umschreiben der Tests hat jedoch 8 h gefressen.

Die Filter selbst waren relativ gut gegenüber dem genauen Aussehen dieser Datenobjekte abstrahiert. Aber die Tests, die ja ganz konkrete Szenarion testen wollten, waren natürlich hart abhängig von der konkreten Implementierung unserer Datenobjekte - schon weil sie in jedem Setup Instanzen dieser Objekte erzeugten. Und nun bei einer neuen Vererbungshierarchie ist das natürlich alles obsolet.

Vielleicht wäre es gut gewesen, nicht in jedem Test "manuell" sich solche Datenobjekte zu erzeugen, sondern vermehrt Factories dafür zu nutzen. Wiederverwendbarere Fixtures. Und zwar nicht nur, weil es den Code kürzer macht (das hätte es oft nicht, es waren selten mehr als einzeiler), sondern weil es die direkten Abhängigkeiten reduziert. Dan hätte wahrscheinlich nur eine Anpassung in dieser DataFactory gemacht werden müssen und nicht in zig tausend Unit-Tests.

Was ich mir aus der Geschichte mitnehmen:

Ein Unit-Test für Komponente X darf ausschließlich direkte Abhängigkeiten zu Komponente X haben! Selbst wenn X selber Abhängigkeiten zu Y hat, darf der Unit-Test nicht selbst mit Y arbeiten, schon gar nicht Objekte vom Typ Y selbst instanzieren! Jegliches Setup für die Abhängigkeiten von X muss im Test auf einer astrakteren Ebene geschehen, sodass der Unit-Test von X nicht direkt mit dem Abhängigkeiten von X in Berührung kommt. Hierfür sind immer Factories, Mocks, etc zu nutzen!
Denn sonst müsste der Unit-Test von X angepasst werden, wenn sich Y intern verändert.

Haltet ihr das für sinnvoll, sowieso klar, total überflüssig ?!

  1. Aber auch wenn diese "Regel" obige konkrete Problematik "löst", so glaube ich nicht, dass ich damit das allgemeine Problem vollständig umschiffen kann. Daher noch einmal ein verallgemeinertes Szenario:
    Man baut (mehr oder weniger) test-driven ein System. Das System selbst ist von der Implementation eigentlich schön, Belange sind separiert, Verantwortlichkeiten klar getrennt, etc. Es gibt viele Tests... Nun kommt eine neue Anforderung, die eine begrenzte Architektur-Änderung erfordert. Das lässt sich vielleicht in 2, 3 h umsetzen... aber ohne Beachtung von Tests. Die alle anzupassen, gemäßg der neuen Architektur und der neuen Verantwortlichkeiten zu verschieben, neue zu bauen, alte zu löschen, kostet dann nochmal 8h mehr Zeit und viel viel mehr Aufwand als die eigentliche Implementation... und am Ende, wenn alle Tests wieder laufen fragt man sich, ob wirklich immer noch alles getestet wurde. Oder hat man etwas vergessen? Einen Aspekt der vielleicht verloren gegangen is. Oder hätte ein neu aufkommender nun auch abgedeckt werden müssen?
    Da gibt auch die coverage nur einen Anhaltspunkt, aber keine Sicherheit.

Diese Unsicherheit ist schlimm, dabei sollen Tests einem doch gerade diese Unsicherheit nehmen! Dass das Gesamtsystem irgendwie noch funktioniert, haben die Integrationstests und Systemtests auf einer von der Architektur komplett abstrahierten Ebene relativ gut sichergestellt. Aber die ganzen einzelnen Unit-Tests ...
Schon Folgendes war Frust: Wir mussten das Basisobjekt anpassen. Es gab rund 50 Verwendungen im Produktivcode aber sagen wir mal 500 Verwendnungen im Test-Code (und davon waren nur 10 aus den Unit-Tests für BasicData selbst). Man hatte schon jetzt keine Lust, das Refactoring überhaupt anzugehen.

Man könnte auch alle Tests einfach löschen und jede Komponente frisch nach TDD wieder entwickeln, doch das ist ja nicht Sinn der Sache. Aber bestehenden Test-Code mit zu refaktorisieren (auf architektureller Ebene, nicht einfach nur EtractMethod, etc.) scheint wirklich verdammt schwierig !

Mein Wissen über TDD hilft mir nur bei einem: Dem Neuentwickeln von Komponenten. Aber es scheint mir so, als ob TDD danach keine Antworten liefert, wenn die Komponente "fertig" ist. Ich habe schön im Zyklus gearbeitet, für jede Anforderung erst Test, dann Implementierung, dann den Code schön gemacht (refactored) und so den Zyklus abgearbeitet bist ich fertig war.

Und wenn sich nun später die Anforderungen leicht ändern oder verschieben? Lösche ich manche Tests, verschiebe ich sie zu anderen Komponenten, schreibe ich sie von Grund auf neu? Wie weiß ich, ob ich dabei nichts übersehe...

Und wie geht ihr damit um? Was habt ihr für Erfahrungen? Mache ich irgendwas komplett falsch?
Für jegliche Tipps und Ratschläge bin ich dankbar!

beste Grüße
zommi

25.04.2011 - 18:35 Uhr

das ganze gleich in eine SortedList zu packen

Das würde ich nicht tun. Für jede Aufgabe gibt es eine passende Datenstruktur. Und für das Ermitteln der Häufigkeiten ist diese Datenstruktur bei dir nun einmal das Dictionary. Daran würde ich nichts rütteln.

Wenn du in einem zweiten, nachgelagerten Schritt die Daten anders aufbereitet benötigst, dann konvertiere sie, sortiere sie, was auch immer.

Und hab keine Angst vor dem Overhead durch das einmalige Konvertieren. Ausschlaggebend sollte die Häufigkeitsanalyse und deren zeitliche Komplexität sein!

beste Grüße
zommi

25.04.2011 - 12:54 Uhr

Hi Bunnychecker,

Jetzt will ich das Dictionary noch nach den Values sortieren, aber ich kriege es nicht hin.

Zur Häufigkeitsanalyse behalte das normale Dictionary bei. Wenn du die Häufigkeiten ausgeben willst, erzeuge dir aus dem Dictionary einfach ein SortedDictionary:

var sortedDict = new SortedDictionary(unsortedDict);

Wenn du nun darüber iterierst, ist es sortiert:

foreach( KeyValuePair<string, string> kvp in sortedDict )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

//Edit: Oh, ich sehe gerade, dass du nach den Values sortieren willst - dann macht obiges natürlich keinen Sinn, entschuldige.

beste Grüße
zommi

25.04.2011 - 02:24 Uhr

Hi,

das Dictionary ist schon perfekt dafür geeignet. Und was spricht so sehr gegen deine erste Variante? Es ist doch ein durchaus geläufiges Verarbeitungsschema:
Daten komplett einlesen - Daten komplett verarbeiten - Daten komplett wegschreiben.
Du musst doch eh in mehreren Schritten über den Eingabtext iterieren. Einmal zum Aufbauen des Huffman-Codes und dann noch einmal zu Kodieren selbst. Also solange deine txt-Dateien nicht Gigabytes groß werden, bleib bei ReadAllText.

Im übrigen wäre auch das byte-weise Einlesen schlecht. Es sind keine Binärdaten. Nutze dann dafür den StreamReader! Aber bleib einfach bei ReadAllText. Außer du willst wirklich mit beliebig großen Dateien umgehen können. Aber dann bekommst du sowieso weitere Probleme; beispielsweise dass ein uint für die Häufigkeiten nicht ausreicht...

Mit LINQ-Magie reduziert sich deine Schleife auf:

text.GroupBy(letter => letter).ToDictionary(category => category.Key, category => category.Count());

beste Grüße
zommi

24.04.2011 - 13:01 Uhr

ich habs zwar noch nich mit flash-videos probiert.
Aber vielleicht hift dir "Virtual Dub".

beste Grüße
zommi

21.04.2011 - 00:25 Uhr
1337!

😉

Aber chic, dass du einfach Methoden als fremde Instanzmethoden ausgeben kannst, war mir garnich klar... aber wenigstens peverify meckert rum.

Mit dieser Methode fummelst du dann quasi in fremden Objekte rum. Genauer gesagt im übergebenen String an der Stelle, wo bei dir @ulong steht. Und dort ist genau bei einem String (hinter der Länge) der eigentliche Content. Und so überschreibst du dann die ersten 4 Unicode Zeichen. Aus "wtf?!" wird damit "1337!"
Und dank String-Internalisierung wird auch bei späterem "wtf?!" der selbe, manipulierte String, verwendet.
Soweit meine Theorie, jedenfalls kommt richtig raus 😉
Warum ist das GC.KeepAlive nötig? Würde er sonst nicht internalisieren?

beste Grüße und danke für das schöne Rätsel. Ich denk schon über das nächste nach 😃
zommi

//Edit: Arggg... ein µ zu langsam 😉 Dann is Scavanger dran!

18.04.2011 - 20:16 Uhr

Hi,

sind meine ersten Schritte mit Events

Dann empfehle ich auch (und mit Hinblick auf die NullReferenceException) noch den Thread [Lösung] Problem mit EventHandler.

beste Grüße
zommi

16.04.2011 - 18:04 Uhr

Hallo,

je nachdem, ob das Spiel selbst 64bit oder 32bit ist, würde ich dein Program daran anpassen. (weniger fehlerquellen).
Ob das Spiel 32Bit ist, erkennst du daran, ob im TaskManager hinter dem ProzessNamen noch ein "*32" steht, wie bei "Firefox.exe *32" oder so.

Dann stellt bei den Projekteigenschaften deiner C# Anwendung die Plattform auf x64 für 64bit bzw. x86 für 32bit.

Falls du deine C# Anwendung nun für 64Bit kompilieren solltest, musst du ReadProcessMemory neu definieren.
Anstatt "UInt32 size" müsstest du "UInt64 size" nehmen... oder noch besser: "IntPtr size". Denn IntPtr ist automatisch die richtige Größe - sowohl auf x64 als auch x86.

beste Grüße
zommi

16.04.2011 - 17:29 Uhr

Hallo TheCherry,

ich habe noch nicht detailliert in deinen Code geschaut, aber was für ein Windows setzt du ein ? 32Bit oder 64Bit? Was ist das Spiel? Für welchen Modus übersetzt du deine .NET Anwendung?

Dein DllImport von ReadProcessMemory ist nämlich fix auf 32Bit eingeschränkt, da du SIZE_T in UInt32 abbildest.

beste Grüße
zommi

11.04.2011 - 13:13 Uhr

Hi,

das geht mit dem param-Statement. Nur eben nicht in einer Funktion, sondern direkt im Skript.

Da kannst du dann natürlich auch Default-Werte festlegen und auch nen ExceptionWurf reinnehmen, wenn der Parameter "pflicht" ist und du nen ordentlichen Text zurückgeben willst. Beispielsweise:

param([int] $myFirstParam = $(throw "Please tell me a number!"))

Write-Host "You told me $myFirstParam."

beste Grüße
zommi

08.04.2011 - 08:50 Uhr

Hi Marcel064,

ich bin normalerweise in der Welt des C++ unterwegs

Dann zieh ich mal den Vergleich zu den STL-Container-Iteratoren.

Ob ein STL Container dieses Iterator-Konzept unterstützt, siehst du daran, ob die Methode "begin()" angeboten wird.
In C# ist eine Collection iterierbar, wenn sie die Methode "GetEnumerator()" anbietet - dies wird durch das Interface IEnumerable repräsentiert.

In C++ bekommst du dann meist ein privates Iterator-Objekt zurück, auf dem du "++" und den Dereferenzierungsoperator "*" angeboten bekommst.
In C# bekommst du dann einen IEnumerator, auf dem du "MoveNext()" und "Current" aufrufen kannst.
Selbes konzept - selbe Verwendung.

Und um nun nicht selbst mit MoveNext() und Current zu hantieren, gibt es das foreach Schlüsselwort.

Enumerable hingegen (ohne das Interface-I) ist einfach nur eine statische Klasse mit vielen statischen Convenience-Methoden, die du auf alle IEnumerables aufrufen kannst (die aber aus weiteren Komfort-Gründen als Extensions-Methods implementiert sind)

Aber eigentlich solltes du sowas in jedem guten Buch finden, und da solltest du auch rein schauen.

beste Grüße
zommi

Ich habe bewusst keine Trennung zwischen IEnumerable und IEnumerable<T> usw gemacht um es nicht zu verkomplizieren. Nur so viel, Die typisierten Generics bieten Typsicherheit zur Compilezeit, sodass klar ist, welche Elemente der Iterator zurückliefert.

02.04.2011 - 13:28 Uhr

Hallo,

ich kenne solche 0/1-Offset Überlegungen allerdings. Ich kann mich noch an ein C++-Buch Erinnern, in dem verschiedene Datenstrukturen für Matrizen diskutiert wurden. (deren Reihen und Spalten Mathematiker intuitiv ab 1 anfangen zu zählen)

Und dort gab es auch den "Trick" eine Union aus einem Array und einem "length"-feld zu machen, das gerade die Abmessungen des 0ten Array-Elementes hat.
So konnte man "intuitiv" und "effizient" mit einem 1-basierten Zugriff arbeiten und hat keinen Platz verschwendet.
Das Argument, dass Sum,Avg usw nicht korrekt funktionieren stimmt da ja nicht, da c-arrays ja auch nur pointer sind und man somit einfach durch pointerarithmetik diesen funktionen das "Array ab dem 1. Element" übergeben kann.

Naja, alles hacky und tricky. Dennoch gab und gibt es solche Überlegungen.

Aber auch mein Rat an mirus: Gewöhn dich an die 0-basierte Indexierung und vergiss alles andere. Code sollte in erster Linie klar und verständlich sein statt clever und trickreich. Und die Informatik "denkt" größtenteils 0-basiert.

beste Grüße
zommi

01.04.2011 - 20:50 Uhr

Hallo smartie,
Ich sehe das auch so, dass das wohl normale Zeiten sind.

Aber für eine Reihe von zeitlich aufeinander folgenden Bildern eignet sich wahrscheinlich eine videokompression besser als viele standbildkomprimierungen. Nicht nur wegen der besseren Kompressionsrate sondern bestimmt auch wegen der (hoffentlich geringeren) Laufzeit

Videos in c# sind nur schwieriger als Bilder...

Beste grüße
Zommi

01.04.2011 - 11:24 Uhr

Hi,

du könntest auch den IndentedTextWriter nutzen.

beste Grüße
zommi

16.03.2011 - 10:03 Uhr

Hi,

sehr gut! Dann bist du jetzt dran.

//EDIT: Lol aber mit der originalen Assembly funktionierts natürlich nicht. 😃 So war es gedacht 😉
Die normale Assembly über Reflection einfach ansprechen geht nicht so einfach, weil hash-checks auf die entry-assembly drin sind. Den Code aber einfach rauskopieren, kopiert etwas falsches, wegen dem "numberA + numberB" - Hack 😉
Wenn man übrigens genau hinschaut, sieht man dass der Reflector das "+" dazwischen auch grün macht, es also zum Bezeichner gehört anstatt schwarz wie den normalen Additionsoperator ;D

Und ich hab ganz schön lange gebraucht, bis ich ein Programm hab, was mir Wort-Paare ausspuckt, sodass bei dem +- Fehler beides mal sinnvolle Wörter rauskommen - damit man denkt, man sei bereits fertig 😉

Noch eine Frage an Joetempes. Konntest du den Program-Konstruktor mit dem Reflector ansehen? Den hab ich nämlich so manipuliert, dass Reflector bei mir in jeder Ansicht abschmiert 😉

beste Grüße
zommi

16.03.2011 - 03:15 Uhr

Hi Spontifixus,

danke für den Lösungs-Zuspruch und insbesondere Danke für die schöne Aufgabe. Ich kann nun hoffentlich etwas ähnlich schönes bieten:

Im Anhang befindet sich eine kleine .NET-Applikation die einen geheimen Text enthält. Dieser Text ist "verschlüsselt" hinterlegt und wird angezeigt, wenn man das richtige Password eingegeben hast.

[B]Wie lautet der geheime Text?[/B]
Alle Mittel und Wege sind zugelassen - seid kreativ !

 [color]Ich habe ein paar Abwehrmaßnahmen/Hindernisse/Fallstricke eingebaut, sonst macht es ja keinen Spaß ;)[/COLOR] Der verschlüsselte, geheime Text befindet sich in der statischen Variable CryptoGame.Program.CryptedText.

Möge der schnellste gewinnen! 😃

Viel Vergnügen und beste Grüße
zommi

15.03.2011 - 01:15 Uhr

Hi,

auf context.Request gibt es Form, QueryString oder auch kombiniert Params. Da sollte alles bei sein.

beste Grüße
zommi

15.03.2011 - 00:57 Uhr

Hi,

mein erster gescheiterter Ansatz war einfach eine neue Main-Methode zu definieren, (ohne parameter, aber mit int als return typ) in der Hoffnung dass der Compiler irgendeiner Reihenfolge beim Ermitteln der Main-Methoden hat. Dem ist aber nich so, wenn mehrere passen könnten, gibts einfach den Fehler, dass mehrere Einstiegspunkte definiert sind.

Aber der wirkliche Trick besteht im Wörtchen partial.

Also wird die gegebende Klasse einfach als partial markiert und in der Solution die nachfolgende Datei irgendwoe versteckt hinzugefügt:

using System;
using System.Linq;

static partial class Program 
{
  static Program() // Typinitialisier! Ha, ich werd vor der Main gerufen!
  {
    if("MyName".Equals(Environment.GetCommandLineArgs().Last()))
    {
      Main(new string[0]); // Main kann man sogar für den Hack wiederverwenden
      Environment.Exit(0); // uuunnd tschüss
    }	
  }
}

beste Grüße
zommi

13.03.2011 - 14:05 Uhr

Hallo Micke,

der andere Prozess hat einen eigenen Adress-Raum. Daher ist deine Speicheradresse, die du mit GetFunctionPointerForDelegate erhälst, nur in deinem Prozess, aber nicht im fremden gültig!

Du musst eine Funktion aufrufen, die bereits im Ziel-Prozess enthalten ist. Und da die kernel32.dll fast immer gegen gelinkt ist, existiert die Funktion LoadLibrary bereits im Ziel-Prozess. Daher wählt man oft diese.

(Oder aber man muss erst mit VirtualAllocEx und WriteProcessMemory eine sinnvolle Funktion in den fremdem Prozess-Adress-Raum hineinschreiben - aber da ist LoadLibrary einfacher)

beste Grüße
zommi

11.03.2011 - 19:55 Uhr

Hallo Pezor,

du musst die Calling Conventions beachten. Da du C-Exporte verwendest, nutzt der Compiler für deine Methoden die _cdecl _Konvention.

Das DllImport-Attribut hat genau dafür noch ein CallingConvention-Feld, das aber standardmäßig auf WinAPI (aka stdcall) eingstellt ist.,

[DllImport("muuh.dll", CallingConvention=CallingConvention.Cdecl)]

Bei deiner Function-Methode fällt das nicht auf, da sich cdecl und stdcall nur wenn Parameter vorhanden sind unterscheiden. Aber prinzipiell musst du auch dort cdecl einstellen.

beste Grüße
zommi

08.03.2011 - 11:09 Uhr

Hallo HeinzTomato,

der syntaktische Unterschied ist minimal. Es gibt die selben OOP Konzept. Ein sprachlicher Unterschied wären zum Beispiel die Funktionsdelegaten und lambdas in C#, die es in Java (noch) nicht gibt. (für Eventhandling) Dort nimmt man Interfaces und anonyme Klassen.
Oder die expliziten get*/set* Methoden statt properties.
Ist aber alles kein Problem.

Woran du dich wirklich gewöhnen musst, ist das framework. Das ist natürlich anders. Insofern ist es mehr ein umstieg vom .Net framework hin zum Java framework. Angefangen bei System.out.println statt System.Console.WriteLine, oder den getrennten Input- und Output-Streams, die es in .Net nicht gibt bis hin zu den komplett anderen GUI-frameworks.
Aber für vieles gibt es äquivalent. Man muss eben nur anfangs länger suchen und sich in die java-Dokumentation einarbeiten, so wie man sich in die msdn einarbeiten muss.

Insofern ermuntere ich dich aber! Schau dir erst n biss die spezial-Syntax an und ab dann is es nur noch framework Funktionen suchen. Aber stöber auch ruhig etwas, es gibt auch Dinge, die man aus dem .Net framework nicht hat und kennt, sodass manches sogar einfacher wird. (das concurrency framework finde ich nämlich auch gelungen, wobei mit .Net 4.0 und plinq wir auch was sehr geiles haben.

Beste grüße
Zommi

01.03.2011 - 00:12 Uhr

Hallo Floste,

Ich frage mich grade, ob es möglich ist, dass eine klasse mehrere Felder enthält , die den gleichen namen tragen.

Das ist möglich und gültiger IL Code. Ein Feld wird durch Name und Typ identifiziert. Und natürlich ist der Name Bestandteil der Assembly (wie ginge sonst Reflection?)

Anbei mal eine Demo-IL-Anwendung, die das demonstriert:

.assembly extern mscorlib {}
.assembly TestAssembly{}
.module Test.dll

.class public Test
{  
  // drei statische Felder mit dem selben Namen - aber unterschiedlichen Typen
  .field static string MyField
  .field static bool MyField
  .field static int32 MyField
  
  // statischer Konstruktor zum Initialisieren der Feld-Werte
  .method hidebysig specialname rtspecialname static void .cctor()
  {
    ldstr "Hello World"
    stsfld string Test::MyField
    
    ldc.i4 1
    stsfld bool Test::MyField
    
    ldc.i4 12345
    stsfld int32 Test::MyField
    
    ret
  }  

  // Main Methode, die alle Felder Werte ausgibt
  .method public hidebysig static void Main(string[] args)
  {
    .entrypoint  
    
    ldsfld string Test::MyField
    call void [mscorlib]System.Console::WriteLine(string)
    
    ldsfld bool Test::MyField
    call void [mscorlib]System.Console::WriteLine(bool)
    
    ldsfld int32 Test::MyField
    call void [mscorlib]System.Console::WriteLine(int32)
    
    ret
  } 
}

Lässt sich einwandfrei mit ilasm kompilieren, peverify meckert ebenfalls nicht und das kompilierte Programm gibt das richtige aus:

[pre]Hello World
True
12345[/pre]

Der .NET-Reflector (deine Konkurrenz/Vorbild 😉 ) stört sich auch daran nicht; siehe Anhang.

beste Grüße
zommi

28.02.2011 - 18:45 Uhr

Hi

such mal (hier im Forum) nach WriteProcessMemory. Das ist der für dich zentrale WinApi Befehl. Auch bei Codeproject müsstest du einschlägige Artikel finden, die sich genau um solche fremd-Programm-Manipulationen drehen.
Beste grüße
Zommi

23.02.2011 - 10:51 Uhr

@zeitmessung:

Die klasse System.Diagnostics.Stopwatch nutzt intern den highperformancecounter. Es gibt also keinen Grund hier selbst die winapi zu nutzen.

Beste grüße
Zommi

23.02.2011 - 10:45 Uhr

Hi,
Komplett den Zugriff auf die wettervorhersage zu verweigern geht nicht, denn du möchtest ja von außen gerade über Wetter.Vorhersage.Temperatur auf ein member von vorhersage zugreifen.
Auch sehe ich keine Gefahr, sodass eine Zugriffsbeschränkung wenig Sinn macht.

Wenn du verhindern möchtest, dass man außerhalb deiner Bibliothek vorhersage Objekte erzeugt,hast du im gründe zwei Möglichkeiten.* du erstellst noch ein öffentliches Interface für die vorhersagen, machst aber die konkrete vorhersageklasse internal

  • du machst den konstruktor für vorhersagen internal

Aber wie gesagt, ich sehe keinen Grund für eine Beschränkung.

Beste grüße
zommi

22.02.2011 - 10:48 Uhr

Hallo,
Ich kenne noch yEd.
Beste grüße. Zommi

22.02.2011 - 10:12 Uhr

Hallo,

das mit dem Stacktrace ginge, hat aber die Inlining-Probleme, die auch in GetMethodCaller() ? und [Artikel] Attribute zur Prüfung von Properties verwenden besprochen werden.

beste Grüße
zommi

18.02.2011 - 12:10 Uhr

Davon abgesehen kommst du mit MemoryStream.GetBuffer sowieso leichter an dein Byte-Array.

Vorsicht! Lieber ToArray() verwenden, denn:

Zitat von: MSDN (MemoryStream.GetBuffer)
Beachten Sie, dass der Puffer zugeordnete Bytes enthält, die möglicherweise nicht verwendet wurden. Wenn z. B. die Zeichenfolge "test" in das MemoryStream-Objekt geschrieben wurde, beträgt die Länge des von GetBuffer zurückgegebenen Puffers 256 Bytes und nicht 4 Bytes, wobei 252 Bytes nicht verwendet wurden. Um ausschließlich Daten im Puffer abzurufen, verwenden Sie die ToArray-Methode. ToArray erstellt jedoch eine Kopie der Daten im Speicher.

Oder man weiß, was man tut und merkt sich die wirkliche Anzahl an sinnvollen Bytes. Aber dies is komplizierter in der Verwendung und wenn der MemoryStream erst einmal geschlossen wurde, bekommt man bei MemoryStream.Length auch noch eine ObjectDisposedException! ToArray hingegen funktioniert immer - erzeugt aber eben eine Kopie.

beste Grüße
zommi

18.02.2011 - 12:05 Uhr

Hallo asharp4dan,

vergiss die schleife, vergiss dein ThreadSleep (was eh egal ist, denn Socket.Receive blockiert!), vergiss das arbeiten direkt mit den sockets!

Hol dir per TcpClient.GetStream() den NetworkStream, steck ihn in einen StreamReader und nutze ReadLine.

beste Grüße
zommi

12.02.2011 - 20:36 Uhr

Hallo,

ich würde über die Verwendung von Generics nachdenken. Schließlich ist das Überschreiben der Methode mit zig neuen (alle nach dem selben Schema) alles andere als DRY. (und auch ich empfinde new hier nicht besonders schön)

Also warum nicht "Motor" als Typparameter angeben? Ab .NET 4.0 kann man dann noch "out" mit angeben und könnte sie auch kovariant einsetzen.

Ein zusätzliches Interface IAuto ermöglicht die allgemeine Nutzung, beispielsweise innerhalb einer Collection (wenn man noch nicht die Kovarianz-Features von 4.0 nutzen möchte)

interface IAuto
{
   Motor Motor { get;}
}

class Auto<T> : IAuto where T : Motor
{
   protected Auto(T motor)
   {
      Motor = motor;
   }   
   public T Motor { get; protected set; }
   Motor IAuto.Motor {get {return Motor;} }
}

class Benziner : Auto<BenzinMotor>
{
   public Benziner(BenzinMotor motor) : base(motor)
   {}
}

class Diesel : Auto<DieselMotor>
{
   public Diesel(DieselMotor motor) : base(motor)
   {}
}

// edit: auf automatisch generierte properties umgestellt und in der basisklasse den konstruktor bereits implementiert.
beste Grüße
zommi

02.02.2011 - 17:07 Uhr

Hallo inflames,

hier meine aktualisierte Liste:
*Vereinfache die Methoden, indem du nicht immer Rückgabewerte speicherst und danach erst diese returnst. Hier die vereinfachten Versionen:

public bool Contains(T item)
{
	return IndexOf(item) != -1;
}
public int IndexOf(T item)
{
	for (int i = 0; i < _count; i++)
	{
		if (_innerArray[i].Equals(item))
		{
			return i;
		}
	}
	return -1;
}
public bool Remove(T item)
{
	int index = this.IndexOf(item);
	if (index == -1)
		return false;
		
	this.RemoveAt(index);
	return true;
}

*Add sollte einfach Insert verwenden, da Anhängen nur ein spezialfall vom beliebigen Einfügen ist. -> DRY

public void Add(T item)
{
	Insert(_count, item);
}

*:::

Den hinteren Teil verschiebst du also einfach mit Array.Copy um eins nach hinten. (bzw. kopierst in dabei in das neue, verlängerte) Und falls du ein neues verlängertes brauchtest, kopierst du auch das davor noch mit:

public void Insert(int index, T item)
{
	if (index < 0 || index > _count) // check range correctly!
		throw new ArgumentOutOfRangeException();
		
	T[] oldArray = _innerArray;
	T[] newArray = _innerArray;
		
	if (_count == this.Capacity) // optionally expand array and copy first half
	{
		newArray = new T[](_count * 2);
		Array.Copy(oldArray, 0, newArray, 0, index); // copy elements before index
	}	
	Array.Copy(oldArray, index, newArray, index + 1, _count - index); // shift elements after index

	_innerArray = newArray;
	
	_innerArray[index] = item;
	
	_count ++;
}

*:::

public void RemoveAt(int index)
{
	if (index < 0 || index >= _count) // check range correctly!
		throw new ArgumentOutOfRangeException();

	Array.Copy(_innerArray, index + 1, _innerArray, index, _count - index - 1);
	
	_count --;
}

beste Grüße
zommi

02.02.2011 - 16:29 Uhr

Hi snupi,

ich kenn mich leider mit dem WinCE System nicht aus, auch über die Implementierung der Synchronisationsprimitiven im Kernel kann ich nichst sagen.

Nur in der "normalen" Windows NT Welt hat ein AutoReset-Event ein spezifisches Verhalten, was auf einem Desktop-System dazu führt, dass die OpenNETCF-Implementierung fehlerhaft läuft.

Es kann sein, dass das AutoResetEvent unter WinCE anders implementiert ist und ein anderes Verhalten besitzt. Dazu hatte ich damals aber nichts finden können.

Genau betrachtet ist dein einmaliges Beobachten eines nicht fehlerbehafteten Durchlaufs jedoch kein Beweis für die Korrektheit der Implementierung! Bei dir trat der Fehler nur (noch) nicht auf. So ist das bei Threading/Synchronisations-Problemen immer. Aber dennoch könnte es sein, dass er wirklich nicht auftreten kann, wegen oben beschriebener WinCE-Besonderheiten, aber das kannst du mit einem Test-Run nicht einfach beweisen.

beste Grüße
zommi

PS: Auf meinen gemeldeten Bug 442 gabs nie eine Antwort, geschweige denn, dass er angenommen und gefixed wurde.

02.02.2011 - 15:26 Uhr

Hi inflames2k,
*Implementiere IEnumerable<T> *Konstruktor-Ketten nutzen um CodeDopplung zu vermeiden (DRY). Ruf einfach aus dem parameterlosen Konstruktor den anderen mit Parameter 4. *Dein Indexer berücksichtigt nicht die aktuelle Länge. Ich kann mit ihm die "uninitialisierten" Werte bis zum Ende der capacity auslesen. Das Verhalten sollte jedoch sein, dass bereits nach "Count" eine Exception kommt. *_innerArray.GetLength(0) durch _innerArray.Length ersetzen. Es ist eh eindimensional. *:::

*Umbenennen: "ResizeInnerList" => "ExpandInnerArray" *:::

*Nutze innerhalb von Remove einfach deine IndexOf Implementierung. *Nutze auch innerhalb von Contains einfach deine IndexOf Implementierung. *Nutze beim KopierKonstruktor (aus einer collection) doch foreach statt der manuellen Enumerator-Implementierung. Zudem würde ich das Ermitteln der Collection-Länge in eine eigene Methode kapseln, und auch hierfür die Konstruktor-Kette nutzen und mit der berechneten Länge den normalen 1-parameterigen Konstruktor füttern. Und dann im Body der Kopierkonstruktors die Elemente setzen. *Verwende konsistent die "echten" Typ-Namen der Standardtypen wie Int32, Boolean oder überall den jeweiligen C#-Alias int, bool, etc... *Warum überprüfst du bei Add deine _innerArray auf null? Kan sie doch nach deinem Code nie sein.

Die Wichtigkeit der Punkte habe ich farblich gekennzeichnet

beste Grüße
zommi

02.02.2011 - 12:31 Uhr

Hallo chilic,

FaultException<T> erbt ja von der nicht-generischen Klasse FaultException. Nach dieser könntest du also Filter und dann im Catch-Block per Reflection prüfen, auslesen, etc...

(Du könntest auch direkt Exception zulassen, per Reflection prüfen, ob es eine Instanz von FaultException<T> ist und verarbeiten, oder andernfalls rethrown)
Das mit dem Rethrow ist zwar nicht sonderlich elegant, aber C# bietet leider nur das Filtern der Exceptions nach ihrem Typ an. VB.NET und C++/CLI hingegen können beliebige Catch-Block-Bedingungen verwenden. Da ist der Code dann etwas eleganter.

Die ab .NET 4.0 eingeführte generische Ko-/Kontravarianz kannst du hier meines Wissens aber nicht ausnutzen, da der generische Parameter T von FaultException<T> nicht als solcher gekennzeichnet ist - mit "out"

beste Grüße
zommi

29.01.2011 - 14:03 Uhr

Hinweis Nummer 2:

Zitat von: msdn (FileStream.BeginWrite)
Unter Windows werden alle E/A-Operationen mit einer Größe von weniger als 64 KB zum Zweck der Leistungssteigerung im synchronen Modus durchgeführt. Asynchrone E/A kann die Leistung bei Puffergrößen unter 64 KB verringern.

Du musst also sowohl die Datei im asynchronen Modus öffnen, als auch größere Puffer als 64KB verwenden.

beste Grüße
zommi

29.01.2011 - 13:51 Uhr

Hi Kr0e,

du musst die FileStreams schon mit dem "useAsync"-Parameter öffnen. Hierfür gibts nen speziellen Konstruktor: public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync).

beste Grüße
zommi

29.01.2011 - 13:02 Uhr

Hi ahiram,

Könntest du mir bitte ein Beispiel für die erste (eleganteste) Variante geben?

Ich will dir mal nicht alles vorgeben, aber das mit den Lookarounds ist wie folgt aufgebaut:

(?<=%pattern-davor%)%eigentliches-pattern%(?=%pattern_danach%)

Die Blöcke "%...%" musst du natürlich passend ersetzen. Und den Lookahead am Ende brauchst du ja eigentlich auch nicht.

Nun solltest du von allein drauf kommen.

beste Grüße
zommi

PS: ein Blick ins [Artikel] Regex-Tutorial kann auch nicht schaden 😉 Und zum Ausprobieren empfehle ich On-the-fly Regex-Tester: Regex-Lab

28.01.2011 - 22:21 Uhr

Hi Kr0e,

ich ergänze mal noch die Antwort von Flotse und hol mal für ne ausführliche Antwort etwas weiter aus. Das was dir .NET unter Windows bietet ist die crème de la crème der asynchronen multithreaded Programmierung für IO.

Warum Threadpool?
Die prinzipielle Idee einen Threadpool zu nutzen ist clever, denn Threads sind mehr oder weniger teure Resourcen. Besonders das Wechseln zwischen zu vielen Threads ist ein unnötiger Overhead und verringert nur den Gesamtdurchsatz. Daher nimmt man wenige Thread (in der Größenordnung der physischen Prozessoren).

Nun gibt es ein Problem für eine Usermode-Bibliothek für Threadpools. Wie erkenne ich, ob ein Thread gerade in einen blockierenden IO-Zustand gegangen ist? Nun muss erstmal geklärt werden, warum das interessant ist. Nunja, sagen wir mal die optimale Anzahl an Threads für einen Threadpool ist gerade die Anzahl der physischen Prozessoren n. Weniger als n ist offensichtlich doof. Aber auch mehr als n Thread sind kritisch, denn es können eh nicht alle gleichzeitig ausgeführt werden und es wird unnötig geschedulet, was Overhead bedeutet. Ok, also nehmen wir genau n.
Nun bearbeitet einer dieser Threads aber eine Aufgabe, die in eine blockierende IO-Operation übergeht. Damit wird dieser Thread vom OS schlafen gelegt.
Auf einmal wird eine CPU frei, die ungenutzt ist! Jetzt wäre es gut, hätten wir doch noch ein paar Threads mehr als n, sodass die nun auf die freie CPU können, denn sonst ist es wirklich Resourcenverschwendung. Aber dafür müssten wir mehr als n Threads erstellt haben. Und wenn keiner gerade IO macht, sind das eben zu viele und es wird zu oft geschedulet!
Ein Teufelskreis! Daher wäre es perfekt, wenn wir vielleicht 2n Threads haben, von denen aber nur n gerade lauffähig sind. (Damit konkurieren nicht alle 2n gleichzeit um die n CPUs) Und sobald nun einer der laufenden Threads in blocking IO übergeht, aktivieren wir einen der wartenden und nutzen die CPUs perfekt aus!
Aber niemand teilt einem usermode-Programm mit, dass ein beliebiger Thread gerade in den Wartezustand übergeht, schade.

Hier kommt nun ein vom OS-unterstützer Thread-Pool ins Spiel. Das OS im Kernel weiß natürlich, wann ein Thread in einen Wartezustand übergeht - es sorgt ja selbst dafür. Und wenn das OS nun auch noch weiß, dass dieser Thread in einem Pool mit x anderen Threads war und davon ein paar schlafend auf ihren Einsatz warten, dann kann das OS einen davon für uns aufwecken! Perfekt! Daher sind OS-unterstützte Threadpools, die direkt mit dem Scheduler zusammen arbeitet besser dran 😉

IO Completion Ports
Nun treibt Windows dieses Thread-Pooling noch auf die Spitze mit Completion Ports. Denn dass OS weiß ja intern nicht nur wann Threads blockieren, sondern weiß insbesondere auch noch, wann IO-Operationen fertig sind und kann dementsprechend auch den Threadpool steuern.

Bei den IO Completion Ports unter Windows verknüpft man zwei Gruppen von Resourcen miteinander. Auf der einen Seite eine Menge von Threads, die den Pool bilden, und auf der anderen Seite eine Menge von IO-Objekten, die ab und an etwas Signalisieren (Datei geschrieben, Daten an Socket eingetroffen, ...).

So könnte man auf einem Quadcore insgesamt 8 Threads in den Pool schicken und 1000 Sockets verwalten lassen. Kommt nun eine Nachricht auf dem Socket an, wechselt dieser in den signalisierten Zustand. Das übernimmt der Kernel für uns. Der sieht nun aber auch "Hey, dieser Socket ist ja in einer IO-Completion-Port-Aktion beteiligt" und weckt gleich einen Thread aus der Threadpool Liste auf, der sich um diesen Socket kümmern soll. Das kann er vier mal hintereinander machen, danach laufen aber 4 Threads auf dem Core und die Kiste is ausgelastet. Er wird nun keinen mehr aufwecken! Wenn nun aber einer der ersten 4 Threads noch schnell was in einer Datei für die Anfrage nachguckt - dummerweise mit der blockierenden IO - dann wird auf einmal eine CPU frei. Der scheduler sieht, dass dies ein Thread aus dem CompletionPortPool war und wird dafür einen 5ten Thread aufwecken und mit dem 5ten schon längst überfälligen Socket versehen - keine CPU wird verschenkt!
(Lediglich wenn der blockierte Thread fertig wird und damit wieder lauffähig, konkurrieren auf einmal 5 Threads um die 4 CPUs, ein kurzzeitiger Zustand der Überfüllung)

Und sobald einer der aktiven Threads seine aufgabe beendet hat, schaut er nochmal in der Competion-Port-Liste, ob bereit ein neues IO-Event wartet. Denn es wäre blöd, diesen Thread schlafen zu legen, und einem neuen, anderen die Aufgabe zu übertragen. Schließlich ist sein Stack/Register noch in der CPU gecachet, deshalb wird das Scheduler ihm zuerst die neue Afgabe geben - clever!

War es hingegen ein Thread aus einem kurzzeitigen Zustand der Überfüllung (5 Threads bei 4 CPUs), dann wird er keine neue Aufgabe bekommen - selbst wenn noch IO-Events warten, um nicht wieder eine Konkurrenz und damit Überfüllung zu erzeugen. Dieser Thread wird also wieder in den Pool schlafen gelegt.

Fazit:
Ein im OS-eingebauter ThreadPool in Verknüpfung mit asynchronen IO-Events ist genial und kann dank der engen Verdratungsmöglichkeit zum OS-eigenen Scheduler höchst effektiv arbeiten! Hier arbeitet die ThreadPool-Implementierung nicht gegen den Scheduler, sondern mit ihm.

.NET Threadpool
Und der .NET Threadpool nutzt nun (zur Hälfte) diese Completion Ports bei asynchroner IO. Die gängigen IO-Klassen File, Socket, ... verwenden in ihrer asynchronen Implementierung genau das!

Zur Hälfte habe ich geschrieben, weil der .NET Threadpool zweigeteilt ist. In einen Pool für selbst gestellte rechenlastige Aufgaben. Und einen zweiten für die asynchrone IO-Verarbeitung.

Insofern kannst du einfach die BeginSend/...-Methoden auf Socket verwenden, das .NET Framework wird die Socket-Klassen für dich unter der Haube mit seinem IO-Completion-Port-Thread-Pool verknüpfen und wenn dein Socket signalisiert, dann wird vom OS-Scheduler ein Thread aufgeweckt, der nun über das .NET-Framework geleitet, deinen Callback auf diesem Socket ausführen wird.

Select
Effizienter geht es eigentlich nicht! Es gibt natürlich auch eine .NET Variante von Socket.Select. Diese skaliert aber nicht so gut, wie die Completion Ports.
Zum einen würde der Thread, der per Select wartet per Hand die IO-Events auf einen eigenen ThreadPool verteilen. Nun laufen aber bei einem neuen Event vielleicht schon genug Threads (so viele wie CPUs). Dann müsste ich das IO-Event erstmal irgendwo zwischenparken und dann zuweisen.
Aber ich kann nicht das Event nem neuen Thread zuweisen, falls einer der gerade laufenden in Blocking IO übergeht, weil ich das ja als eigenen Usermode-Threadpool-Scheduler überhaupt nicht mitbekomme! (Problematik von oben)

Und zweite Schwierigkeit ist, dass der Select-Thread den zentralen Dispatcher spielt und eventuell zu langsam wird. Bei jedem neuen IO-Event wird dieser Thread erstmal wieder aktiv, dispatcht kurz und legt sich schlafen. Unnötiges Thread-Scheduling! Zudem ist das ein unsymmetrisches Modell, das genau beim Dispatcher einen Flaschenhals hat. Was ist, wenn dieser eine Thread nicht mit dem dispatchen hinterherkommt? Man bräuchten einen Multithreaded-Select-Dispatcher. Mehrere Threads, die jeweils nur auf einer Untermenge "Select" aufrufen. Nunja, dann wirds aber echt konfus! Aber hey, das OS-interne Scheduling von Windows ist von hause aus parallel implementiert (SMP-System), skaliert also besser auf vieeelen CPUs mit.

Das mit dem Select-Dispatching sieht auch in der Vorstellung doof aus: Das OS bekommt von der Netzwerkkarte auf irgendeiner CPU asynchron (vielleicht sogar mehrere pallel) n Interrupt von der Netzwerkkarte. Nach einigem Prozedere werden früher oder später diese Events wieder serialisiert für einen einzigen Thread, der Select auf ihnen gesagt hat. Der hingegen macht nichts weiter, als die Events an unterschiedlcihe Threads parallel und asynchron zu verteilen! Da hätte man die doch gleich an die Threads geben können! Und genau das machen die IO-Completion-Ports.

IOCompletion hat also gegenüber Select einige Vorteile. Nur ist die Verwendung von Select aus einem C-Programm heraus noch möglich. Die Verwendung der IOCompletionPorts-API verkompliziert das Programm extreeeemst. Aber .NET kapselt das ja alles schön wodurch es für den .NET-User traumhaft einfach wird.

Ich mein, welcher Low-Level-Socket-Programmierer kennt, geschweige nutzt schon die asynchrone Socket-APIs wie WSARecv anstatt der blockierenden (bsd-socket-konformen) recv, die man aus Tutoritals, Vorlesungen, Büchern, von Linux kennt. Und dann noch IO-Completion-Ports, die die wenigsten kennen.

Von daher: ein Hoch auf .NET 😃
(was hättest du auch in einem c#-Forum erwartet 😉 )

Und auch wenn es für java unzählige Frameworks für asynchrone sockets gibt, bei .NET ist alles bereits ingetriert.

Wenn du das .NET 3.5 Framework verwendet, kannst neben den BeginXXX/EndXXX-Methoden auch noch die für Sockets neu eingeführten XXXAsync-Methoden verwenden.
Diese sind sogar noch performanter und erzeugen nicht so häufig neue Objekte (was wiederum den GC entlastet und die Gesamtleistung steigert) Siehe: Get Connected With The .NET Framework 3.5: Socket Class Performance

beste Grüße
zommi

PS: Aber such auch einfach mal im Forum nach "asynchron socket"

26.01.2011 - 17:13 Uhr

Hallo gaussmath,

sehe ich das richtig, dass du zum einen eine Fortran-DLL für die nutzung aus C# heraus erzeugt hast, aber für den puren Fortran-Test, diese DLL überhaupt nicht nutzt, sondern den Fortran Code direkt in eine EXE-kompilierst ?

In diesem Unterschied könnte sich ja alles begründen. Vielleicht wird beim Fortran Kompilieren in eine Exe etwas anderes getan, als beim Kompilieren in eine DLL. Du sagtest, du hättest es mit den selben Einstellungen getan... mh....

3 Dinge würde ich analysieren:*Schreibe ein C programm, was die Fortran-Dll nutzt und miss erneut. (das sollte in etwa genausolang dauern, wie dein C# program, denn C# verändert die Dll genausowenig wie C) *Schreibe ein Fortran-Programm was ebenfalls die Fortrag-DLL nutzt (und nicht den SourceCode direkt reinkompiliert bekommt) *Recherchiere, was beim Fortran-Export-C-DLL-Kompilieren alles passiert. Wie gibts du an, welche Methoden "exportiert" werden? Hast du da fälschlicherweise alle angegeben? Was sagt das Tool DependencyWalker, welche Funtktionen von der DLL exportiert werden. Werden manche deiner Compiler-Optionen beim DLL-Kompilieren nicht beachtet?

beste Grüße
zommi

26.01.2011 - 16:21 Uhr

Hat jemand vielleicht irgendwelche Referenzen von Microsoft selbst, wo ich etwas über den Performanceverlust beim Aufrufen von nativem Code nachlesen kann?

Beispielsweise bei An Overview of Managed/Unmanaged Code Interoperability oder Improving Interop Performance.

Die Texte beziehen sich zwar teilweise noch auf ältere CLRs, aber das Prinzip ist gleich, und dort steht:

Zitat von: msdn
Approximate overhead for a platform invoke call: 10 machine instructions (on an x86 processor)

bzw. wird durch das Schaubild Calling a Flat API: Step by Step das gut erläutert.

Wie aber FZelle und herbivore angesprochen haben, diesen konstanten Overhead hast du bei jedem Interop-Call. Alles dahinter läuft nativ in der DLL ab. Daher so wenig Interop-Calls wie möglich und nur große Funktionsblöcke auslagern.

beste Grüße
zommi