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

Forenbeiträge von zommi Ingesamt 1.361 Beiträge

06.10.2014 - 14:14 Uhr

Hey Tom,

und auf C++ seite auch?

Ja, du solltest dich für eine Aufruf-Konvention entscheiden: stdcall oder cdecl.
Und das natürlich auf C++ wie auf C# Seite, damit sie korrekt miteinander kommunzieren können.
(es geht in erster Linie darum, ob der Aufrufer (hier C#) oder der Aufegerufene (hier C++) den Stack aufräumt - und darüber sollten sich beide einig sein!)

Und du solltest beim C++ Code das Keyword mit den zwei Unterstrichen verwenden. Also __stdcall sowie __cdecl.
Mit einem Unterstrich wird es zwar auch vom Compiler akzeptiert, ist aber undokumentiert.

Probier einfach mal beides auf cdecl zu stellen.

beste Grüße
zommi

06.10.2014 - 13:31 Uhr

Hey,

sieht ja ganz so aus, als ob da immer noch ein Name Mangling am Werk ist, schließlich heißt die Methode _Z8stringmev und nicht stringme.

Versuch mal auf __cdecl auf C++ Seite und natürlich CallingConvention.Cdecl auf C# Seite umzustellen.

Es scheint, als würde MSVC++ immernoch mangeln.
(Stackoverflow: Why extern “C” still cannot remove name mangling in the following case)

Eventuell hilft ja die Linker-Anweisung aus diesem Stackoverflow-Artikel um absolut kein Name-Mangling vorzunehmen:

#pragma comment(linker, "/EXPORT:ExportSymbol=DecoratedName");

Oder aber du gibst auf C# Seite den gemangelten Namen im DllImport-Attribut an:

EntryPoint="_Z8stringmev"

beste Grüße
zommi

06.10.2014 - 12:26 Uhr

Hey,

du müsstest mal mit dem Dependency Walker direkt die Win32Project3.dll öffnen und dann schauen, ob in dem rechten, mittleren Fenster du eine exportierte C-Funktion mit dem Name "stringme" findest.

beste Grüße
zommi

02.10.2014 - 13:02 Uhr

Hey,

beim DllImport muss es naürlich "CallingConvention.StdCall" heißen.


extern "C" __declspec(dllexport) int _stdcall stringme()

[DllImport("libStrinr.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern int stringme();

Ohne Argumente und Interesse am ReturnWert müsste aber stdcall wie cdecl kompilieren, aber ist natürlich inkorrekt. Mit echten x64-binaries ist das alles egal, da gibts nur eine Calling Convention. Und wenn du void zurückgibst, und dann doch fälschlicherweise der Inhalt vom eax/rax register als ein int interpretiert wird, dürfte auch nichts schlimmen machen. Aber ja, auch die Return-Typen sollten natürlich übereinstimmen.

**

dumpbin

** und **

dependency walker

** sind zwei tools die dir hier beim Fehler finden helfen können.

beste Grüße
zommi

28.09.2014 - 16:31 Uhr

Hallo Palladin007,

bei mir ist es zwar schon länger her, dass ich mit Reflection und Mono.Cecil gearbeitet habe, aber vielleicht reichen meine Erinnerungen noch 😃

Es ging damals darum beliebigen .NET Code zu profilen, was mit einschließt, dass jeder Typ und jede Methode "eingepackt" werden muss in einen Typ und eine Methode mit identischer Signatur.
Insbesondere musste das auch mit generischen Typen funktionieren, deren konkrete spätere Instanzen uns auch nicht bekannt waren, da diese potentiell erst außerhalb in einer ganz anderen Assembly definiert sind.

Eine Erkenntnis war: Vergiss Reflection, nimm Mono.Cecil.
Wenn ich mich recht entsinne, musste für ein gültiges Type-Objekt auch immer die Assembly geladen werden, anders lässt es Reflection nicht zu.
Cecil hat für Assembly, Type, Method eigene Typen, die es eben nicht erfordern alles zu laden, was das Leben komplizierter, aber auch einfacher macht.
Mit Reflection selbst war das alles eine Qual. Entladen von Assemblies ging damals auch nicht. Und noch einige Probleme.

Allerdings ist das eng mit dem Emit-Namespace verbunden, mein Plan, die Definition der kompletten Methode unabhäng zu halten, funktioniert damit nicht mehr.

=> Mono.Cecil

Damit kann ich die Parameter-Definition aber nicht mehr einheitlich mit einem Type-Objekt unabhängig halten, denn die Definition ist ja erst funktionsfähig, wenn der generische Typ-Parameter definiert wurde.

Wie schon gesagt, die Reflection Type-Objekte bietet keine gute Trennung zwischen "deklariert" und "definiert". Daher => Mono.Cecil

Ich möchte unabhängig von dem Emit-Namespace bleiben,

=> Mono.Cecil

Ich finde das Thema einfach sehr interessant und möchte im Prinzip etwas Eigenes machen, wie Mono.Cecil, nur nicht ganz so umfrangreich.

Forke Mono.Cecil 😉

beste Grüße
zommi

17.09.2014 - 12:15 Uhr

Hey,

ja klar, die Differenzkodierung muss schon "sinnvolle" Differenzen bilden. Und PNG macht das eben nur auf 8 oder 16 Bit Basis. Wenn ich diese aber nur mit bit-verschobenem Brei fülle, verschleiert er natürlich jedwede Redundanz vor dem Kompressor.

Man muss einem Kompressions-Algorithmus schon zuarbeiten, und es ihm leicht machen, die Redundanzen zu entdecken. Daher kann es auch effektiver sein, ein paar 0-Bits einzustreuen, damit ein byte-weise arbeitender Kompressor die Muster besser erkennt und ausnutzen kann.

Und ich meinte mit "clever", dass die Konzepte bei PNG 1:1 auch bei Merti_berg gut passen. (rasterbasiert, differenzkodiert, deflate).

Aber ich stimme dir völlig zu. Sich in die Beschränkungen von PNG reinzupressen macht keinern Sinn. Schließlich handelt es sich nicht um Farbbilder. Und da die Ideen hinter PNG leicht selbst zu implementieren sind, ist ein eigenes Dateiformat die beste Wahl.

beste Grüße
zommi

17.09.2014 - 11:53 Uhr

Hi Merti_berg,

zunächst musst du dir überlegen, was dein primäres Ziel sein soll.*Geschwindigkeit beim kompletten Einlesen *Geschwindigkeit / Overhead beim selektiven Einlesen einzelner Elemente *Geschwindigkeit beim Schreiben *Möglichkeit Teile zu aktualisieren *Festplattenverbrauch

Manche Dinge gehen einher, manche sind eher komplementär.
Beispielsweise kann eine Kompression für geringeren Festplattenbedarf gleichhzeitig die Geschwindigkeit für das komplette Einlesen optimieren. Schließlich ist fast immer (sofern du die CPU während dem IO-Warten nicht auslastest) das Einlesen einer komprimierten Datei und On-The-Fly-Dekompression schneller. (vergl. Blosc) Wenn du aber schnell auf einzelne Werte zugreifen musst, ist es hingegen schlecht, wenn die komplette Datei noch einmal komprimiert wurde.

Die Idee PNG zu missbrauchen ist ja eigentlich ganz clever.
Wobei PNG nicht viel Magie ist.
Raster-weises abspeichern, differentielle Kodierung gegenüber dem Wert links/oben vom aktuellen und abschließend eine Deflate-Komprimierung. Mach das einfach genau so selbst.

Fragen musst du dich aber auch, welche Reihenfolge die Daten haben sollen:*Zeilenweise und dann innerhalb eines Pixels die verschiedenen "Kanäle" *Spaltenweise und dann innerhalb eines Pixels die verschiedenen "Kanäle" *Kanalweise und innerhalb eines Kanals hintereinander zeilen-/spaltenweise alle zugehörigen Werte" *...

Das hängt davon ab, wie du später auf die Daten zugreifen möchtest. Zudem beeinflusst das Layout auch, wie gut eine Kompression arbeiten kann.
Wahrscheinlich lassen sich (sofern deine Differenzkodierung) die Kanäle einzeln besser komprimieren. Das spart auch zu ekliges Bitshiften. Der Zugriff auf alle Daten zu einem (X,Y)-Paar ist dann hingegen aufwändig und du musst alles dekodieren. Wenn die Dateien aber nur klein sind, kein Problem.
Also ein gedankliches Für- und Wieder 😃

Und ja, wenn du anschließend eine gute Kompression nutzt, sollte es nur einen geringen Unterschied machen, ob du führende Nullen mit kodierst - also 32-Bit Zahlen abspeicherst, obwohl deine Zahlen nie größer als 20 Bit sind.
Aber probier es am besten einfach aus.

beste Grüße
zommi

17.09.2014 - 10:43 Uhr

Hallo nochmal,

ich würde ganz grundlegend von FTP abraten und HTTPS empfehlen.1.Es ist nicht garantiert, dass aus jedem Firmennetz FTP funktioniert.
HTTP(s) hingegen hat eine höhere Abdecking.

1.Es ist unverschlüsselt - was bei diesen Daten sicherlich nicht sonderlich schlimm ist - aber vielmehr noch beitet es keine Authentifizierung des Servers. Und beim Kunden ein Programm zu installieren, was automatisch im Hintergrund Daten von nicht überprüfbaren Quellen auf die Festplatte runterlädt und bestenfalls noch sofort im Browser öffnet ... Klares Nein.
Natürlich gibt es SFTP und FTPS, aber beides brauch nochmal wieder andere Ports, was in noch weniger Firmennetzen garantiert ist und meines Wissens sind die C#-Libs auch hier wieder nicht von Hause aus im .NET Framework.
Und ja, du könntest einen eigenen Authentifizierungsmechanismus einbauen, vielleicht sogar die Datei-Pakete selbst signieren. Aber du willst ja auch mal fertig werden 😉

Daher ein klares Votum für HTTPS.
(und vielleicht eben auch Git über HTTPS 😉 )

beste Grüße
zommi

16.09.2014 - 17:21 Uhr

Hi unnamed123,

ich wiederum sehe kein Problem in der Verwendung einer existierenden Versions-Kontroll-Verwaltung.

Ich würde einfach Git nehmen und über libgit ansprechen. Git unterstützt auch Binary Diffs, sollte also auch damit klarkommen.

Du musst ja auch bedenken, dass du für eine solche Versionsverwaltung sowohl Client als auch Server umsetzen müsstest. Und zudem für effiziente Übertragung eine Diff-Implementierung.

Gleichzeitig bietet Git von Hause aus die Möglichkeit zu branchen.
Vielleicht gibt es die Hilfe später in mehreren Sprachen je nach Lokalisierung des Clients? Sicherlich möchtest du auf Serverseite auch die alten Versionen irgendwo noch archivieren - dann müsste der Server schon mehr können. Zudem können Mitarbeiter einfach mit einem eigenen Git-Client die zentralen Hilfe-Dateien updaten. Und und und ...
Klingt also eigentlich ganz gut 😃

Ich muss zugeben, ich selbst habe es noch nie verwendet und noch nicht gebraucht. Aber wenn, würde ich glaube ich über Git gehen 😃

beste Grüße
zommi

15.09.2014 - 00:26 Uhr

Hey,

ich habe mal an der Geschwindigkeit etwas geschraubt. MD5 ist nun rausgeflogen und stattdessen eine eigene, ziemlich obskure 32-Bit Hashfunktion reingekommen. Da diese sicherlich nicht so gute Eigenschaften wie MD5 aufweist, habe ich im Gegenzug die Rundenzahl auf 10 erhöht.

Nachfolgend das Snippet. Sie fühlt sich ein Bisschen an wie die Random Klasse (mit der Next()-Methode), bietet aber überdies noch eine komfortable Shuffle-Methode, die einem eine Abfolge von kollisionsfreien Pseudozufallszahlen im Intervall [0,N) zurückgibt.

class Shuffler
{
    uint N;
    int HalfBits;
    uint Counter;
    uint BitMask;
    uint[] Keys;
    const int ROUNDS = 10;

    public Shuffler(uint n) : this(n, (int)DateTime.Now.Ticks) { }

    public Shuffler(uint n, int seed)
    {
        N = n;
        Counter = 0;

        int bits = (int) Math.Ceiling(Math.Log(n, 2));
        HalfBits = (bits + 1) / 2;
        Console.WriteLine("HalfBits: {0}", HalfBits);
        BitMask = (1u << HalfBits) - 1u;

        var seeder = new Random(seed);
        Keys = Enumerable.Range(0, ROUNDS)
                        .Select(i => (uint)seeder.Next())
                        .ToArray();
    }

    uint Encrypt(uint x)
    {
        uint right = (x & BitMask);
        uint left = ((x >> HalfBits) & BitMask);
        for(int i=0; i<ROUNDS; i++)
        {
            uint temp = right;
            uint hash = ((right * 13u + Keys[i]) * 63845467u + 2345u) % 38444179u; 
            hash &= BitMask;
            right = left ^ hash;
            left = temp;
        }
        return (left << HalfBits) | right;
    }

    public uint Next()
    {
        if (Counter >= N)
            throw new InvalidOperationException("Cannot provide more than N distinct values");
        uint x = Counter++;
        do
        {
            x = Encrypt(x);
        } while (x >= N);
        return x;
    }

    public static IEnumerable<uint> Shuffle(uint n)
    {
        return Shuffle(n, (int)DateTime.Now.Ticks);
    }

    public static IEnumerable<uint> Shuffle(uint n, int seed)
    {
        var p = new Shuffler(n, seed);
        for(uint i=0; i<n; i++)
            yield return p.Next();
    }
}

Ich habe die Laufzeit mal gegen einen Fisher-Yates-Shuffle getestet um komme auf vergleichbare Laufzeiten - bei einem Speicherbedarf von O(1).

beste Grüße
zommi

PS: Obiges Snippet liefert auch wie ich finde ausgesprochen gut gleichverteilte Permutationen. Man muss auch bedenken, dass es schon ab 13 Elementen mehr Permutationen als Zufallszahlgeneratoren gibt, da 13! bereits die Zahl möglicher 32-Bit Seeds übersteigt.

14.09.2014 - 20:30 Uhr

Hey UltraEdit,

eine wunderschöne Aufgabe wie ich finde 😃.

Du suchst ja eine zufällige Permutation des Kreuzprodukts aus Vornahmen und Nachnamen. Ich würde zunächst jedem möglichen Voll-Namen eine eineindeutige Nummer zuordnen.

Bei K Vornahmen und L Nachnahmen sind das K*L Zahlen. Wenn du nun eine solche Zahl "index" hast, kannst du daraus leicht den Namen ableiten:


vorname = vornahmen[index % K];
nachname = nachnamen[index / K];

Jetzt sind wir also nur noch an einer Permutation der ersten N:=K*L natürlichen Zahlen interessiert.

1) Random Picking mit Lookup
So wie du es bereits umgesetzt hast, generierst du eine zufällige Zahl aus [0,N] und holst dir den passenden Namen dazu. Damit du nicht mehrfach den selben Namen erwischst, speicherst du dir die bereits gezogenen in einer Tabelle und wählst bei einer Kollision einfach neu.
Entweder speicherst du diese Tabelle "sparse" als HashTable<int> oder "dense" als bool[N].
(Auch hier zahlt sich die Verwendung der Nummer-Indizes statt der Vollnamen aus, da das Hashen der Strings auch wieder Zeit kostet.)
Allerdings benötigst du entsprechend viel Speicherplatz, mindestens so viel wie du Elemente generieren möchtest.

Die Wahrscheinlichkeit für eine Namenskollision ergibt sich aus dem Geburtstags-Paradoxon und ist in der Größenordnung von sqrt(N) generierten Namen gehäuft zu erwarten.

Je mehr Namen du generieren möchtest, desto häufiger sind Kollisionen und desto öfter musst du nach einer neuen Zahl suchen. Die Laufzeit ist also insbesondere nicht linear.

2) Complete Permutation
Aufbauend auf herbivores Vorschlag kannst du einfach ein Array aller Namen (oder besser: NamenIndizes) vorher aufbauen, dann einmal permutieren und dann einfach von vorne weg die Namen nehmen.
Für das Permutieren bietet sich der Fisher-Yates Algorithmus an. Dort kannst du sogar rechtzeitig aufhören, wenn du genügend Namen hast. Das gesamte Array must du aber dennoch aufbauen, was relativ viel Speicherplatz benötigt. (bei dir jedoch nur 7MB)

Bei angemessenen Mengen an Namen würde ich persönlich diesen Weg gehen.

3) Iterative Permutation
Aber nun zum "theoretisch" wie ich finde schönsten Ansatz 😃
Du hast das Problem, dass du nur einen PseudoRandomNumberGenerator (PNRG) zur Verfügung hast, aber an einer PseudoRandomPermutation (PRP) interessiert bist.

Nun gibt es glücklicherweise das Luby-Rackoff-Theorem, dass besagt, dass ein Feistel-Netzwerk mit mindestens 4 Ebenen dazu genutzt werden kann, um aus einer beliebigen Klasse an PseudoRandomFunctions (PRF) eine PseudoRandomPermutation (PRP) zu generieren.

Anders ausgedrückt: Die Grundlage für die Konstruktion von Block-Verschlüsselungen.

Eine Block-Verschlüsselung ist nichts anderes als eine pseudo-zufällige Permutation von Bit-Blöcken.
Betrachten wir einfach mal die 128-Bit AES Verschlüsselung mit einem festen Schlüssel. Wenn ich damit einen 128-Bit Block "verschlüssle", wird im Grunde nur der Block auf einen anderen Block abgebildet (permutiert). Hierbei dürfen natürlich keine Kollisionen auftreten, sonst kann ich ja nicht mehr eindeutig entschlüsseln. Daher handelt es sich um eine Permutation aller denkbaren 128-Bit Blöcke. Zudem ist sie pseudozufällig, da sie für einen Angreifer zufällig aussieht, er also ohne den Schlüssel nicht einfach zurückrechnen kann.

Und die Technik hierfür liefern Feistel-Netzwerke. Diese ermöglichen die Konstruktion solcher Block-Chiffren (beispielsweise für DES).

Wenn wir nun eine Block-Chiffre für den Zahlenbereich [0, N] konstruieren können, können wir einfach einen Zähler (0, 1, 2, 3, ...) Stück für Stück verschlüsseln und erhalten eine zufällige - garantiert kollisionsfreie - Zahlenfolge aus [0,N].

Allerdings arbeiten Feistel-Chiffren bit-basiert, also nur auf "ganzen" Bit-Blöcken.
Wir können also nicht direkt den Bereich [0, N] verschlüsseln, sondern nur den nächst größeren ganzen Bitblock [0, 2^n].

Konkrete Idee:
Bei deinen N = (3422*522) ist die nächst größere Zweierpotenz (2^21). Also konstruieren wir uns eine 21-Bit-Verschlüsselung. Dann generieren wir einen zufälligen "Schlüssel" (der Seed für deine zufällige Permutation). Und anschließend verschlüsseln wir nacheinander die Zahlen 0, 1, 2, 3, 4, ...

Falls wir dabei auf eine Zahl größer als N stoßen, verwerfen wir diese einfach und machen mit der nächsten weiter. Da wir aber die Blockgröße möglichst passend gewählt haben, gibt es kein überflüssiges Bit und es ist garantiert, dass du höchstens doppelt so viele Zahlen generieren musst. Also immer noch eine garantiert lineare Laufzeit.

Der Feistel-Cypher braucht intern nun aber etwas mehr als einen Zufallszahlengenerator. Er braucht eine Zufallszahlenfunktion. Das ist aber im Grunde einfach eine Hash-Funktion, deren Ausgabe ja auch als "zufällig" angesehen wird. Ich habe dafür einfach mal MD5 verwendet.
Du kannst aber auch einen normalen Verschlüsselungsalgorithmus hier einbetten. Halt irgendetwas, was eine Zahl X zufällig auf eine andere Zahl Y abbildet.

Gerade wegen dem eingebauten MD5 ist das ganze jedoch etwas langsam.
Man kann es aber bedenkenlos durch eine "effizientere" Hash-Methode ersetzen.

Vorteil ist die garantiert lineare Laufzeit und der konstante Speicheraufwand.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Linq;
using System.Collections.Generic;

class Mainy
{
    public static void Main(string[] args)
    {
        string[] forenames = {"Adam", "Bernd", "Charlie", "David"};
        string[] surnames = {"Repke", "Schubert", "Trem"};

        // Calculate 5 random combinations
        var names = CombineNames(forenames, surnames).Take(5);

        foreach(string name in names)
            Console.WriteLine("Hello {0}", name);

    }   

    public static IEnumerable<string> CombineNames(string[] first, string[] second)
    {
        int total = first.Length * second.Length;

        var permutator = new Permutator(total);
        for (int i=0; i<total; i++)
        {
            // Generate random index for First-Second-Pair
            int code = permutator.Next();
            int firstIndex = code / second.Length;
            int secondIndex = code % second.Length;

            // Return compound name
            yield return String.Format("{0} {1}", first[firstIndex], second[secondIndex]);
        }
    }

}

class Permutator
{
    private int N;
    private int HalfBits;
    private int Counter;
    private int BitMask;
    private Func<int, int>[] PseudoRandomFunctions;
    private Random Seeder;

    public Permutator(int n) : this(n, (int)DateTime.Now.Ticks) { }

    public Permutator(int n, int seed)
    {

        this.N = n;
        int bits = (int)Math.Ceiling(Math.Log(n, 2));

        // round up to an even number of bits
        if (bits % 2 != 0)
            bits += 1;

        this.HalfBits = (bits / 2);
        this.BitMask = (1 << this.HalfBits) - 1;

        // init Random Generator for round keys   
        this.Seeder = new Random(seed);

        // initialize Pseudo Random Function for all rounds
        this.InitPseudoRandomFunctions(4);

        // reset Counter
        this.Counter = 0;
    }

    // Creates a PseudoRandomFunction (PRF).
    // PRFs should be injective into {2^HalfBits}
    // (Can be truncated hashes like MD5, SHA1 or symmetric encryptions like DES or AES)
    private Func<int, int> createPseudoRandomFunction(int roundKey)
    {
        MD5 md5Hash = MD5.Create();
        byte[] keyBytes = BitConverter.GetBytes(roundKey);
        byte[] md5Input = new byte[sizeof(int) * 2];
        System.Buffer.BlockCopy(keyBytes, 0, md5Input, 0, sizeof(int));

        return (int x) => {
            byte[] inputBytes = BitConverter.GetBytes(x);
            System.Buffer.BlockCopy(inputBytes, 0, md5Input, sizeof(int), sizeof(int));
            byte[] hash = md5Hash.ComputeHash(md5Input);
            return BitConverter.ToInt32(hash, 0) & this.BitMask;
        };
    }

    // Create a set of PRFs, one for each round with distinct, random round keys 
    private void InitPseudoRandomFunctions(int rounds)
    {

        this.PseudoRandomFunctions = Enumerable.Range(0, 4)
                                        .Select(i => this.Seeder.Next())
                                        .Select(this.createPseudoRandomFunction)
                                        .ToArray();
    }

    // Encrypts a given Number using a Feistel Cipher and the PFRs for each round.
    // (Bijectively maps {N} -> {N})
    private int Encrypt(int x)
    {
        int right = x & this.BitMask;
        int left = (x >> this.HalfBits) & this.BitMask;

        foreach(var pfr in this.PseudoRandomFunctions)
        {
            int temp = left;
            left = right ^ pfr(left);
            right = temp;
        }

        return (left << this.HalfBits) | right;
    }

    // Returns the encrypted Counter
    public int Next()
    {
        // We can only encrypt Domains of power 2.
        // Thus we reject all values bigger than N and move on with the next Counter.
        int result;
        do
        {
            result = this.Encrypt(this.Counter++);
        }
        while(result >= this.N);

        return result;
    }
}

Eine andere Minimal-Implementierung findet man als Gist: Feistel Hash

beste Grüße
zommi

PS: Die selben Techniken finden beim Format-preserving encryption statt. Beispielsweise gibt es auch einen standardisierten 'FFX-mode' für AES, ein standardisiertes Feistel-Netzwerk, welches intern AES nutzt und im Grunde wie obiger Code arbeitet. Warum ist das eigentlich noch nicht im .NET Framework?

08.09.2014 - 17:55 Uhr

Um TTL=0 müsstest du dir meines Wissens keine Sorge machen.

Alle Clients sollten bei einem disconnect/reconnect ihre DNS-caches so oder so leeren. Wenn da Leichern übrig bleiben würden, würde da irgendwie einfach alles schief laufen.

Du musst beide Möglichkeiten kombinieren!
Ohne den DNS-Server und ne gescheite Antwort sendet der Client ja kein einziges HTTP-Paket los.

wie bekomme ich denn die Hotspot-User davon überzeugt, dass ich der zu verwendende DNS-Server bin

Der Hotspot verteil über DHCP an die Clients IP-Adressen und DNS-Server Adressen. Hier trägst du die IP deines DNS-Servers ein.

Kann ich einfach so alle HTTP-Requests abfangen? Ja, machen alle so 😉 (HTTPS hingegen geht nicht)

Kann ich einfach einen eigenen DNS-Server implementieren? Ja klar, das Protokoll ist einfach. Aber ich würde da nach einem kleinen Tool suchen.

Wie mache ich möglichst wenig "kaputt" Auf den Clients kannst du wenig kaputt machen.

wie kann ich das Server-System einfach in den Ursprungszustand versetzen DNS-Server abschalten. Bzw vielmehr musst du beim DHCP-Server den dort hinterlegten DNS-Server anpassen.

08.09.2014 - 17:29 Uhr

Eigener DNS Server, der auf alle Anfragen mit der lokalen IP antwortet klingt vernünftig.

Die automatische Umleitung auf die Startseite wird meist mit einem versteckten HTTP-Request realisiert.

Bei iOS sieht das wie folgt aus:

GET /library/test/success.html HTTP/1.0
Host: [URL]www.apple.com[/URL]
User-Agent: CaptiveNetworkSupport/1.0 wispr
Connection: close

Apple erwartet, dass hier mit StatusCode 200 geantwortet wird. Wenn hingegen ein HTTP-Redirect zurückkommt (StatusCode 302), sollte sich magisch der Browser mit dieser Seite öffnen.

Google/Android fragt nach http://clients3.google.com/generate_204 und erwartet StatusCode 204, also auch hier einfach mit redirect (302) antworten.

Windows fragt nach http://www.msftncsi.com/ncsi.txt und erwartet StatusCode 200 mit der exakten Antwort "Microsoft NCSI".

Der Post How to create WiFi popup login page hilft dir vielleicht auch weiter.

Diese Anfrage-Pattern müsstest du anhand von Host/Domain/User-Agent eigentlich gut herausfiltern können, beispielsweise mit URL rewrite rules. Oder einfach auf alles mit nem 302-Redirect antworten, was nicht deiner normalen Kantinen-Url entspricht.

Stichwort hast du ja eigentlich schon genannt: "Captive Portal Detection"

beste Grüße
zommi

29.08.2014 - 14:36 Uhr

Nein ich kann das ganze nicht in SQL abbilden.

Vielleicht geht es ja trotzdem. Filtern auf Basis von ein paar String-Eigenschaften klingt nicht so komplex.

Woran scheitert die Umsetzung mittels SQL? Ich würde das als allererstes probieren. Viele Datenbanksysteme arbeiten von hause aus hoch-optimiert und parallel.

In eine anderen Programmiersprache das ganze umzusetzen kommt nicht Infrage, dazu ist das Projekt wirklich zu groß.

Du musst ja nicht das ganze Projekt portieren. Es gibt ja mit PlatformInvoke/Interop/ManagedC++/COM/SharedMemory/Pipes/Sockets... unzählige Möglichkeiten wie Programmteile unterschiedlicher Sprachen miteinander kommunizieren können.

Du musst nur den performance-kritischen Teil portieren.

Und entweder ist der klein genug und die Logik einfach genug, dass das kein Problem ist das in einer anderen Sprache nachzubauen.
Oder der Code ist so umfangreich, dass du dir selbst widerspricht: Denn wenn es komplexe Berechnungen sind, dann kann der Methodenaufruf nicht der Flaschenhals sein 😉

beste Grüße
zommi

29.08.2014 - 11:16 Uhr

Hi falsecode,

  • Ich bezweifle, dass es sich um 2,8 Billionen (2800 Milliarden oder auch 2,8 * 10^12) Datensätze handelt 😉 Ein Bit pro Datum macht dann schon 350 GB. Das schiebt man nicht einfach so zwischen Datenbank und Programm hin und her 😉
    Meinst du vielleicht Milliarden?

  • Kannst du die Logik direkt in SQL abbilden?

  • Zwar kann der compiler schon viel optimieren, aber achte darauf, dass du im Release baust, dass alles gewährleistet ist, dass der Compiler Schleifen entrollen und Methoden inlinen kann. Oder du machst das eben "von Hand".

  • Parallelisierung ist auch ein guter Weg

  • Vektorisierung (SSE-Instruktionen) ebenfalls, dann müsstest du aber wahrscheinlich auf C/C++ ausweichen.

Was machst du denn genau mit den Daten?

beste Grüße
zommi

27.08.2014 - 14:13 Uhr

Hallo,

Wir reden von grob einer Datenbankabfrage pro Minute und dem anschließenden Senden von 40 TCP Paketen an ein paar Digitalanzeigen.
Und schon fallen Begriffe wie "Last", "Performance"... Premature Optimization?!?! 😉

Da kann man auch ne einzeilige command-pipeline aus nem sql-tool, xargs und netcat im cron-file hinterlegen, fertig 😉

Zum Pogramm:* Ich würde nicht auf Trigger-Features der Datenbank aufbauen. Basic-SQL reicht auch. Und Polling stört hier keinen. Eher noch würde bei bei häufigeren Updates unnötig getriggert werden, wenn eine weniger häufige Anzeigenänderung ausreicht. (Meine Annahme: #DatenUpdates > #AnzeigeUpdates => Pollen besser als Triggern)

  • Ich würde ein internes Modell von dem was du anzeigen möchtest aufbauen. (Quasi Model-View-ViewModel pattern). Also einfach ein Mapping von Reihe auf deine Zahl.
  • Jetzt gibt es eine Komponente (Methode) die mit der SQL-Abfrage-Aggregation dieses Dictionary aktualisiert.
  • Jetzt gibts ne zweite Komponente, die die Werte in dem Dict an die Anzeigen nacheinander sendet.
  • Einen einzigen Timer, der erst Methode 1 und danach Methode 2 aufruft.

So bleibt es erstmal einfach.
Die Trennung in zwei Komponenten und das interne Modell macht es einfacher, Änderungen einzubauen.

Wenn du immer mehr anzeigen bekommst, und das TCP-Pakete absenden "skalieren" muss, kannst du diese Methode intern auf parallel-for oder Async-TCP Methoden umstellen.

Wenn die Datenbankabfragen zu aufwändig werden, kannst du diesen anderen Teil auch umstellen, vielleicht eines Tages auf Trigger, oder häufigere Updates, oder oder. Die einzige Schnittstelle ist das Model.

Und so lange du nur einen einzeigen Timer verwendest, musst du auch nicht über synchronisierte Zugriffe auf das interne Modell nachdenken.

beste Grüße
zommi

26.08.2014 - 10:47 Uhr

Tu mich allerdings noch schwer mit der Syntax.

Das glaub ich, RegEx sind ja noch wie eine eigene Programmiersprache. Aber da man sie nahezu überall gewinnbringend einsetzen kann, selbst auf der Konsole, lohnt sich der Lernaufwand.
Regex und SQL sollte in jeden Programmiererwerkzeugkasten gehören, egal welche Basis-Sprache man verwendet.

Neben den oben geposteten Forums-Link kann ich auch Regex101 empfehlen.
Hier beispielsweise der Link zum obigen Pattern ^\D*(?<number>0*(?<nonzeros>\d+))(?<suffix>.*)$. Es entspricht zwar nicht 100% den Fähigkeiten der .NET-Regexes, kommt dem im pcre Mode aber schon sehr nahe.
Wenn du dann feine .NET-Details testen willst, führt kein Weg an herbivores RegexLab vorbei 😉

beste Grüße
zommi

26.08.2014 - 10:06 Uhr

Hi shufflekeks,

ich würde auch zu Regulären Ausdrücken greifen. Sie machen den Code kürzer, behalten alle Formatierungsinformationen in einem String (und nicht verstreut in sechs verschiedenen Zahlen über deine Methode) und lassen sich wesentlich leichter von der Logik anpassen.

Entweder kannst de wie herbivore vorschlägt, die Teile einzeln auslesen: Das Präfix, die Zahl, der optionale Text; und diese danach wieder zusammensetzen. Dann kannst (und solltest) du auch alle Erwartungen bezüglich der Längen dieser Teile im Code via Asserts oder Exceptions formulieren,

        Regex extractor = new Regex(@"^\D*(?<number>0*(?<nonzeros>\d+))(?<suffix>.*)$");
        {
        Func<string, string> onlyNumber = (string s) => 
        {
            var match = extractor.Match(s);
            if(!match.Success || match.Groups["number"].Value.Length != 6)
                throw new FormatException ("Input does not follow spec.");
            return match.Groups["nonzeros"].Value + match.Groups["suffix"].Value;
        };
        
        // Examples
        Console.WriteLine(onlyNumber("G000123-Example"));
        Console.WriteLine(onlyNumber("GE000123-Example"));
        Console.WriteLine(onlyNumber("G000000-Example"));
        Console.WriteLine(onlyNumber("S000000-"));
        Console.WriteLine(onlyNumber("S000001"));
        Console.WriteLine(onlyNumber("S00000017")); // -> exception
        Console.WriteLine(onlyNumber("S456")); // -> exception
        Console.WriteLine(onlyNumber("F000")); // -> exception

Aber du kannst natürlich auch sagen: Eigentlich will ich nur diesen Präfix und die bis zu 5 führenden Nullen abschneiden.
Dann machst du eben genau das:

        Regex replacer = new Regex(@"(^\D*0{0,5})");
        Func<string, string> onlyNumber = (string s) => replacer.Replace(s,"");
        
        // Examples
        Console.WriteLine(onlyNumber("G000123-Example"));
        Console.WriteLine(onlyNumber("GE000123-Example"));
        Console.WriteLine(onlyNumber("G000000-Example"));
        Console.WriteLine(onlyNumber("S000000-"));
        Console.WriteLine(onlyNumber("S000001"));
        Console.WriteLine(onlyNumber("S00000017"));  // -> enthält noch Nullen
        Console.WriteLine(onlyNumber("S456"));
        Console.WriteLine(onlyNumber("F000"));  // -> Zahl fehlt
        }

Wie du siehst, gibt es aber hierbei zwei Fälle, die vielleicht nicht gerade für eine "robuste" Logik sprechen. Aber dank Regulären Ausdrücken kannst du das ganz leicht anpassen, indem du statt der fixen bis zu 5 Nullen sagst, dass so viele Nullen wie möglich abschneiden willst, aber danach noch mindestens eine Ziffer stehen bleiben soll (da sonst die Zahl Null komplett entfernt wird):

^\D*0*(?=\d)

beste Grüße
zommi

25.08.2014 - 16:01 Uhr

Hey,

wenn du String.Split mit null oder einem leeren Array aufrufst, werden alle Whitespace-Zeichen genommen.
Ich würde es also so hier probieren:


// strip comments
int comment_start = line.IndexOf('\'');
if(comment_start >= 0)
  line = line.Substring(0, comment_start);

// skip empty lines and lines with comments
if(line.IsNullOrWhiteSpace())
  continue; //oder return, oder was anderes...

string[] data = line.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)

beste Grüße
zommi

28.07.2014 - 03:03 Uhr

Hi Charly,

ich kann dir leider nichts zur "preislichen Vorstellung" sagen, aber je nachdem, wie verkaufsfähig dein alleiniger Programmteil ist, würde ich schon kräftig drauflegen - schließlich vertreibt dein Auftraggeber dann deine Software - die du ja schließlich auch selbst hättest vertreiben können und wird nicht zuletzt zum direkten Konkurrenten. Wenn du aber nur im Rahmen eines großen Projektes lediglich etwas Zuarbeit leistest, angeheuert wurdest, weil die internen Entwickler überlastet sind und deine Werksleistung keinen sonderlich eigenständigen Wert hat, dann würde ich eher weniger draufschlagen.

Ansonsten muss ich auch herbivore widersprechen:

Was spielt es da für eine Rolle, ob er seine Einkünfte aus der Nutzung oder dem Verkauf der Software erzielt?

Woraus er seine Einkünfte generiert könnte wettbewerbsrechtlich interessant sein, aber allein aus urheberrechtlicher Sicht ist es höchst relevant ob er es "weitervertreibt" oder "lediglich" nutzt. Zwar kennt das Gesetzt den übergeordneten Begriff "Nutzung", aber differenziert ihn doch ständig aus und spricht häufig von unterschiedlichen "Nutzungsarten". Und je umfangreicher die Nutzungsarten, desto umfangreicher die Vergütung.
Der Urheber ist und bleibt bei uns König...
Denn prinzipiell bleiben so viele Rechte wie möglich beim Urheber. Nur was explizit im Vertrag eingeräumt wird, ist auch gültig. Bei einem Hauch von Schwammigkeit gilt im Zweifel nur das nötigste (§ 31 Abs. 5) und das wenigste (§ 37).

Was wäre, wenn die Produkte des Kunden einen unerwartet hohen Erfolg haben, er sich also dumm und dämlich daran verdienst? Würdest du dann sagen, hey, das alles hast du auch durch meine [:::

Ja, ja und ja 😃 Es ist schließlich mein gutes Recht (§ 32a S. 1).
Ich verweise nur mal auf einen der vielen sogenannten "Bestseller"-Fälle: BGH · Urteil vom 10. Mai 2012 · Az. I ZR 145/11 (Fluch der Karibik)
Der Synchronsprecher von Johnny Depp wurde damals angemessen entlohnt, aber der Film wurde ein so mordsmäßiger Hit, dass - im Nachhinein betrachtet - die damalige Vergütung unangemessen schien.
Ähnliche Fälle gabs zum Kameramann von "Das Boot" oder einem Drehbuchautor von "Alarm für Cobra Elf". Also warum andere Maßstäbe für Softwareentwickler?

Deshalb ist es auch bei den mehr künstlerischen Berufen nicht unüblich solch ein potentiell erhöhtes zukünftiges Honorar bereits im Vertrag festzusetzen um spätere Streitereien zu vermeiden.

Darüber hinaus hat der Weitervertrieb gegenüber den anderen Nutzungsarten eine völlig andere Qualität, was schon § 35 unterstreicht, da ein Rechteinhaber nur mit Zustimmung des Urhebers weitere Nutzungsrechte an Dritte einräumen darf. Deshalb ist es auch nicht mit der bloßen Mehrfachnutzung und Kopie innerhalb des Unternehmens zu vergleichen. Denn auf einmal wird jemand völlig Neues ein Rechteinhaber an meinem Werk.
Ebenfalls eine neue Qualität hat die Änderung des Werkes (§39), die auch explizit erst vereinbart werden muss und sich natürlich gegenüber der reinen Nutzung auch in der Honorierung bemerkbar machen muss.
Gleiches gilt auch für neue, unbekannte Nutzungsarten (§ 32).
Überdies bekommt der Auftraggeber mit jeder neuen Nutzungsart auch umfangreichere Ansprüche gegenüber dem Auftragnehmer. So kann bei ebenfalls übergebenem Quellcode inklusive aller relevanten Nutzungsrechte durchaus ein Mangel vorliegen, wenn dieser schlecht dokumentiert ist. Das korrespondierende Recht auf Nachbesserung muss natürlich auch seinen Gegenwert in einem Mehr an Bezahlung haben.
Und und und...

All diese Punkte unterstreichen die Notwendigkeit einer differenzierten und oftmals alles andere als "pauschalen" Lizenzvergütung.

Aber natürlich nicht vergessen... "angemessen" gilt in beide Richtungen 😉

beste Grüße
zommi

18.05.2014 - 21:25 Uhr

Hi Mallet,

bei Anwendung "klaut" Konsolenfenster? konnten wir zwar nicht alles lösen, aber vielleicht hilft dir auch die Funktion GetConsoleWindow.

beste Grüße
zommi

05.05.2014 - 18:18 Uhr

Unter Windows könnte dein Dienst auch auf Wunsch ein Datei-Handle an den anfragenden Prozess weiterreichen. Also Client sagt Dienst "ich will File XY", Dienst öffnet File XY und sendet das Handle an den Client. Client erzeugt nun einen FileStream aus dem Handle.
Siehe DuplicateHandle.

beste Grüße
zommi

24.03.2014 - 22:33 Uhr
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] position)   

