Laden...

Forenbeiträge von moelski Ingesamt 183 Beiträge

31.03.2014 - 13:13 Uhr

Moin !

Man könnte da nur eine normale Referenz auf ein Objekt übergeben, dessen Properties dann geändert werden können.

Das habe ich fast befürchtet. Dann muss ich mir für mein Console Property Grid was anderes überlegen.

Danke für die Info herbivore

31.03.2014 - 12:49 Uhr

Moin !

Kann ich eine Klassen Property als Referenz an eine andere Klasse übergeben?
Mein Versuch dazu klappt leider nicht:

namespace refTest
{
    using System;

    class Program
    {
        static void Main()
        {
            var data = new MyData { myData = "Hello" };

            var test = new MyProp(ref data.myData);

            test.Change();

            Console.WriteLine(data.myData);
            Console.ReadKey();
        }
    }

    public class MyData
    {
        public string myData { get; set; }
    }

    public class MyProp
    {
        string Test { get; set; }

        public MyProp(ref string input)
        {
            this.Test = input;
        }

        public void Change()
        {
            this.Test += " Welt";
        }
    }
}

Hintergrund ist das ich gerne von einer Klasse in einer Konsolen App bestehende Properties ändern würde.
Mein obiges Beipsiel funktioniert so nicht. Geht das in der Form überhaupt?
Ich könnte (im obigen Beispiel) natürlich die Instanz von MyData übergeben und dann den Wert anpassen. Aber das würde mir in meinem Fall nicht helfen.

21.03.2014 - 09:42 Uhr

Moin !

Sehr hilfreiches Beispiel herbivore. Vielen Dank.
Es muss gar nicht mal so bunt und mit eigenem Fenster sein. Werde mir das mal vornehmen und die für mich wichtigen Teile raus ziehen.

21.03.2014 - 08:18 Uhr

Moin !

Danke für die Infos, nur leider ist das nicht ganz was ich brauche 😉

Ich suche etwas mit dem ich zur Laufzeit der Command Anwendung Settings anpassen kann. Ich möchte damit Einstellungen anpassen die erst zur Laufzeit relevant sind und nicht per command Line Args übergeben werden und auch nicht mittels Settings fest vorgegeben werden können.

Ich mache mal ein fiktives Beispiel zur Verdeutlichung. Ein User möchte CSV Dateien in eine Datenbank importieren. Dabei kommen aber z.B. unterschiedliche CSV Trennzeichen vor und z.B. unterschiedliche Zeilenende.
Was ich suche wäre nun eine Klasse mit der ich Eigenschaften einstellen kann. Das könnte dann so aussehen:

CSV Trenner   ;
Zeilenende    CR/LF

Der User sollte nun z.B. mit Cursor raus / runter eine Eigenschaft auswählen können und mit links / rechts aus einer gegebenen Liste selektieren. Oder bei einem Feld wo ein Text eingegeben werden muss mittels Enter eine Texteingabe anzeigen.

Halt ähnlich wie eine PropertyGrid in GUI Anwendungen.

20.03.2014 - 15:44 Uhr

Moin !

Wir entwickeln eine Anwendung die über Class Libaries Geräte anbindet (bsp: Daten einer Wetterstation auslesen). Diese Libaries werden von einer WinForms Anwendung genutzt. Parallel dazu gibt es aber auch eine Consolen Applikation welche die gleichen Libaries zur Datenverarbeitung nutzt. Bis hier auch alles ok.
Nun werden aber demnächst Geräte eingebunden die konfiguriert werden können. Nehmen wir die Wetterstation um z.B. die geografische Lage vorzugeben. Dafür bekommt die Class Library einen Dialog und in der GUI Anwendung ist damit auch alles im Lot.
Jetzt bin ich am überlegen wie ich die Settings auch aus der Consolen App erreichbar machen kann. Gibt es da evtl. eine fertige Klasse die mir eine Art Property Editor auf Textebene bereitstellt? Dann müsste ich das Rad nicht komplett neu erfinden.

25.02.2014 - 08:02 Uhr

Moin Abt,

WMI möchte ich vermeiden. Meine bisherigen Versuche waren da ncht wirklich zielführend und es ist auch im Vergleich zur API eher träge bei der Abfrage.
WDK würde sicher gehen, aber dann muss ich alles umschreiben.

Die Kommunikation an sich klappt ja sobald das Gerät wach ist. Also der Grundlegende Code passt. Es fehlt im Moment eben nur dieses eine Stückchen Code um ein Device aufzuwecken.

25.02.2014 - 07:45 Uhr

Moin !

Wir haben unter Windows 8.1 ein Problem mit der HID kommunikation. Nach ersten Tests liegt das an den neuen Power Management Funktionen von Windows 8.1. Nicht genutzte Geräte werden sehr schnell in einen Sleep Modus gesetzt.
Im Netz gibt es dazu auch zahlreiche Fragen und auch einen Workaround:
After installing Windows 8.1, my USB device doesn't charge or it disconnects and reconnects frequently...