sollte es sein (ohne Gewähr).

Hi, es sollte SizeConst=3 heißen und nicht SizeParamIndex.

Beste Grüße
zommi

23.03.2014 - 14:03 Uhr

Du könntest über die GetConnectivity des Netzwerkes das jenige auswählen, das "NLM_CONNECTIVITY_IPV4_INTERNET" oder "NLM_CONNECTIVITY_IPV6_INTERNET" hat.

Das klingt jedenfalls so, als ob du das bekommen müsstest, was den Zugang ins Internet ermöglicht...

beste Grüße
zommi

22.03.2014 - 20:19 Uhr

allerdings ist die Palette irgendwie kaputt gegangen.

Kannst du das genauer beschreiben?

Ich seh' grad noch, dass dein Minimum-Finden immernoch fehlerbehaftet ist.
Wenn du eine korrekte Index-Farbe angibst, wird einfach abgebrochen, ohne minI zu setzen. Und bei einer 16-Wert Schleife bringt der Abbruch keinen immensen Vorteil 😉
Es müsste also wohl heißen:

int min = int.MaxValue;
int minI = 0;
for (int i = 0; i < 16; i++)
{
    Color cPal = bmp.Palette.Entries[i];
    int r = c.R - cPal.R;
    int g = c.G - cPal.G;
    int b = c.B - cPal.B;
    int d = r * r + g * g + b * b;
    if (d < min)
    {
        min = d;
        minI = i;
    }
}