Ich habe nun mal ein paar Tests gemacht und bin zwar zu einer Lösung gekommen, die finde ich allerdings eher unbauchbar.

            SafeHandle handle = DsKernel.CreateFile(
                this.DevicePath,
                FileAccess.ReadWrite,
                FileShare.ReadWrite,
                IntPtr.Zero,
                FileMode.Open,
                DsKernel.FileAttributes.Overlapped,
                IntPtr.Zero);

            success = DsHidApi.HidD_GetAttributes(handle.DangerousGetHandle(), ref DeviceAttributes); 

            if (success)
            {
                this.Vid = DeviceAttributes.VendorID.ToString("X4");
                this.Pid = DeviceAttributes.ProductID.ToString("X4");
                this.Version = DeviceAttributes.VersionNumber;
                
                success = DsHidApi.HidD_GetPreparsedData(handle.DangerousGetHandle(), ref preparsedData); 
                
                DsHidApi.HIDP_CAPS Capabilities = new DsHidApi.HIDP_CAPS(); 
                Int32 result = 0;
                result = DsHidApi.HidP_GetCaps(preparsedData, ref Capabilities);

                int bufferLength = 126;
                byte[] buffer = new byte[bufferLength];

               ############## Thread.sleep(200);

                // Bei Api Call Problemem mit DsLog.ResultOfApiCall("HidD_GetManufacturerString"); abfragen
                if (DsHidApi.HidD_GetManufacturerString(handle.DangerousGetHandle(), buffer, bufferLength))
                {
                    this.Manufacturer = Encoding.Unicode.GetString(buffer).Replace("\0", string.Empty);
                }

Das ist ein Stück aus der HID Enumeration. Es geht u.a. um das Auslesen von Manufacturer, Serial Number und Product String.

Wenn ich den Code ohne das Sleep laufen lasse bekomme ich > Fehlermeldung:

Fehler 31: A device attached to the system is not functioning.

Mit dem Sleep läuft die Kommunikation sauber durch und die Erkennung klappt auch.

Nun ist ein Sleep aber sicher keine Lösung. Drum meine Frage ob jemand eine saubere Lösung für dieses problem kennt. Gibt es evtl. einen Api Call um das Gerät aus dem Sleep Mode zu "befreien" oder kann ich ein Flag so lange abfragen bis das Gerät bereit für eine Kommunikation ist?

25.12.2013 - 06:12 Uhr

Moin !

Ich hoffe hier gibt es ein paar TChart Nutzer die evtl. schon mal ein ähnliches Problem hatten (und gelöst haben).
Und zwar geht es um das Eintragen von Daten in Serien aus einem Thread heraus. Mein bisheriges Vorgehen war folgendes:
* Invoke auf Chart um dann für alle Serien ein Clear aufzurufen. Anschließend noch ein AutoRepaint = false
* jetzt (ohne invoke) die Serien mit Daten befüllen (series.Add(...))
* Invoke auf Chart um Autorepaint = true zu setzen und ein Refresh ausführen.

Ich bin mir nicht sicher ob das der richtige Weg ist, aber es funktioniert mal soweit. Nur wenn ich dann während dem Dateneintragen das Fenster mit dem Chart z.B. vergrößere werden die Serien trotz Autorepaint auf false gezeichnet. Und das für die bis dahin eingetragenen Daten.

Ich hatte auch schon den Fall das ein Fokusverlust der Anwendung einen Repaint ausgelöst hat während dem Dateneintragen.

Ich habe dann getestet was passiert wenn ich auch das Dateneintragen über ein Invoke ausführe. Nur leider führt das zu enormen Performance Verlusten.

Dann habe ich versucht die Problematik mittels lock zu lösen:
* Chart.BeforeDraw > Monitor.Enter(_lockObject)
* Chart.AfterDraw > Monitor.Exit(_lockObject)
* Dateneintragen in Serien gekapselt mittels lock(_lockObject){....}
Das funktioniert auch nicht wirklich. Damit ist die ganze Applikation geblockt. Ein Resize des Fensters ist z.B. nicht mehr möglich.

Kurzum, kann mir jemand ein paar Hinweise geben wie ich ein TChart aus einem Thread raus mit Daten füttern kann ohne das meine Anwendung geblockt wird?

PS: Noch frohe Weihnachten

15.12.2013 - 15:03 Uhr

Moin !

Ich poste morgen mal ein paar meiner Queries. Gleich ist erst Nikolausfeier und Sohnemann muss stramm stehen 😁

Statt ständig alles abzurufen wird nun nur das abgerufen, was tatsächlich in diesem Moment gebraucht wird.

Hmm, aber da ich nur ganz normale Queries absetze rufe ich eh nur das ab was ich brauche.

Wie auch immer ... Ich poste morgen mal ein paar meiner Abfragen. Dann schauen wir weiter.

15.12.2013 - 14:38 Uhr

Moin Abt,

soweit sogut, aber ich nutze das EF nicht. Ich nutze in meiner Anwendung nur ganz normale Abfragen mittels Command + Reader. Und die Queries sind nichtmal sonderlich kompliziert. Es gibt so gut wie keine verketteten / verschachtelten Queries. Im Grunde nutze ich nicht viel mehr als ein paar sehr einfache Inserts und Selects die sich immer nur auf eine Tabelle beziehen. So ganz genau wüsste ich nicht was ich da optimieren soll. 🤔

Ich könnte ja auch noch nachvollziehen das es wegen der EF Anpassungen in Summe etwas langsamer wird. Aber in dem Masse kann ich das nicht nachvollziehen.

Muss ich evtl. mein Connection String was mit angeben was bei 6.5.4 mitunter nicht nötig war?

Und mir fällt gerade noch was ein. Ich habe mal testhalber den MySQL Connector von DevArt verwendet. Damit war ein Grossteil um Faktor 2 schneller als mit dem normalen MySQL Connector...

15.12.2013 - 12:23 Uhr

verwendetes Datenbanksystem: MySQL 5.5.28

Moin !

Ich nutze für unsere Applikation den MySQL Server 5.5.28. Und als MySQL Connector bis dato immer die Version 6.5.4. Nun musste ich VS2012 neu installieren und wollte damit auch den Connector auf die aktuelle Version heben (6.7.4).

Soweit hat das auch alles geklappt, nur ist dadurch alles furchtbar träge geworden. Teilweise 50-100x langsamer (wenn ich mal mein Logging dazu betrachte). Habe dann 6.7.4 wieder deinstalliert und 6.5.4 wieder drauf gemacht. Und schon habe ich wieder die gewohnte Geschwindigkeit.

Hat jemand ähnliches beobachtet? Und gibt es dafür eine Erklärung?

08.11.2013 - 23:26 Uhr

Moin die Herren,

ich denke ich kann zum einen eine Lösung aufzeigen und zum anderen auch (hoffentlich) ein paar
Details erklären ..

Fangen wir mit letzterem an ...
Ich glaube man darf den windows Fehler 997 nicht als Fehler betrachten sondern nur als Hinweis 👅 Und wenn man sich mal genau den fehlertext ansieht "Overlapped I/O Operation Is in Progress" wird auch klar warum. Die Meldung besagt ja nur das eine Overlapped Operation noch am Werken ist. Nicht mehr und nicht weniger. Auf mein Problem gemünzt bedeutet dass das Schreiben oder das Lesen noch nicht zu Ende ist.

Ich bin dann im Netz über die Win Funktion GetOverlappedResult gestolpert. Und irgendwann hatte ich dann wieder mein geschätztes Buch "USB Complete 4th Edition" auf dem Tisch. Und siehe da ... Dort wird das lesen und Schreiben von HID Reports ganz anders gehandhabt nämlich über WriteFile und ReadFile.
Und bei ReadFile gab es dann auch ein Beispiel wie man denn auf den Fehler 997 reagiert. Dort wird ein Event verwendet das durch ReadFile getriggert wird und letztlich einen Timeout liefert oder eben das komplette Ergebnis.

Ich habe das dann mal in eine Demo gegossen und siehe da ... es gibt zwar noch 997 Meldungen, aber die sind auch logisch denn wenn keine daten kommen kann der lesevorgang nicht abgeschlossen werden. Das führt dann zu einem Timeout und das Spiel geht von vorne los.

**Wenn ich die Testanwendung unter Win32 XP laufen lasse habe ich noch ein kleines Problem. Dort sagt er mir den Fehler 3 "The system cannot find the path specified.". Das verstehe ich noch nicht ganz zumal die Daten sauber weggeschrieben werden und WriteFile auch Success meldet.
Vielleicht hat da jemand eine Idee. **

Aber das grundproblem mit Fehler 997 scheint gelöst (oder sagen wir mal verstanden).

Hier noch meine aktuelle Testapp auf das Wesentliche gestrippt:

namespace HIDTest997
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Threading;

    using Microsoft.Win32.SafeHandles;

    class Program 
    {
        private const string strPath = @"\\?\hid#vid_04d8&pid_fd93#9&32d3c4cd&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
        //  private const string strPath = @"\\?\hid#vid_04d8&pid_fd93#6&21343d7a&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
        
        #region Sniffer Stuff

        private static SafeFileHandle readHandle;
        private static SafeFileHandle writeHandle; 

        private static int m_nInputReportLength;

        internal const Int32 FILE_FLAG_OVERLAPPED = 0X40000000;
        internal const Int32 FILE_SHARE_READ = 1;
        internal const Int32 FILE_SHARE_WRITE = 2;
        internal const UInt32 GENERIC_READ = 0X80000000;
        internal const UInt32 GENERIC_WRITE = 0X40000000;
        internal const Int32 INVALID_HANDLE_VALUE = -1;
        internal const Int32 OPEN_EXISTING = 3;
        internal const Int32 WAIT_TIMEOUT = 0X102;
        internal const Int32 WAIT_OBJECT_0 = 0;    

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Int32 CancelIo(SafeFileHandle hFile);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr CreateEvent(IntPtr SecurityAttributes, Boolean bManualReset, Boolean bInitialState, String lpName);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, Int32 hTemplateFile);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern Boolean GetOverlappedResult(SafeFileHandle hFile, IntPtr lpOverlapped, ref Int32 lpNumberOfBytesTransferred, Boolean bWait);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Boolean ReadFile(SafeFileHandle hFile, IntPtr lpBuffer, Int32 nNumberOfBytesToRead, ref Int32 lpNumberOfBytesRead, IntPtr lpOverlapped);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Int32 WaitForSingleObject(IntPtr hHandle, Int32 dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Boolean WriteFile(SafeFileHandle hFile, Byte[] lpBuffer, Int32 nNumberOfBytesToWrite, ref Int32 lpNumberOfBytesWritten, IntPtr lpOverlapped); 

        #endregion
        
        static void Main(string[] args)
        {
            Thread InOutThread = new Thread(DoInOut) { Name = "HID IO Thread" };
        //    this.ContinueThread = true;

            InOutThread.Start();

            byte[] send = new byte[65];
            send[1] = 2;

            writeHandle = CreateFile(strPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, 0);
            if (!writeHandle.IsInvalid)
            {
                Int32 numberOfBytesWritten = 0;
                Boolean success = false; 

                try
                {
                    m_nInputReportLength = 65;
                    success = WriteFile(writeHandle, send, send.Length, ref numberOfBytesWritten, IntPtr.Zero);
                    Console.WriteLine("WriteFile success = " + success + " " + numberOfBytesWritten);

                    success = WriteFile(writeHandle, send, send.Length, ref numberOfBytesWritten, IntPtr.Zero);
                    Console.WriteLine("WriteFile success = " + success + " " + numberOfBytesWritten);

                    if (!success)
                    {
                        if (!writeHandle.IsInvalid)
                        {
                            writeHandle.Close();
                        }
                    } 
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Failed to get the detailed data from the hid.");
                }
            }
            else 
            {
                Console.WriteLine("Handle Invalid");
            }

            Console.ReadKey();
        }

        private static void DoInOut()
        {
            readHandle = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

            IntPtr eventObject = IntPtr.Zero;
            NativeOverlapped HidOverlapped = new NativeOverlapped();
            IntPtr nonManagedBuffer = IntPtr.Zero;
            IntPtr nonManagedOverlapped = IntPtr.Zero;
            Int32 numberOfBytesRead = 0;
            Int32 result = 0;

            var inputReportBuffer = new Byte[65]; 

            while (true)
            {
                try
                {
                    //  Set up the overlapped structure for ReadFile.

                    eventObject = CreateEvent(IntPtr.Zero, false, false, "");

                    HidOverlapped.OffsetLow = 0;
                    HidOverlapped.OffsetHigh = 0;
                    HidOverlapped.EventHandle = eventObject;    

                    // Allocate memory for the input buffer and overlapped structure. 

                    nonManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length);
                    nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(HidOverlapped));
                    Marshal.StructureToPtr(HidOverlapped, nonManagedOverlapped, false);

                    ShowError("Befor Read");
                    var success = ReadFile(readHandle, nonManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, nonManagedOverlapped);
                    ShowError("After Read");

                    if (!success)
                    {
                        Console.WriteLine("waiting for ReadFile");

                        result = WaitForSingleObject(eventObject, 3000);

                        switch (result)
                        {
                            case WAIT_OBJECT_0:

                                //  ReadFile has completed

                                success = true;
                                Console.WriteLine("ReadFile completed successfully.");

                                GetOverlappedResult(readHandle, nonManagedOverlapped, ref numberOfBytesRead, false);

                                break;

                            case WAIT_TIMEOUT:

                                //  Cancel the operation on timeout

                                //CancelTransfer(hidHandle, readHandle, writeHandle, eventObject);
                                Console.WriteLine("Readfile timeout");
                                success = false;
                                break;
                            default:

                                //  Cancel the operation on other error.

                                //CancelTransfer(hidHandle, readHandle, writeHandle, eventObject);
                                Console.WriteLine("Readfile undefined error");
                                success = false;
                                break;
                        }
                    }

                    if (success)
                    {
                        // A report was received.
                        // Copy the received data to inputReportBuffer for the application to use.

                        Marshal.Copy(nonManagedBuffer, inputReportBuffer, 0, numberOfBytesRead);

                        string hex = BitConverter.ToString(inputReportBuffer).Replace("-", " ");
                        Console.WriteLine(hex);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }    
            }
        }

        private static void ShowError(string addon)
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (lastWin32Error != 0)
            {
                string win32ErrorMessage = new Win32Exception(lastWin32Error).Message;
                Console.WriteLine("[{2}] {0} (ErrorID : {1})", win32ErrorMessage, lastWin32Error, addon);
            }
            else
            {
                Console.WriteLine("[{0}] OK", addon);
            }
        }
    }
}
08.11.2013 - 16:07 Uhr

Es ist zum Haare raufen. Beim USB Sniffer von CodeProject habe ich jetzt auch den 997 Fehler.
X(

Langsam habe ich keine Ideen mehr.
Hast du evtl. noch einen Tip herbivore?

Updates:
Ich habe jetzt mal die 32Bit Version auf XP getestet. Dort kriege ich den 997 nicht.
Weiterhin habe ich die UAC auf meinem Win7 64Bit mal ausgeschaltet. Hat aber auch nichts genutzt.

08.11.2013 - 14:58 Uhr

Aha, jetzt wird ein Schuh draus 🙂
Werde ich heute Abend testen. Habe jetzt erst Kinderdienst und Sankt Martin vor der Brust 👅

Nachtrag:
Ich fürchte das ist es auch nicht. Denn wenn ich bei BeginAsyncRead und ReadCompleted einen Breakpoint setze, dann komme ich da auch nur an wenn Daten ankommen. Eine Endlos Loop kann ich da nicht entdecken.
Und die Umsetzung bei dem CodeProject Sample hat das auch genau so implementiert wie oben gezeigt.

08.11.2013 - 14:19 Uhr

Moin !

also das der Auruf der Methode BeginAsynRead() eine Endlosschleife aufbaut, die andauernd ohne Pause liest ist dir klar, oder?

Nein bis jetzt nicht. Warum sollte da eine Endlosschleife entstehen? Das Device sendet nur auf Anfrage. Und wenn mehrere Pakete kommen sollen die ja auch gelesen werden.

Ich nutze doch gerade BeginAsynRead / ReadComplete damit der Empfang möglichst im Hintergrund abläuft.

Vielleicht schreibt zur selben Zeit auch das HID.

Def. nein. Das meldet sich nur auf Anfrage.

08.11.2013 - 12:45 Uhr

Moin !

Ich verstehe es nicht. Es gibt auf CodeProject dieses Sample:
A USB HID Component for C#

Wenn ich die Testanwendung starte klappt alles super. Ich bekomme keine Fehler beim Schreiben.

Nun habe ich den minimalen Code aus der Application rausgezogen der nötig wäre um mit einem HID Device zu kommunizieren. Im Grunde nur ein Kommando hinschicken und das ergebnis empfangen.
Der DevicePath ist hard codiert ...

Hier mal der Code:

namespace HIDTest997
{
    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Runtime.InteropServices;

    using Microsoft.Win32.SafeHandles;

    class Program
    {
        #region Sniffer Stuff

        private static IntPtr m_hHandle;
        private static FileStream m_oFile;
        private static int m_nInputReportLength;

        /// <summary>CreateFile : Open file for read</summary>
        protected const uint GENERIC_READ = 0x80000000;
        /// <summary>CreateFile : Open file for write</summary>
        protected const uint GENERIC_WRITE = 0x40000000;
        /// <summary>CreateFile : Resource to be "created" must exist</summary>
        protected const uint OPEN_EXISTING = 3;
        /// <summary>CreateFile : Open handle for overlapped operations</summary>
        protected const uint FILE_FLAG_OVERLAPPED = 0x40000000;
        /// <summary>Simple representation of the handle returned when CreateFile fails.</summary>
        protected static IntPtr InvalidHandleValue = new IntPtr(-1);

        [DllImport("kernel32.dll", SetLastError = true)]
        protected static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)] string strName, uint nAccess, uint nShareMode, IntPtr lpSecurity, uint nCreationFlags, uint nAttributes, IntPtr lpTemplate);

        #endregion

        static void Main(string[] args)
        {
            string strPath = @"\\?\hid#vid_04d8&pid_fd93#8&2a005973&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
            
            m_hHandle = CreateFile(strPath, GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);

            if (m_hHandle != InvalidHandleValue || m_hHandle == null)
            {
                try
                {
                    m_nInputReportLength = 65; 

                    m_oFile = new FileStream(
                        new SafeFileHandle(m_hHandle, false),
                        FileAccess.Read | FileAccess.Write,
                        m_nInputReportLength,
                        true);

                    BeginAsyncRead(); // kick off the first asynchronous read                              
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Failed to get the detailed data from the hid.");
                }
            }
            else 
            {
                m_hHandle = IntPtr.Zero;
                Console.WriteLine("Failed to create device file");
            }

            byte[] send = new byte[65];
            send[1] = 2;

            ShowError("Befor Write");

            try
            {
                m_oFile.Write(send, 0, 65);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            
            ShowError("After Write");

            Console.ReadKey();
        }

        private static void BeginAsyncRead()
        {
            byte[] arrInputReport = new byte[m_nInputReportLength];

            ShowError("Befor Read");
            
            try
            {
                m_oFile.BeginRead(arrInputReport, 0, m_nInputReportLength, new AsyncCallback(ReadCompleted), arrInputReport);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            
            ShowError("After Read");
        }

        protected static void ReadCompleted(IAsyncResult iResult)
        {
            byte[] arrBuff = (byte[])iResult.AsyncState;	// retrieve the read buffer
            try
            {
                m_oFile.EndRead(iResult);
                try
                {
                    string hex = BitConverter.ToString(arrBuff).Replace("-", " ");
                    Console.WriteLine(hex);
                }
                finally
                {
                    BeginAsyncRead();
                }
            }
            catch (IOException ex)	// if we got an IO exception, the device was removed
            {
                Console.WriteLine("Error IO");
            }
        }

        private static void ShowError(string addon)
        {
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (lastWin32Error != 0)
            {
                string win32ErrorMessage = new Win32Exception(lastWin32Error).Message;
                Console.WriteLine("[{2}] {0} (ErrorID : {1})", win32ErrorMessage, lastWin32Error, addon);
            }
            else
            {
                Console.WriteLine("[{0}] OK", addon);
            }
        }
    }
}