Darüber hinaus würde ich drüber nachdenken, eine andere Abstandsfunktion für die Farben zu nutzen. Schau mal bei Delta E. Sollte auch im Internet C# Beispielcodes geben.

PS: Pack mal deine Code-Beispiele in C#-Blöcke statt normale Code-Blöcke. Mit Syntax Highlighting ist das etwas besser zu lesen 😉

22.03.2014 - 19:59 Uhr

Aufgefallen ist mir noch folgendes: Du solltest ...

  1. auch minI statt i schreiben.
  2. ImageLockMode.ReadWrite statt ImageLockMode.ReadOnly nutzen, wenn du schreibst
  3. Das Locking/Unlocking aus der Methode herausziehen und nur ein Mal machen statt bei jedem Pixel erneut (das frisst unglaublich viel Zeit) ... Und dafür musst du dann aber selbst den Offset für x-y-Koordinaten ausrechnen, was aber nicht schwer ist.

Nachfolgend noch ein Beispielcode:

void Set8bppIndexedPixel(Bitmap bmp, int x, int y, Color c)
{
    int i;
    int min = int.MaxValue;
    int minI = 0;
    for (i = 0; i < 16; i++)
    {
        Color cPal = bmp.Palette.Entries[i];
        int r = c.R - cPal.R;
        int g = c.G - cPal.G;
        int b = c.B - cPal.B;
        int d = r * r + g * g + b * b;
        if (d == 0) break;
        if (d < min)
        {
            min = d;
            minI = i;
        }
    }
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    PixelFormat lol = System.Drawing.Imaging.PixelFormat.Format4bppIndexed;
    BitmapData bmpData = bmp.LockBits(rect,
                                      ImageLockMode.ReadWrite,
                                      lol);


    int offset = (y * bmpData.Stride) + (x >> 1);
    int currentByte = Marshal.ReadByte(bmpData.Scan0, offset);
    if((x & 1) == 1) // hinteres Nibble setzen
    {
        currentByte = (currentByte & 0xF0) | (minI);
    }
    else // vorderes Nibble setzen
    {
        currentByte = (currentByte & 0x0F) | (minI << 4);
    }
    Marshal.WriteByte(bmpData.Scan0, offset, (byte)currentByte);
    bmp.UnlockBits(bmpData);
}  