Wenn ich die App Starte dann kommt irgendwann BeginAsyncRead. "Befor Read" liefert noch 0 als Error. Aber sobald das BeginRead durchlaufen wurde kriege ich den 997. Und das obwohl der Code 1:1 kopiert ist aus der Testanwendung von Codeproject. Ich verstehe es gerade nicht 🤔

Wenn ich das BeginAsyncRead auskommentiere dann sollte er ja nur noch das Kommando senden. Aber auch hier kriege ich bei After Write den 997.

immer sauber Dispose()

Im Moment noch nicht. Denn ich möchte ja das Handle und den FileStream nutzen und nicht löschen. Aber du hast Recht. Das muss ich auf jeden Fall noch ergänzen. Hilft aber auch nicht bei meinem Problem - schon getestet.

bzw. Flush()

Flush hift nicht. Habe ich auch noch bei keiner HID umsetzung gesehen dass das wer genutzt hätte.

So ich hoffe der Code ist kurz genug =) und evtl. kann jemand den großen misteriösen Bug sehen. 🤔
Kann doch nicht sein das es an der Consolen App hängt, oder?
Wobei den Fehler 997 hatte ich auch schon in einer WinForms Anwendung.

07.11.2013 - 16:37 Uhr

Moin !

Ich denke mal zum Lesen und Schreiben in den Stream benutzt du den BinaryReader/Writer. Sobald eine dieser Klassen am Stream ist ( ist sie ab Instanzierung ) kann keine andere Klasse den Stream nutzen.

Ich kann deinen Vorschlag heute Abend mal testen. Aber vorab mal hierzu...
Was ich dann aber nicht verstehe warum es mit dem UI Thread (schreiben) und einem Pool Thread (lesen mittels Async) funktioniert, nicht aber mit einem eigenen Thread für Schreiben. 🤔

Interessehalber, über was für ein HID reden wir?

Unterschiedliche ... z.B.
Hitec HTS-Navi 2.4GHz USB Telemetry Receiver
IISI Cockpit

PS: Wäre es ok wenn ich hier mal meine HID Klasse einstelle? Vielleicht sieht ja jemand auf den ersten Blick des Pudels Kern und kann dazu was sagen?

06.11.2013 - 18:08 Uhr

Moin !

Ich denke du solltest probieren ead zum die Threads zu synchronisieren.

Wie könnte ich das angehen? Der Fehler tritt beim Schreiben auf. Und ich schreibe nur einen Output Report mit 65 Byte raus.
Das würde bedeuten dass das Gerät schon mit dem Senden anfängt ohne das der gesamte Report empfangen wurde.
Wie könnte ich den Asynchronen Read denn so lange blocken bis mein Schreibvorgang durch ist ?

bei einem Human Interface Device sollten die Datenmengen eher gering sein.

Sind sie ja im Grunde auch. Das Problem habe ich schon direkt beim ersten Report. Und die Menge der Daten kann ich im Übrigen gar nicht beeinflussen da vom Gerät vorgegeben.

Warum willst du überhaupt aus einem anderen Thread darauf zugreifen?

Ich hätte gerne den Thread für HID Handling getrennt vom UI. Ob das nun zwingend notwendig ist sei mal dahin gestellt. Aber es sollte im Grunde doch funktionieren !?

Mir erscheint es ratsam, nur aus dem GUI-Thread auf das HID zu zugreifen ... so wie es das HID von dir scheinbar auch will.

Müsste es dem HID Device nicht Schnuppe sein von wo die Daten kommen? Auch eine serielle Schnittstelle arbeitet in einem eigenen Thread bezüglich Datenkommunikation. Das sollte bei HID auch klappen.

Nachtrag ...
Ich habe jetzt mal das BeginAsyncRead(); auskommentiert. Bedeutet ja das er erst gar nicht versucht Daten zu lesen. Selbst dann bekomme ich den Fehler 997. 🤔

06.11.2013 - 12:42 Uhr

Moin !

Ich habe mir eine Klasse geschrieben um mit einem HID Device zu kommunizieren. Nur leider stoße ich dabei auf ein Problem beim Daten senden:

Fehlermeldung:
997 overlapped i/o operation is in progress

Der Datenempfang läuft über ein Asynchrones Lesen mittels BeginAsyncRead / ReadCompleted. Das funktioniert auch ohne Probleme.
Das Daten Senden wollte ich auch über einen eigenen Thread lösen. Doch sobald ich die Daten schicke bekomme ich eben den oben genannten Fehler. (Die Daten werden zum Senden in eine Queue eingetragen, der Thread über EventWaitHandle angestoßen und dann die Daten gesendet)
Sende ich die Daten aus dem UI Thread direkt klappt alles anstandslos.

Muss ich aufgrund des asynchronen Lesens evtl. Lesen und Schreiben gegeneinander sperren?

Kann mir evtl. jemand einen Tip geben woren das liegen könnte?

18.10.2013 - 11:04 Uhr

Moin !

WOW 8o
Danke für diesen ausführlichen Test samt Erklärungen !

Meine Variante entspricht ja dann deinem ReplaceArrayForInit.
Ist also nicht so langsam 👍

dass der Flaschenhals die Datenübertragung sein sollte, insbesondere wenn wir über RS232 reden.

Bei RS232 bin ich da voll bei euch. Und für den TCP Traffic wird es dann wohl auch hinreichend optimiert sein 🙂

18.10.2013 - 08:37 Uhr

Die Lösung reicht mir auch erstmal.

Danke allen für die Infos und Ideen.

18.10.2013 - 08:25 Uhr

Moin !

@herbivore:

Aber vermutlich geht es überhaupt nicht um Geschwindigkeit

Das sehe ich etwas anders. Du hast schon Recht das im Moment kein Engpass vorliegt. Aber wie ich im ersten Post schon schrieb möchte ich auch die Daten die vom Netzwerk kommen in einem Logging festhalten. Und da können sehr schnell viele Daten kommen. Und dann könnte ich mir schon vorstellen das man an dieser Konvertierung etwas optimieren kann und sollte.

Ich habe mal deine Ideen in eine Funktion gegossen. Rausgekommen ist das hier:

        public string ReplaceSpecials(string input)
        {
            string[] change =
            {
                "<NUL>", "<SOH>", "<STX>", "<ETX>", "<EOT>", "<ENQ>", "<ACK>", "<BEL>", "<BS>", "<TAB>", "<LF>", "<VT>", "<FF>",
                "<CR>", "<SO>", "<SI>", "<DLE>", "<DC1>", "<DC2>", "<DC3>", "<DC4>", "<NAK>", "<SYN>", "<ETB>", "<CAN>", "<EM>",
                "<SUB>", "<Esc>", "<FS>", "<GS>", "<RS>", "<US>"
            };

            var sb = new StringBuilder(input.Length * 3);

            for (int i = 0; i < input.Length; i++)
            {
                if (input[i] > 31)
                {
                    sb.Append(input[i]);
                }
                else
                {
                    sb.Append(change[input[i]]);
                }
            }

            return sb.ToString();
        }

Was jetzt noch fehlt wäre die Verwendung eines Char Arrays. Das lass ich mal für eine weitere Optimierung offen 🙂

10.10.2013 - 13:51 Uhr

Moin !

@m0rius:
Ich will ja die Sonderzeichen nicht raus filtern sondern durch was lesbares ersetzen. Insofern hilft dein Ansatz nur bedingt.

10.10.2013 - 13:35 Uhr

Moin,

Warum ignorierst du die Steuerzeichen nicht schon in deiner Empfangsroutine?

Weil ich die Daten gerne sehen und analysierenmöchte 😉

Stringbuilder Replace sollte da beim nachträglichen Replace am schnellsten gehen.

Danke. Dann werde ich das mal damit versuchen.

10.10.2013 - 12:36 Uhr

Moin !

Ich möchte in einem Terminalprogramm das RS232 und TCP Daten empfängt gerne die empfangenen Daten u.a. als String darstellen. Das führt aber logischerweise zu Problemen sobald Steuerzeichen ins Spiel kommen (Zeichen 0-31).

Nun würde ich gerne bei eintreffenden Strings diese Zeichen ersetzen. z.B. "\r" in "<CR>" oder auch "0x0D". Was ist dafür die schnellste Variante? Spontan würde ich ja erstmal die normale Replace Methode verwenden. Bin mir aber nicht sicher ob das der sinnvollste und schnellste Weg ist. Ich müsste ja dann 32 Replaces an den String hängen ... 🤔

Hat evtl. jemand eine bessere (und evtl. auch schnellere) Variante zur Hand?

20.08.2013 - 10:54 Uhr

Moin !

Der Farbwert für die Position wäre ein einfacher 3satz wenn man mit dem HSV farbraum arbeitet

korrekt.

Ich werde das damit versuchen.

Danke für die Info.

20.08.2013 - 09:51 Uhr

Moin !

Nein er meint wohl ein Farbband

Genau.

Mit Min / Max / Value hat man ja eine position im Farbband.
Und die Frage war ob es eine einfache Möglichkeit gibt bei gegebener Start & Endfarbe den Farbwert für die Position zu bekommen.

20.08.2013 - 09:13 Uhr

Moin !

Gibt es in C# eine elegante / schnelle Möglichkeit um eine Farbe zu berechnen bei gebenem Min / Max / Value als Double Werte?

Ich möchte damit in einem Chart eine Höhenlinie einfärben.

In .Net gibt es doch einige Klassen für Gradienten. Könnte man sowas evtl. missbrauchen um einen Farbwert zu ermitteln?

Bsp:
Max = 400, Min = 100, Value = 250
ColorMax = Red
ColorMin = Blue
Ergebnis : 50% Blau, 50% Rot als Color Wert (ergibt einen Lila Ton)