beste Grüße
zommi

22.03.2014 - 19:42 Uhr

Hi Sebastian,

bei 4bpp Bitmaps ist jedes Pixel - wie der Name sagt - nur 4 Bits, also ein halbes Byte groß. (auch Nibble genannt)

Allerdings nutzt du Marshal.WriteByte zum setzen eines Pixel-Wertes.
Damit überschreibst du natürlich immer 2 benachbarte Pixel gleichzeitig!
Dein 4Bit-Wert der Variable i wird von der Laufzeit auf 8Bits erweitert und mit Nullen aufgefüllt. Diese Nullen überschreiben dann entsprechend den benachbarten Pixelwert. Die Streifen im Bild sind also immer 1-Pixel auseinander und sollten den Farbwert mit dem Index 0 besitzen.

Da du aber nur ganze Bytes und keine Nibbles mit der Marshal-Klasse editieren kannst, musst du das bisherige Byte auslesen, mit etwas Bit-Gemurxe den neuen Wert in das passende Nibble reinwurschteln und zurückschreiben.

beste Grüße
zommi

18.03.2014 - 20:02 Uhr

Hi kahleberg,

die Signatur aus der Dokumentation ist schonmal ein Anfang.
Sieht eigentlich passend aus...

Da du schreibst, dass du unter 32-Bit unterwegs bist, kann es kein problem mit 64/32-Bit geben.