Das hier geht schon in die Richtung:
Calculating Color Values for Visualization (C#)

Aber evtl. geht das auch noch einfacher ?

26.04.2013 - 09:58 Uhr

Moin !

Visual Studio 2012 kann das zumindest mit IE 10.

Auch das hatte ich probiert - ohne Erfolg.
Muss man dabei noch was spezielles beachten?

Oder mal anders gefragt ... Wie ist denn die generelle Vorgehensweise dabei? Meine Hoffnung war ja ... Code schreiben, Breakpoint setzen, F5 drücken und wenn ich an die Breakpointstelle komme stoppt das JavaScript und ich lande in VS ...

Aber vielleicht reichen die F12 Entwicklertools im Internet Explorer auch. Da kannst du auch Javascript mit debuggen.

Das geht. Allerdings hatte ich die Hoffnung alles in VS machen zu können. Denn mit VS.PHP habe ich auch einen netten PHP Debugger der in VS läuft. So hätte man eine zentrale Stelle um alle Projektteile zu debuggen.

26.04.2013 - 08:53 Uhr

Moin !

Ich bin gerade dabei eine kleine erweiterung für unsere Anwendung zu erstellen. einmal einen PHP REST Webservice und eine einfache HTML / JavaScript Client Seite welche Daten vom Webservice abrufen und anzeigen soll. Soweit die Idee ...
Nun hatte ich eigentlich gehofft das VS 2012 in der Lage ist pures HTML + JavaScript zu debuggen. Aber ich kriege es nicht zum laufen. Breakpoints werden niemals erreicht.

Im IE (9) habe ich das Debugging für JavaScript aktiviert.

Ich habe mir dann gedacht das ich einfach eine leere ASP.NET Seite nehme und die nur mit nativen HTML Elementen befülle. Wenn ich dort JavaScript einfüge bekomme ich bei einem Breakpoint die Aussage: "The breakpoint will not currently be hit. No symbols have been loaded for this document."

Wenn ich eine normale statische HTML Seite im VS habe mit JavaScript werden die Breakpoints einfach ignoriert.

Kann ich mit VS 2012 überhaupt eine normale HTML Seite + JavaScript debuggen (also den JS Teil)? wenn ja was muss ich tun damit VS auch bei Breakpoints hält?

15.11.2012 - 15:22 Uhr

Moin !

Macht es einen Unterschied, ob du im Debug oder im Release arbeitest?

Nein. In beiden Fällen braucht das While ... read ... ~500-600ms.

MySQL Client sind ja offen

hmm, ob ich das unbedingt möchte 😉 Da bin ich mir noch nicht so sicher ...

15.11.2012 - 14:31 Uhr

verwendetes Datenbanksystem: MySQL 5.5

Moin !

Ich versuche gerade Daten aus einer MySQL DB (Lokal) zu lesen und in einem TChart darzustellen. Klappt auch - allerdings ist mir dabei aufgefallen das die CPU Last recht hoch ist und die Verarbeitung ziemlich langsam.

Zuerst war mein Verdacht TChart, aber das kann ich nun nicht mehr bestätigen. Nach einigen Versuchen muss ich immer mehr zu der Erkenntnis kommen das mySqlDataReader.Read() der Übeltäter ist. Allerdings ist mir total schleierhaft warum 🤔

Mein (abgestrippter) Code sieht so aus:

                            MySqlCommand cmd = new MySqlCommand(stm, conn);
                            mySqlDataReader = cmd.ExecuteReader();

                            while (mySqlDataReader.Read())
                            {
                                int countColumn = mySqlDataReader.FieldCount;
                            }

Das SQL Query liesst aus einer lokalen DB 50.000 Zeilen zu je 12 Werten. (Ich weiss das macht keinen Sinn das in ein Chart zu packen - aber es geht mir auch nur um die Geschwindigkeit der Verarbeitung im Moment 😉 ).

Mein Verständnis der Materie ist folgende:

  1. ExecuteReader führt das Query aus und holt mir das Ergebnis ab.
  2. mySqlDataReader bietet mir dann Methoden um die abgeholten Daten auszuwerten.

Dazu habe ich mal folgenden Versuch gemacht ... Einen Breakpoint nach ExecuteReader setzen, wenn der Code gestoppt hat das Netzwerkinterface deaktivieren, Code weiter laufen lassen. Die Daten sind vorhanden. Also müssten Sie auf jeden Fall schon in meiner Anwendung zur Verfügung stehen.

Wenn ich dann aber die 50000 Zeilen mittels Read lese und nix anderes mache als die Felderzahl zu lesen, dann braucht das schon eine halbe Sekunde auf einem Core I7 mit 2 Ghz. 8o
Was zum Geier macht der denn da? Auf meinem langsamen Testnotebook sieht es noch viel schlimmer aus. Da reden wir schon von mehreren Sekunden - und wie gesagt ... Die Daten werden nicht mal in die Nähe der Grafik gebracht...

Hab ich hier irgendwie einen Gedankenfehler? Oder ist der mySqlDataReader einfach so schlecht? Ich hatte je gehofft das ich auf die Daten des Reader direkt zugreifen kann. Aber leider kommt man ja nicht an die Columns und Rows ran (direkt).

Hat jemand ähnliche Erfahrungen und ggf. eine Optimierung / einen Trick auf Lager?

Grüße Dominik

13.11.2012 - 15:05 Uhr

Moin !

Ich habe heute ein paar Tests gemacht und denke ich bin zu einem Entschluss gekommen 🙂

Zunächst mal gibt es hier sehr schöne Wetterdaten als CSV:
http://www.ed.ac.uk/schools-departments/geosciences/weather-station/download-weather-data

Die habe ich als Grundlage genommen (das allumfassende CSV mit 3,4 Millionen Zeilen Daten) und habe über eine kleine Command App die Daten in die DB geschrieben.

Dabei habe ich die gleichen Daten einmal in eine Tabelle geschrieben die pro Zeile nur einen Wert fasst und dafür aber mit einigen IDs wie Projekt, Gerät, ValueID versehen ist.
Und danaben habe ich eine Tabelle wo die Daten eben "parallel" eingetragen werden. Also so wie es bei meinen dynamisch erzeugten Tabellen der Fall wäre.

Folgende Ergebnisse honnte ich feststellen:

  1. Die Tabellengröße ist bei der Singeltabelle um einiges größer. Für mich auf jeden Fall ein entscheidendes Kriterium.
  2. Die Inserts dauern bei der Single Variante länger in Summe. Ich denke das liegt einfach daran das um Faktor x mehr Inserts laufen als bei der Multi Tabelle.
  3. Die Ergebnisse sind bei der Multi Tabelle einfach schneller auswertbar - da nehme ich in Kauf das ich erst über ein Query den Tabellenamen ermitteln muss. zudem sind die Queries deutlich einfach handhabbar als bei der Single Variante.
  4. Für den User ist es auch einfacher möglich mal eine bestimmte Tabelle aus der DB abzuziehen. Bei der Single Variante hat man wiederum das Problem eines sehr komplizierten Queries oder aber von mehrfachen Abfragen.

Aber ausschalggebend für mich sind defakto Punkt 1 & 2.

12.11.2012 - 20:08 Uhr

Moin !

Das gibt dir nen Key-Value-Pair aus, was du in deinem Programm wunderbar auslesen und weiter verarbeiten kannst, z.B. zum Kurven zeichnen.

Das ist richtig. Aber ich hatte gehofft das ich die Daten als DataSet lesen kann. Das könnte ich dann direkt mit einem Chart verknüpfen.

Wenn ich deinen Vorschlag nutze muss ich erst Code dazwischen packen um die einzelnen Kurven zu extrahieren.

Und das gleiche Problem hätte ich wenn ich mehrere Selects absetze (für jede Kurve). Da entfällt zwar der Code zum Extrahieren, aber dafür muss ich eben jede Kurve mit einem eigenen Select belegen.

Funktioniert zwar beides, ist aber suboptimal ... 😉

12.11.2012 - 16:37 Uhr

Moin !

Aus welchem Grund möchtest du das Ganze in zwei Spalten aufteilen?

Ganz einfach ... Das sind unterschiedliche Werte.
Ich möchte später von ValueID = 1 eine Kurve zeichnen können und von ValueID=2.
Mit deinem Query geht das nicht.

SELECT A.value, B.value
FROM ( select Value from Test where test.ProjectID = 1 AND test.DeviceID = 1 AND test.ValueID = 1 ) A,
( select Value from Test where test.ProjectID = 1 AND test.DeviceID = 1 AND test.ValueID = 2 ) B

Auch das liefert nicht die Ergebnisse die ich mir erhoffe. Wenn man mal dieses als Tabelle voraussetzt:
ValueID, Value
1, 12
2, 23
1, 13
2, 24

Dann kommt da raus:
12, 23
13, 23
12, 24
13, 24

Erwarten würde ich aber das hier:
12, 23
13, 24

12.11.2012 - 14:08 Uhr

Moin !

Ich habe mir jetzt mal zum Testen folgende Tabelle angelegt:

CREATE TABLE `test` (
	`ID` INT(10) NOT NULL AUTO_INCREMENT,
	`ProjectID` INT(10) NULL DEFAULT NULL,
	`DeviceID` INT(10) NULL DEFAULT NULL,
	`ValueID` INT(10) NULL DEFAULT NULL,
	`Value` DOUBLE NULL DEFAULT NULL,
	PRIMARY KEY (`ID`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3;

Und dazu ein paar Daten die sich nur in ValueID unterscheiden.

Mein Test Select war folgender:

SELECT  ( select Value from Test where test.ProjectID = 1 AND test.DeviceID = 1 AND test.ValueID = 1 ) AS A,
        ( select Value from Test where test.ProjectID = 1 AND test.DeviceID = 1 AND test.ValueID = 2  ) AS B

Als Ergebnis meines Queries erhoffte ich mir zwei Spalten als Ergebnis. Leider kommt MySQL damit so nicht klar. Och bekomme den Fehler Subquery returns more than 1 row

Für einen Test mit einer großen Tabelle wäre aber gerade eine solche Abfrage wichtig.
Hat jemand eine Idee ob das geht und wie das Query aussehen sollte?

11.11.2012 - 19:56 Uhr

Moin !

Du hast keinen Überblick über den Aufbau deiner Datenbank, weil du irgendwann (recht schnell...) nicht mehr weißt was vielleicht irgendwann mal noch für Tabellen angelegt werden.

Doch das weiss ich im Grunde ganz genau. Denn diese dyn. erzeugten Tabellen werden einem Projekt zugeordnet.
Und es wird auch nur ein Typ von Tabellen dynamisch angelegt - das sind die Tabellen wo die Daten letztlich drin laden. Die ganze Verwaltung der restlichen Infos passiert natürlich in statischen Tabellen die von Anfang an feststehen.

Es ist auch so das die dynamisch erzeugten Tabellen nicht nur zu einem Gerät gehören. Dazu kommt auch noch ein Projekt. Insofern kann man die dyn. Tabellen recht gut zuordnen.

Für Dein Vorgaben ist eine relationale Datenbank eigentlich nicht unbedingt das richtige.

Jep. Ich habe heute von hbase (Apache) gehört. Das wäre für meine Anwendung sicherlich eher passend. Aber ich möchte mir offen halten das ich die Datenbank (genauso wie die Auswertung der Daten) bei einem ISP hoste. Und da kommt man leider nicht so richtig um MySQL als Quasi Standard herum.

Wenn es Dir um Performance geht, ist mySQL sowieso fehl am Platz.

Also ich habe bei meinen Versuchen mit Inserts (mit / ohne Transaktionen bzw. Inserts mit mehreren Datensätzen) festgestellt das ich bei einem lokalen MySQL Server auf ca. 20000-40000 Zeilen komme pro Sekunde.
Für die Bereiche die ich mit meiner Software addressieren möchte ist das erstmal vollkommen ausreichend.
Undich könnte mir vorstellen das da immer noch Optimierungspotential drin liegt.

Mir ist übrigens noch etwas eingefallen was bei der einen großen Tabelle recht umständlich wäre ... Das ist das Auslesen von Datensätzen. Nehmen wir mal an ich speichere immer der Reihe nach Temp, Feuchte, Regenmenge, Temp, Feuchte, usw.
Wenn ich jetzt die Datenpunkte für Temp lesen will dann kann ich das zwar machen indem ich jeden dritten Wert lese. Aber ich müsste danach die Datenpunkte für Feuchte und danach für Regenmenge lesen. Sprich ich muss jede Kurve einzeln auslesen.
Bei einer Tabelle pro Gerät Kann ich mit einem Select aber gleich alle Daten lesen und kann auch einfacher auf die Daten filtern.

Ich habe jetzt in Summe 6 Meinungen zu dem Thema und bis dato gibt es für beide Seiten die gleiche Anzahl an Befürwortern 😉

11.11.2012 - 12:13 Uhr

verwendetes Datenbanksystem: MySQL 5.5

Moin !

Ich stecke hier gerade in einer Grundsatzdiskussion fest und wollte euch mal um eure Meinung dazu bitten.

Zu meinem Projekt ... Es ist eine Software die von unterschiedlichen Geräten Daten aufzeichnet und die Daten in einer MySQL DB abspeichert. Ein Gerät könnte z.B. eine Wetterstation sein und die Werte sowas wie Luftfeuchte, Temperatur, Regenmenge, ...
Da die Geräte alle unterschiedliche Daten liefern können war mein Ansatz bis dato folgender:

Ich lege für jedes Gerät eine Tabelle an und das auch erst dann wenn das Gerät benutzt wird. Da die Anzahl und Art der Werte eine Gerätes bekannt sind (ist ebenfalls in der DB abgespeichert) kann ich zur Laufzeit eine neue Tabelle für ein neues Gerät anlegen wo ich dann die Daten eintragen kann.
Zudem gibt es natürlich eine Tabelle als Referenz zwischen Gerät und Tabellenname.

Was mir an dem Konzept selber nicht gefällt ist das Verwalten der zur Laufzeit erzeugten Tabellen in einer eigenen Tabelle.

Nun habe ich die andere Meinung gehört das man besser nur eine Tabelle anlegt und dort die Daten einträgt.

Mein Ansatz wäre also so:
Tabelle Gerät 1

Datum,    Wert 1, Wert 2
29.01.01, 100,    200

Tabelle Gerät 2

Datum,    Wert A, Wert B, Wert C
29.01.01, 33,     44,     55

Der Ansatz mit einer Tabelle sähe dann so aus:

Datum,   GeräteID, ValueID, Wert
29.01.01 1,        1,       100
29.01.01 1,        2,       200
29.01.01 2,        3,       33
29.01.01 2,        4,       44
29.01.01 2,        5,       55

Die Frage die sich mir nun stellt - Was ist sinnvoller?

Ich sehe bei beiden Lösungen Vor- und Nachteile:
Bei mehreren Tabellen muss ich eine extra Referenztabelle führen.

Bei einer großen Tabelle sehe ich auf Dauer Performanceprobleme. Ich kann zwar einen Index über die Zeit legen, aber ich habe im Web schon öfters gelesen das große Tabellen mitunter zu Performancekillern werden.

Wie ist eure Meinung zu dem Thema?
Und vielleicht auch generell die Frage - Sollte man zur Laufzeit Tabellen erstellen oder nicht (das beantwortet das Thema vielleicht eh schon)?

05.11.2012 - 07:43 Uhr

Moin jogibear9988,

danke für den Hinweis. Liesst sich erstmal sehr gut.
Muss man nur abwägen ob die zusätzliche DLL sich lohnt. Denn wenn ich das über eine XML Struktur löse müsste ich beim Start der Anwendung nur immer die letzten hinzugefügten Assemblies "scannen" und der XML hinzufügen.

Aber wie gesagt. Danke für den Hinweis 👍

03.11.2012 - 17:45 Uhr

Moin !

Kannst du das anders lösen? Indem z.B. in einer XML-Datei ein Mapping zwischen Schnittstelle und implementierenden Typen hergestellt wird und dann gezielt die Instanz(en) erstellt werden.

So in etwa hatte ich mir das auch schon überlegt.

Das mit dem Profiler werde ich mal versuchen.

03.11.2012 - 15:14 Uhr

Moin !

In unserer Anwendung nutzen wir Assemblies um Berechnungsmodule (Umwandlung von Datenlogger Daten) einzubinden.
Nach dem Programmstart werden alle Assemblies in einem bestimmten Ordner geladen und es wird nach der Implementierung eines bestimmten Interfaces gesucht.

Kann mir jemand sagen welchen Overhead das in meiner Anwendung erzeugt - vor allem im Bezug auf Speicherverbrauch? Denn das Assembly kann ja aus der Default Appdomain nicht mehr entfernt werden.

Nun stellt sich halt die Frage welche Auswirkungen das hat wenn später mal einige Dutzend Assemblies beim Start geprüft werden. Ich habe schon gelesen das man diese Überprüfung auch in einer eigenen AppDomain machen könnte, aber dennoch würde mich mal interessieren welche "Auswirkungen" das Laden hat.

01.11.2012 - 20:32 Uhr

Moin !

IMessageFilter ... Meine mich zu entsinnen das man damit nicht alle Messages abfangen kann.

Aber kann ich auc nochmal einen Blick drauf werfen. Danke !

01.11.2012 - 12:22 Uhr

Moin herbivore,

you made my day 👍 👍

Vielen Dank für deine Hilfe. Funktioniert nun in Console und WinForm Anwendung :evil:

01.11.2012 - 09:03 Uhr

Moin herbivore,

danke für deine Erklärung !

Ich habe jetzt folgendes gemacht:

 Thread t = new Thread(new ThreadStart(StartNewForm));
t.Start();

private void StartNewForm()
{
Application.Run(new TestMessages(DoMessage));
}

private void DoMessage(object sender, MessageReceivedEventArgs eventArgs)
{
HandleDeviceChange(eventArgs.Message);
}

Und der Constructor in meinem Form sieht so aus:

        public delegate void ActiveDeviceRemovedHandler(object sender, MessageReceivedEventArgs e);

        public event ActiveDeviceRemovedHandler DoMessage;

        public TestMessages(ActiveDeviceRemovedHandler DoMessage)
        {
            this.DoMessage += DoMessage;
            InitializeComponent();
        }

Dann kann ich das Formular in einem eigenen Thread starten und über das Event habe ich meinen Hook für die Message Verarbeitung:

        protected override void WndProc(ref Message aMessage)
        {
            if (aMessage.Msg == DsSetupApi.WM_DEVICECHANGE)
            {
                DsLog.Core.LogVerbose(aMessage.ToString());
                if (DoMessage != null)
                {
                    MessageReceivedEventArgs mre = new MessageReceivedEventArgs(aMessage);
                    DoMessage(this, mre);
                }
            }

            base.WndProc(ref aMessage);
        }

Funktioniert soweit ganz gut.

Kannst du mir evtl. noch einen Tip geben wie ich den Thread mit der Form sauber beenden kann?

01.11.2012 - 07:58 Uhr

Moin !

Ich schreibe mir gerade eine ClassLib zur Kommunikation mit RS232 und USB HID. Nun würde ich in der ClassLib gerne eine Möglichkeit schaffen USB Geräte (Arrive / Remove) zu erkennen.
Die Lib soll sowohl in einer Consolen- wie auch WinFormanwendung laufen.

Da die Consolen Anwendung keine Messages mitbekommt war meine Idee folgende :

  • in der ClassLib ein Form hinzufügen
  • Form Instanz erzeugen
  • Form hidden setzen
  • mit Show "aufrufen"

Allerdings klappt das nicht in einer Consolen Anwendung. Denn wie ich gelesen habe reicht ein einfaches Show nicht damit das Form auch die Messages mitbekommt. Das geht zwar mit ShowDialog, aber das ist auch nicht die Lösung weil dann die Anwendung steht ...

Auch der DriveDetect von hier:
Detecting USB Drive Removal in a C# Program
hat mir nicht wirklich weiter geholfen. Denn der funktioniert ebenfalls nicht mit Consolen Anwendungen.

Hat jemand evtl. eine Lösung wie ich sowohl in Console als auch WinForms Anwendung die Messages abgreifen kann?

22.10.2012 - 22:13 Uhr

Moin !

Vermutlich ist die Sache ganz einfach, aber im Moment komme ich auf keinen grünen Zweig ...

Also gegeben seien 3 Klassen:

class core
{
   public string Value { get; set; }
}

class typ1 : core {}

class typ2 : core {}

Bis hier nichts besonderes. Nun möchte ich zwei Listen erzeugen lassen:


List<typ1> list1;
List<typ2> list2;

Diese Listen möchte ich mit einer Methode füllen. Und zwar soll durch die Methode 1-x neue Instanzen von dem jeweiligen List Typ erstellt werden und in der Instanz möchte ich die Properties aus der core Klasse setzen.

Kann mir jemand einen Wink geben wie ich das bewerkstelligen kann? 🤔

21.10.2012 - 21:40 Uhr

Moin !

Ich schreibe mir gerade eine Lib um HID & serielle Ports zu enumerieren über die SetupAPI. Zusätzlich gibt es ja noch die "Enumeration" über DeviceIOControl wo man über die USB HUBs Informationen zu den angeschlossenen Devices abrufen kann - u.a. über IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX.
Ach wenn ich mich mit der Win API etwas schwer tue klappt das meiste dann doch ganz gut 😉

Wenn man nun z.B. einen USB/Seriell Adapter an den Rechner anschließt, dann findet man den in beiden Enumerationen wieder.

Nun frage ich mich aber wie ich diese beiden Enumerationen in Beziehung setzen kann.
Auf der einen Seite habe ich den USB Tree mit den zugehörigen Daten wie man es auf diesem Bild sieht:
siehe Anhang

Auf der anderen Seite habe ich die Infos die ich über SetupDiGetDeviceRegistryProperty bekomme. u.a. z.B. SPDRP_FRIENDLYNAME .

Ich würde jetzt gerne de Daten des USB Trees u.a. mit dem COM Port Namen anreichern. Aber ich habe im Moment keine Idee wie ich von dem USB Device auf die zugehörigen Device Registry Properties komme oder andersrum.

Ich habe erst Versucht über SPDRP_LOCATION_INFORMATION eine Beziehung zu bekommen. Aber diese Property ist gar nicht für alle Devices verfügbar.

Auch über CM_Get_Parent komme ich nicht weiter. Denn da erhalte ich den USB Hub an dem das Device angeschlossen ist.

Hat jemand einen Tip wie ich da vorgehen muss? Geht das überhaupt?
Ich hoffe ich habe es verständlich beschrieben ... 🤔

10.10.2012 - 15:39 Uhr

Moin !

Ich nehme alles zurück. Gebt mir Tiernahmen und Schande über mich.

Habe bei beiden Varianten den Connect zur DB mitgemessen. X(

Mit DataSet bin ich jetzt bei 2560 Werten bei 68ms und mit DataReader bin ich aufmal bei 0,71ms. 8o

Schande auf mein Haupt. Aber wie sagte schon unser Ausbilder immer: Wer mist mist Mist :evil:

10.10.2012 - 15:22 Uhr

Moin !

mysql> EXPLAIN SELECT * FROM data1 LIMIT 2560;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
|  1 | SIMPLE      | data1 | ALL  | NULL          | NULL | NULL    | NULL | 160317 |       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
1 row in set (0.00 sec)

Ich habe jetzt mal über ID einen Index gelegt. Bringt aber erstmal keine Beschelunigung.

Wenn ich das Query mit mysql absetze dann zeigt er mir am Ende folgendes:
2560 rows in set (0.02 sec)

10.10.2012 - 14:05 Uhr

verwendetes Datenbanksystem: MySQL 5.5

Moin !

Nachdem ich mit dem Schreiben in die Datenbank nun ganz zufrieden bin (auch wenn es mit MongoDB noch schneller ginge 🙂 ) versuche ich mich gerade auf der anderen Seite - dem Daten Lesen.

Hier teste ich 2 Varianten und die Ergebnisse sind schon wieder erschreckend ...

  1. Mittels DataReader
    Der relevante Code:
conn = new MySqlConnection(cs);
conn.Open();
string stm = "SELECT * FROM data1 ORDER BY ID DESC LIMIT 2";
MySqlCommand cmd = new MySqlCommand(stm, conn);
rdr = cmd.ExecuteReader();

Die eigentliche Auswertung der Daten ist an dieser Stelle mal irrelevant.

  1. Mittels DataSet
    Der relevante Code:
string stm = "SELECT * FROM data1 ORDER BY ID DESC LIMIT 2";
conn = new MySqlConnection(cs);
conn.Open();
ds = new DataSet();
da = new MySqlDataAdapter(stm, conn);
da.Fill(ds, "data");

Nun habe ich je zwei Versuche unternommen: einmal mit LIMIT 2 und einmal mit LIMIT 2560. (2560 habe ich mal als max Wert genommen weil die Daten später mitunter in einem Chart landen. Und mein Monitor hier hat 2560 Pixel in der Breite.)

Die Ergebnisse:

  1. Mittels DataReader
LIMIT 2      ca 226 ms
LIMIT 2560   ca 218 ms
  1. Mittels DataSet
LIMIT 2      ca 204 ms
LIMIT 2560   ca 280 ms

Die Werte machen mich doch etwas nachdenklich ... Zunächst mal denke ich das die Datenmenge die ich hier Abfrage kaum eine Rolle spielt - der Server läuft auch lokal.
Was mich aber stutzig macht sind die 200ms die immer "verbraucht" werden egal ob DataSet, DataReader und egal ob 2 oder 2560 Zeilen.
Meine Vermutung ist das es an der Reflection liegt denn weder DataReader noch DataSet "kennen" die Daten die sie bekommen.

Kann man hier was optimieren? 🤔