dann scheint etwas mit der Aufrufkonvention nicht zu stimmen, oder die Dokumentation passt nicht 😉
Steht dazu was in der Dokumentation?
Ist es ein C++-Interface oder simple C-Funktionen?
Du kannst auch mal mit dem DependecyWalker in die DLL schauen...

Du sagst, dass du keine Headerdatei (zur Hand) hast. Dann musst du diese besorgen. Die Hersteller eurer Bibliothek muss ja welche anbieten - gerade wenn man seine Bibliothek aus nativen C-Programmen nutzen möchte.

beste Grüße
zommi

18.03.2014 - 18:55 Uhr

Hi kahleberg,

mit "andere Seite" war gemeint: Der C-Code, nicht dein C# Code. 😉

Irgendwo muss ja diese Funktion EPS48_InitProt definiert sein, mit Parametern, Rückgabewert... das hast du dir ja nicht ausgedacht 😉

Am besten zeigst du die Definition in der Header Datei, wo die EPS48_InitProt Methode definiert wird.

beste Grüße
zommi

18.03.2014 - 15:42 Uhr

Hey Urganot,

eine sehr performante Variante sollte sein:

  1. kleines Bild vorbereiten, das nur ein einziges "Kästchen" zeigt. (wobei aber nur links+oben der Gitterrahmen oder unten+rechts zu sehen sein sollte, da er sonst doppelt so breit wird)
  2. Das als Hintergrundbild zuweisen
  3. yourControl.BackgroundImageLayout = ImageLayout.Tile; setzen

beste Grüße
zommi

18.03.2014 - 15:38 Uhr

Hi Panther,

probier mal in der zweiten for-Schleife im DrawLine die x-y-Werte zu vertauschen.

beste Grüße
zommi

14.03.2014 - 11:07 Uhr

Hi Kaktusfan1707,

Router beschränken zumeist die Broadcast / Multicast Weiterleitung.
Wenn ein Broadcast in deinem Netzwerk nicht funktioniert, probiere mal eine gezieltere Multicast Addresse.
Aber behalte immer im Auge: Im Zielnetzwerk muss es funktionieren. Dort musst du es also auch testen.

Aber die UDP Pakete weitergeleitet werden, kannst du ja Testen, in dem du auf einem zweiten PC im selben Netz mal auf die UDP Pakete lauscht. Oder auch einfach Wireshark anmachst und mitloggst.

Mehrere Threads brauchst du nicht.
Wenn du nur eine Anfrage rausschickst, dann kannst du das blockierend machen und danach auf die Antworten warten.

Du kannst aber gerade bei den Antworten mit den Asynchronen Methoden der Sockets arbeiten.

beste Grüße
zommi

10.03.2014 - 23:32 Uhr

ich doch auch 😃

Damit du aus den eigentlichen C# projekten auch in den nativen Code reinsteppen kannst, musst du eben Mixed-Debugging aktiviert haben.
Zugleich müssen natürlich die nativen Bibliotheken in die du dann reinsteppen willst mit PDBs und Sourcen vorliegen, deren Pfade du deinem Visual Studio aber eben auch mitteilen musst.

beste Grüße
zommi

10.03.2014 - 12:53 Uhr

Hi camelord,
1.In den Projekteigenschaften den Debugger Type auf Mixed setzen. (How to: Debug in Mixed Mode) 1.Den Pfad zu den PDB/Source-Dateien richtig setzen. (Specify Symbol (.pdb) and Source Files in the Visual Studio Debugger)

Und natürlich auch die Debug-Builds deines CMake Projekts verwenden.
Dann sollte es eigentlich funktionieren.

Beste Grüße
zommi

06.03.2014 - 13:53 Uhr

Nach meinen Verständnis kann man die Windows Search schon erweitern. Aber da das wieder alles auf nativem Code aufsetzt, ist .NET als Plugin hier etwas schwierig. Wobei natürlich ab .NET 4 das etwas unproblematischer ist... aber gerade bei einer Suche merkt man den Performanceverlust durch ständiges Interop vielleicht schon...

Aber das hier sollte dir als Einstieg sicherlich weiterhelfen:
Windows Search Developer's Guide: Extending the Index

beste Grüße
zommi

06.03.2014 - 13:17 Uhr

Der Code berechnet übrigens CRC16 à la XMODEM mit dem Polynom 0x1021.

Falls du sicherstellen willst, das alles richtig funktioniert, oder nach einer anderen Implementierung Ausschau hälst.

Im Thread CRC-16 Prüfsumme gibts hunderte Einträge dazu 😃

Beste Grüße
zommi

06.03.2014 - 12:56 Uhr

Jap, eine ganz normale for-Schleife tuts.

    static ushort[] crc_table = 
    { 
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 
        0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef 
    }; 
      
      
    static ushort CalculateCrc(char[] data) 
    { 
        ushort crc = 0; 
        int i = 0;
        
        for(int k=0; k<data.Length; k++)
        { 
            i = (crc >> 12) ^ (data[k] >> 4); 
            crc = (ushort) (crc_table[i & 0x0F] ^ (crc << 4)); 
            i = (crc >> 12) ^ (data[k] >> 0); 
            crc = (ushort) (crc_table[i & 0x0F] ^ (crc << 4)); 
        } 
      
        return (ushort)(crc & 0xFFFF); 
    }
    

beste Grüße
zommi

06.03.2014 - 12:40 Uhr

Hi BlackEye,

die MSDN sagt zu WaitForExit:

In the .NET Framework 3.5 and earlier versions, the WaitForExit() overload waited for MaxValue milliseconds (approximately 24 days), not indefinitely.

Also solltest du das zwecks Abwärtskompatibilität in einer Endlosschleife machen:


do 
{
    p.WaitForExit();
} while (!p.HasExited);

Du kannst es auch in eine Extension-Method auslagern... "ReallyWaitForExit" 😉

beste Grüße
zommi

06.03.2014 - 12:32 Uhr

Hi uniklubi,

dir sollte Stackoverflow: TraceRoute and Ping in C# weiterhelfen.

Beste Grüße
zommi

03.03.2014 - 16:17 Uhr

Hi baer,

Tipp 1:
Verwende eine Kompression auf dem gesamten Datenkanal. Dein Protokoll selbst sollte verständlich, klar, möglichst wenig kryptisch sein.
Wenn du sooo viele Daten transferierst, dass du dir um Datums-Kompression sorgen machst, dann ist es eh viel effektiver, wenn du die Gesamtheit komprimierst. So wird auch gleich die Redundanz zwischen den einzelnen Datums-Angaben ausgenutzt.
Ich vermute einfach mal, dass deine Datumsangaben nicht absolut zufällig sind, sondern eher geringerer Entropie sind. Und dann holst du noch viel mehr raus und behälst ein schönes Protokoll.
Einige Protokolle unterstützen ja von sich aus Kompression (SSH, HTTP, ...) oder du nutzt eben einen GZip/DeflateStream.

Alternativer Tipp:
Tage seit 01.01.1970 als UInt16 (a la Unix Timestamp-Sekunden) per base64.
Ergibt 133% * 2bytes = 3 Bytes zur Übertragung.

beste Grüße
zommi

28.02.2014 - 16:39 Uhr

Und es kommt auch noch das richtige raus? 😃
Wenn etwas auf einmal zuu schnell geht... dann macht mich sowas immer skeptisch 😉

28.02.2014 - 12:35 Uhr

Hi LeChiffre,

was du vorhast, ist identisch mit einem Full Outer Join.
Du könntest dafür sogar die Enumerable.Join Methode nutzen, allerdings geht diese pauschal nur für 2 Collections und außerdem nutzt diese es nicht aus, wenn deine Eingangslisten vorsortiert sind.

Und wenn man davon ausgeht, dass innerhalb einer jeden Datei die Einträge nach der Zeit sortiert sind, dann kannst du ähnlich wie beim Merge-Schritt im Merge-Sort deine Dateien On-The-Fly zusammenbringen, in dem du gemeinsam über alle Dateien gleichzeitig iterierst.

Dann sparst du dir das vorherige Erzeugen, das Verwalten eines HashSets, etc...
Vielleicht ist das ja was für dich.
Ich habe es zwar nicht getestet, es könnte aber auch schneller sein.

Hier ist mal eine Implementierung für solch einen SortedFullOuterJoin:

using System.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;

public class Joiner
{
    class Iterator<T>
    {
        internal T Item;
        internal bool HasItem;
        internal IEnumerator<T> Enumerator;

        internal Iterator(IEnumerable<T> xs){
            this.Enumerator = xs.GetEnumerator();
            this.MoveNext();
        }

        internal void MoveNext()
        {
            this.HasItem = this.Enumerator.MoveNext();
            if (this.HasItem)
                this.Item = this.Enumerator.Current;
        }
    }

    public static IEnumerable<IList<T>> SortedFullOuterJoin<T,K>(IEnumerable<IEnumerable<T>> xs, Func<T,K> key)
        where K : IComparable<K>, IEquatable<K>
    {
        var iterators = new List<Iterator<T>>();

        try
        {
            foreach(var x in xs)
                iterators.Add(new Iterator<T>(x));

            while (iterators.Any(e => e.HasItem))
            {        
                K minKey = iterators
                                .Where(e => e.HasItem)
                                .Min(e => key(e.Item));

                var minIterators = iterators
                                    .Where(e => e.HasItem && key(e.Item).Equals(minKey));
                
                yield return minIterators
                                .Select(e => e.Item)
                                .ToArray();

                foreach (var e in minIterators)
                        e.MoveNext();
            }
        }
        finally
        {
            foreach(var it in iterators)
                it.Enumerator.Dispose();
        }
    } 
}


class MainClass
{
    

    class Data
    {
        public int Key { get; set; }
        public string Value { get; set; }

        override public string ToString()
        {
            return String.Format("{0}:{1}", this.Key, this.Value);
        }
    }


    public static void Main()
    {
        var first = new Data[]{
                        new Data{Key=0, Value="a"},
                        new Data{Key=1, Value="b"},
                        //new Data{Key=2, Value="c"},
                        new Data{Key=3, Value="d"},
                        //new Data{Key=4, Value="e"},
                        //new Data{Key=5, Value="f"},
                    };

        var second = new Data[]{
                        new Data{Key=0, Value="A"},
                        //new Data{Key=1, Value="B"},
                        new Data{Key=2, Value="C"},
                        new Data{Key=3, Value="D"},
                        //new Data{Key=4, Value="E"},
                        new Data{Key=5, Value="F"},
                    };

        var third = new Data[]{
                        //new Data{Key=0, Value="Aa"},
                        new Data{Key=1, Value="Bb"},
                        new Data{Key=2, Value="Cc"},
                        new Data{Key=3, Value="Dd"},
                        new Data{Key=4, Value="Ee"},
                        new Data{Key=5, Value="Ff"},
                    };

        var xs = new Data[][]{first, second, third};

        var joined = Joiner.SortedFullOuterJoin(xs, x=>x.Key);
        foreach(var es in joined)
        {
            var text = String.Join(",", es.Select(e => e.Value.ToString()));
            Console.WriteLine(text);
        }
    }
}

Das Beispiel liefert auf der Konsole:


a,A
b,Bb
C,Cc
d,D,Dd
Ee
F,Ff

Deinen Code könnte man dann wie folgt umschreiben: (du musst das aber wohl erst noch zum kompilieren bringen 😉 )


public class RainMeasurement
{
    public string GroupID {get;set;}
    public DateTime Date {get;set;}
    public decimal Value {get;set;}
} 

public static IEnumerable<RainMeasurement> ReadRainFile(string fileName)
{
    var ci = CultureInfo.InvariantCulture.Clone() as CultureInfo;
    ci.NumberFormat.NumberDecimalSeparator = ".";

    var groupID = Path.GetFileName(new FileInfo(fileName).FullName).Substring(0, 2);

    string[] separators = { " ","\t" };
    foreach(var line in File.ReadLines(fileName))
    {
        var items = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        if(items.Length == 4)
        {
            yield return new RainMeasurement{ 
                                GroupID = groupID,
                                Date = Convert.ToDateTime(items[2] + " " + items[3]),
                                Value = decimal.Parse(items[1], ci)};
        }
    }
}

public static void YourMain()
{
    var dtregendaten = MakeTableRegendaten2(19);
  
    var fileNames = Directory.GetFiles(@"*");
    var multiRainItems = fileNames.Select(ReadRainFile);


    var joined = Joiner.SortedFullOuterJoin(multiRainItems, item=>item.Date);
    foreach(var group in joined)
    {
        var dtrow = dt.NewRow();
        dtrow[0] = group[0].Date;
        foreach(var item in group)
            dtrow[item.GroupID] = item.Value;
        dtregendaten.Rows.Add(dtrow);
    }

}

Beste Grüße
zommi

11.02.2014 - 11:28 Uhr

Hi Gnozo,

wenn die Festplatte der Flaschenhals ist und sich deine CPUs langweilen, dann solltest du über eine Kompression nachdenken.
Gzip und co haben selbst einen zu geringen Durchsatz, aber spezialisierte Kompressoren wie LZ4, Snappy oder Blosc sind hervorragend geeignet.

Irgendwie müssen deine Daten ja auf die Platte gekommen sein. Hier einfach direkt komprimiert abspeichern (sollte auch Zeit sparen, da sicherlich auch hier die Festplatte der Flaschenhals ist).
Und dann beim Einlesen on-the-fly entpacken. Etwas mehr Arbeit für die CPU (die bei dir eh nicht ausgelastet ist), aber der Durchsatz erhöht sich drastisch, weil die Festplatte nur noch einen Bruchteil so viel zu tun hat!

Natürlich musst du den Kompressionsgrad passend einstellen, damit das Dekomprimieren nicht doch zum Flaschenhals wird. Aber so schnell sollte das nicht passieren.

PS: Je nachdem, ob du die Dateien mehrfach durchsuchen musst, könntest du vielleicht auch einen Index oder sonstige cleverere Datenstrukturen aufbauen um das Suchen an sich zu beschleunigen, damit nicht sequentiell durchgegangen werden muss.

beste Grüße
zommi

28.01.2014 - 00:52 Uhr

Hallo alle,

Einleitung:
"Geld auf dem Papier" ist nur dann etwas wert, wenn alle akzeptieren, dass es einen Wert hat.
Wenn ich herbivore überrede, mir für den Gegenwert eines Diamanten (knapp 500 euro) seinen Account zu verkaufen, ich aber gerade keine Diamanten dabei habe, dann kann ich ihm zuweilen einen Ersatzwert geben.
Variante a) Ich schreibe ihm auf einen kleinen Zettel "500 Euro", male den noch schön bunt an und gebe ihn ihm.
Variante b) Ich schreibe ihm "Kaufvertrag", Gegenstand, Wert, Datum, Anschrift und Wert, ... auf einen Zettel, lasse ihn noch unterschreiben und gebe ihn ihm.
Variante c) Ich nehme so einen "vorgefertigten Zettel" aus meinem Portemonnaie, wo die Bundesdruckerei mit Wasserzeichen "500 Euro" draufgedruckt hat und gebe ihn ihm.

Was wird er wohl machen?

Variante a wird er nicht akzeptieren, weil er ihn selbst nicht weiter nutzen kann. Denn niemand anders wird ihm den abkaufen und akzeptieren, falls er mal 500 euro weiterzahlen muss. Da dieser Zettel für die restlichen Leute, mit denen herbivore später mal Geschäfte machen möchte, keinen Wert hat, hat er für ihn selbst auch keinen Wert.

Variante b und c wird er akzeptieren, da hier alle anderen akzeptieren, dass herbivore nun wirklich 500 eur hat. Weil alle einen Kaufvertrag nach dem BGB als rechtsverbindlich anerkennen und auch alle sich einig sind, dass ein 500-Eur-Schein 500 eur wert ist. ((In beiden Fällen, weil eine staatliche Authorität diese "alle" überzeugt hat, dass beides einen Wert hat)

Daher tut herbivore das auch.

Nun wollen wir das gleiche mit dem Bitcoin machen. Und dabei zusätzlich noch auf das Papier verzichten.
Wenn ich herbivore also sage: "Hiermit übergebe ich dir 1 Bitcoin". Dann sind das erstmal nichts als Worte. Auch wenn ich das ihm digital übermittle.

Wenn sich herbivore aber nun sicher sein könnte, dass alle anderen meine Worte mit angehört haben, also akzeptieren, dass herbivore nun "reicher" ist. Schließlich haben sie es ja mit angehört. Gleichzeitig werden mich Leute komisch angucken, wenn ich jemand anderem diese Bitcoin "gebe". Schließlich haben sie doch gehört, wie ich diese schon herbivore gegeben habe.

Und die BlockChain ist nun die Liste aller von allen mitgehörten "Bitcoin-Übertragungs-Versprechen"

Und woher weiß man nun, dass "Die Mehrheit" aller Bitcoiner gehört und akzeptiert haben, dass mir Geld geschickt wurde?
Nun, damit eine Transaktion in der BlockChain landet, muss ein aufwändiges Problem gelöst werden (quasi inverses Hashing). Das ist sooo aufwendig, dass einer allein das nicht schaffen kann und sagen wir mal 1.000.000 Rechenstunden benötigt. Wenn ich nun nach aber bereits 1 Stunde sehe, dass das Problem geknackt wurde, dann gehe ich davon aus, dass im Schnitt 1.000.000 Rechner daran beteiligt waren und demnach auch 1.000.000 Personen diese Transaktion gesehen haben müssen.

Nun ist der Schwierigkeitsgrad dieses Problems im Bitcoin-Protokoll variabel, sodass ich immer davon ausgehen kann, dass eine "Mehrheit" die Transaktion gesehen und akzeptiert hat.

Die Lösung des kryptographischen Problems ist daher ein Surrogat für das akzeptieren durch die Mehrheit.

Und genauso wie das reale Mitanhöhren eines mündlichen Versprechens die Aufmerksamkeit kostet, so kostet dies hier eben "digitale Aufmerksamkeit" = Rechenzeit.

Im Grunde brauche ich als Endnutzer (Nicht-Miner) eigentlich nur schauen, welche Transaktionen zu meinen Konten hin (in der globalen Chain) bestätigt wurde. Alles andere ist mir eigentlich egal.
Denn wenn ich selbst an jemanden Geld schicke, dann weiß ich ja, dass ich das gemacht habe, hier habe ich also kein Vertrauensproblem.
Jedoch, wenn umgekehrt jemand behauptet, mir Geld geschickt zu haben, dann "akzeptiere" ich das erst, wenn das Bitcoin-Netzwerk dies bestätigt hat und seine Transaktion auf mein Konto im Kopf der BlockChain enthalten ist.

Ich selbst nutze einen BitcoinClient, der nur einen Bruchteil der BlockChain herunterlädt und lediglich das Ende synchronisiert hält.

Wenn man sagt, dass heutige Staatswährung von einer Zentralen Instanz kontrolliert werden, der alle Vertrauen, dann ist Bitcoin das genaue Gegenteil. Dort vertraut niemand niemandem. Und daher packt Bitcoin direkt das "Byzantine Generals' Problem" an.
Und wenn ich niemandem traue, brauch ich eigentlich auch selbst die komplette Blockchain.
Allerdings gibts es ja nicht nur weiß und schwarz. Und genauso kann eine digitale Währung existieren, die auf ein "verteiltes" Vertrauensverhältnis setzt. Man vertraut nicht einem alleine - aber man misstraut auch nicht allen.
Und in genau einem solchen System reichen auch partielle BlockChains.

beste Grüße
zommi

14.12.2013 - 09:09 Uhr

Hi key-board,

nachfolgend meine Gedanken hierzu:

  • Warum verwendest du eigenständige Prozesse statt Threads in einem einzigen? Gerade wenn du garantiert auf einem einzigen PC läufst, hast du weitaus komfortablere Möglichkeiten zur Parallelisierung (PLinq, ThreadPool, Tasks, TPL, ...)

  • Was du als "Datei im Arbeitsspeicher" beschreibst, nennt sich "Memory-Mapped File" und mit dem MemoryMappedViewStream gibt es .NET Klassen für deren Handling.

  • Komfortabler ist es auf jeden Fall, wenn du auf einer Kommunikations-Middleware aufbaust. Diese Middlewares kann man komfortabel konfigurieren, oder erkennen sogar automatisch, was die effizienteste Kommunikation zwischen zwei Teilnehmern ist. (Ob nun IPC zwischen zwei Prozessen auf dem selben PC, oder IP-Verbindungen zwischen zwei Rechnern, ...)
    Hier könntest du folgendes nutzen*Message Passing Interface (MPI), wofür es mit MPI.NET auch ein Projekt gibt *WCF mit netNamedPipeBinding für Interprozesskommunikation oder netTcpBinding im Netzwerk. *Je nachdem, wie dein Kommunikationsschema aussieht, bietet sich vielleicht auch MessageQueuing an. Gerade bei einem Master-Slave-Szenario wo die Slaves einfach immer mit Arbeit versorgt sein sollen für maximalen Durchsatz bietet sich Queuing an. Hierfür kannst du entweder auch das passende WCF Binding netMsmqBinding wählen, oder auf andere MQ-Server wie RabbitMQ mit .NET Bindings wechseln.

Das schöne ist, dass du mit einer solche Middleware die Möglichkeit hast, die Programme relativ einfach auf UNTERSCHIEDLICHE Rechner zu verteilen. Und schon skaliert deine Anwendung besser, du hast "Cluster-Unterstützung" und die Simulation läuft einfach schneller ab. Klingt doch nicht schlecht, oder? 😃

14.12.2013 - 08:36 Uhr

Hi OhThereIsAPeanut,

Schau mal bei [FAQ] Flackernde Controls vermeiden / Schnelles, flackerfreies Zeichnen vorbei. Dort werden alle Themen rund ums Zeichen behandelt.

Du kannst beim Aufrufen von Invalidate(...) einen Bereich angeben, der sich verändert hat und somit das Neuzeichnen des kompletten Controls verhinden.
Gleichzeitig bekommst du in der OnPaint(...) über den PaintEventArgs Parameter (insbesondere ClipRectangle) mit, welcher Bereich neu gezeichnet werden soll.

Beste Grüße
zommi

13.12.2013 - 00:10 Uhr

Hi vikings,

wenn du die Farben als Strings in Listen/Arrays o.Ä. vorhälst, kannst du mit LINQ und der Intersect-Methode das gewünschte erreichen:

colors1.Intersect(colors2).Count()

beste Grüße
zommi

//Edit: Wenn du von Tabellen sprichst und damit Datenbank-Relationen meinst, sollte dir eine SQL-Query mit COUNT + DISTINCT + JOIN weiterhelfen.

10.12.2013 - 16:34 Uhr

Hi steffen_dec,

abhängig von der Kritikalität deiner Anforderung ist C# vielleicht überhaupt nicht geeignet.

Was einen Einfluss auf den GC hat:*Eine höhere Thread.Priority deiner Main-Threads sollte dich häufiger und den GC seltener ran lassen. *Mit passenden Latency Modes kannst du das Verhalten des GC etwas "zügeln". *Und damit dann trotzdem nichts kaputt geht: Constrained Execution Regions

Woran du vielleicht gedacht hast, ist das fixed Statement, aber dadurch wird nur das Verschieben eines einzelnen Objekts während eines GC-Laufs verhindert.

beste Grüße
zommi

10.12.2013 - 15:06 Uhr

Hallo wakestar,

Strings sind in .Net unveränderlich. Daher können diese auch nicht durch Aufrufe an native Bibliotheken verändert werden.

Wie von herbivore vorgeschlagen, verwendet man korrekterweise den StringBuilder, wenn man einen textuellen Out-Parameter hat. Siehe Default Marshalling for Strings: Fixed-Length String Buffers.

Nun hat VB.NET (anscheinend aus abwärtskompatibilitätsgründen) einige Magie für ByVal Strings. Siehe Why do C# and VB.NET implicitly marshal char* differently?. Daher klappt es in VB auch einfach so.

Beste Grüße
zommi