Laden...
T
Benutzerbeschreibung

Forenbeiträge von Torgrimm Ingesamt 24 Beiträge

17.08.2012 - 21:21 Uhr

Vielen Dank erstmal euch beiden

Der von dir, gfoidl, vorgestellte Beitrag klingt schonmal ziemlich vielversprechend. Den Ansatz werde ich mir über das Wochenende einmal näher ansehen. Beim bisherigen Lösungssuchen bin ich auch immer wieder an den Punkt gestoßen, an dem sich der Hund in den Schwanz beißt.

@ Neidhard
Zur Datenmenge:
Dies hängt ein wenig vom überwachten Objekt und der konkreten Konfiguration ab.
Bei höchster Abtastrate sind das ca.: 1,7 MB/s. (Hier wird auf 11 Kanälen gemessen)
Die längsten Messungen wären bei der Konfiguration ca. 2 GB groß.

Theoretisch sind jedoch auch Maschinen im Dauerbetrieb als Überwachungsobjekte interessant. Hier würde die tatsächliche Datenaufzeichnung allerdings nur nach Verletzung von Grenzwerten erfolgen. Das Szenario ist bisher nur als Erweiterung vorgesehen.

Bei den KeyValuePairs war ich in der Tat etwas undeutlich. Da ich nur zwei Zustände speichere, ist es mir eigentlich egal was am Ende ausgegeben wir. Im konkreten Beispiel habe ich das float gewählt, da auf diese Weise die Schnittstelle nach außen hin gleich der der Analogdaten ist.

Ich habe den Code diesbezüglich kommentiert.

17.08.2012 - 17:08 Uhr

Hallo,

ich erstelle derzeit ein Datenformat für große Messdateien. Dabei stoßen verschiedene Anforderungen zusammen und mir ist derzeit nicht komplett klar, wie ich diese alle technisch am besten vereine.

  1. Die Messdateien bestehen sowohl aus Header, als auch aus binären Messdaten. Die Headerdaten sollen in einem XML Format abgelegt sein.
  2. Die Messdatei muss hinsichtlich ihrer Struktur auf n-Analoge (float) und m-Digitale Kanäle ausgelegt sein. (n reicht dabei in der Praxis von 2 bis ~64, m von 8 bis 64)
  3. Eine Datei sollte hinsichtlich ihrer Größe möglichst unbeschränkt sein.
  4. Die Dauer einer Messung ist erst nach ihrem Ende bekannt.
  5. Lesender Zugriff muss wahlfrei möglich sein.
  6. Das Format muss mit dem Streaming Pattern vereinbar sein. Speziell WCF und FileStreams müssen unterstützt werden.
  7. Ein Live-Stream muss möglich sein. Hierbei ist der wahlfreie Zugriff nicht zwingend erforderlich.
  8. Der Aufbau der Headerdaten muss durch die Anwendung definiert werden können.
  9. Wünschenswert wäre eine Möglichkeit eine Datei digital signieren zu können.
  10. Die Speicherung der (digitalen) Daten muss effizient erfolgen. (Hinsichtlich des Speicherverbrauchs)
  11. Das Format kann keine Aussage über die Form der gespeicherten Analogdaten machen.

Unter einem Livestream verstehe ich in diesem Kontext das On-the-Fly weiterleiten von Messdaten vom Messgerät en die entsprechenden Konsumenten.
Ein Konsument kann z.B. eine grafische Darstellung oder eine Evaluierungsroutine sein. Dabei ist es erforderlich, dass es für den Konsumenten weitestgehend transparent ist, ob er mit Livedaten oder Daten aus dem Dateisystem arbeitet.

Nun meine Fragen:
Kennt jemand eine Implementierung die ähnliches Leistet?
Ich habe bisher vor allem versucht im Bereich von Multimedia (Video/Audio Streaming) Anregungen zu finden. Jedoch sind die Beispiele dort in der Regel schon sehr spezifisch und auf einem hohen Abstraktionslevel.

Mir bereitet derzeit das Live-Streaming Kopfzerbrechen. Im ersten Entwurf wird dieses Feature noch nicht unterstützt. Derzeit werden die Daten zum Zeitpunkt einer Messung im Speicher aufbereitet und bis zum Messungsende im Messungsobjekt gesammelt. Anschließend wird dieses Serialisiert. Um die Serialisierung möglichst einfach zu halten, wird diese über die DataContract und DataMember Attribute gesteuert. Dies hat nur den schwerwiegenden Nachteil, dass es bei längeren Messungen zu OutOfMemoryExceptions kommt. Auch ist ein Livesteam nicht möglich.
Nun möchte ich das Format, bzw. die Serialisierung so erweitern/ändern, dass diese Einschränkungen behoben werden. Welchen Ansatz sollte man hier verfolgen?

Eine Messung ist derzeit vereinfacht so aufgebaut:

	[DataContract]
	[KnownType(typeof(Channel))]
	[KnownType(typeof(BinaryChannel))]
	public class Measurement
	{
		[DataMember]
		public String Header1 { get; set; }
		[DataMember]
		public String Header2 { get; set; }
		[DataMember]
		public String Header3 { get; set; }
		[DataMember]
		public String Header4 { get; set; }
		[DataMember]
		public String Header5 { get; set; }

		[DataMember]
		public List<Channel> Channels { get; set; }
		public List<BinaryChannel> Channels { get; set; }
	}

	[Serializable]
	public class Channel
	{
		public String ChannelHeader1 { get; set; }
		public String ChannelHeader2 { get; set; }
		public String ChannelHeader3 { get; set; }
		public String ChannelHeader4 { get; set; }
		public String ChannelHeader5 { get; set; }

		float this[int index]
		{
			get
			{
				return Values[index];
			}
		}
		private List<Single> Values { get; set; }
	}

	[Serializable]
	public class BinaryChannel
	{
		public String BinaryChannelHeader1 { get; set; }
		public String BinaryChannelHeader2 { get; set; }
		public String BinaryChannelHeader3 { get; set; }
		public String BinaryChannelHeader4 { get; set; }
		public String BinaryChannelHeader5 { get; set; }

		float this[int index]
		{
			get
			{
				// calculate the actual value for the index and return it
				bool b = CalculateStateOfIndex(index);

				return b ? 1.0f : 0.0f; // Return 1 or 0, depending on the state
			}			
		}
		bool CalculateStateOfIndex(int Index) { ... }

		// This is only the internal storage of the Binary Values
		// Each entry in the list, represents a changed state on the binary channel
		// Key: The actual state, Value: Number of occurrences of the Key until the next state change
		private List<KeyValuePair<bool, int>> Values { get; set; }
	}

Extra:
Die Binären Daten werden derzeit unsauberer Weise Messheader mit abgespeichert. Gespeichert wird eine Liste von Paaren aus Zustand sowie Anzahl der Messwerte, dessen.
So wird aus der Binärfolge: 00001111111111111111111111111111111111111111111110000
0;4
1;45
0;4

Da sich die einzelnen Binärkanäle im laufe einer Messung nur selten ändern, kann auf diese Weise ein nicht unerheblicher Teil an Speicherplatz eingespart werden.
Zum live-streamen erscheint mir diese Vorgehensweise allerdings als eher ungeeignet, da die Länge mit jedem neuen Messwert aktualisiert werden müsste. Gibt es ein besseres Verfahren, als die 64 Bit für jeden Messwert neu zu senden?

23.02.2010 - 10:12 Uhr

Kleine Anmerkung noch:
Wenn man die Assign Methode und den '=' Operator vom ConditionalAssignmentParser in den FctParser verschiebt funktioniert das ganze so wie es soll.

Ich bin damit allerdings noch nicht so richtig zufrieden und werde das Thema wieder verfolgen, sobald ich etwas mehr Zeit habe.

22.02.2010 - 15:38 Uhr

EDIT: Vor etwas über einem Jahr dachte ich aber auch noch das man in C# Betriebssysteme in lowlevel entwickeln könnte 😄

Naja, prinzipiell ist das schon möglich und wird auch getan:
http://research.microsoft.com/en-us/projects/singularity/

Es ist nur die Frage wie LowLevel du gehen magst. Im Fall von Singularity sind (wenn mich mein Gedächtnis nicht trügt) ~98% des Codes in C# geschrieben. Der Rest sind aber auch wieder C und Assembler.

18.02.2010 - 18:35 Uhr

Da schaut her:
http://www.microsoft.com/visualstudio/en-us/products/msdn/default.mspx#roadmap

Außerdem sind gerade die MSDN Lizenzinformationen erschienen, die könnten auch interessant sein:
http://download.microsoft.com/download/7/B/1/7B18407A-AC79-4949-A318-A6636D96F497/Visual%20Studio%202010%20Licensing%20-%20Feb-2010.pdf

Besonderen Lob verdient meiner Meinung nach, dass der TFS nun mit jeder MSDN Subscription verfügbar ist.

12.02.2010 - 14:48 Uhr

Hallo,

ich habe mich mal daran gemacht und den offenen Punkt umgesetzt, mehrere Variablen verwenden zu können. Gelöst ist es wie von dir angeregt über ein Konstanten Dictionary. Im Prinzip funktioniert das auch genauso mit den Variablen selbst.

Außerdem ist es nun möglich Konstanten (oder apäter auch Variablen) in einer Formel mit dem '=' Operator zu setzen. Für meinen Anwendungsfall missbrauche ich den Parser ein wenig und arbeite mit Strings anstatt von Zahlen. Daher gibt es zwei neue Reservierte Zeichen, das '@' und das ".

Das '@' schließt eine Konstante ein, die auf ihren Wert aufgelöst werden soll (R-Value).
Das " schließt eine Konstante ein, deren Wert neu zugewiesen werden soll(L-Value) oder aber einen beliebigen String für eine Operation, zB. einen Vergleich (R-Value);

Beide Zeichen sind statisch in der Constant-Klasse belegbar. Die beiden Zeichen fügen sich in der Priorität noch vor den Klammern ein.

Soweit so schön. Dummerweise hat sich hier noch ein Bug eingeschlichen dem ich gerade etwas ratlos gegenüber stehe. Jedenfalls kann ich mir das beobachtete Verhalten nicht richtig erklären. Ihc habe dazu eine Beispielanwendung beigefügt, die den Fehler demonstriert.

Was ist eigentlich los?

Nun, die erste Instanz des Parsers funktioniert ganz tadellos.
Wird diese nun allerdings Zerstört und danach eine neue Instanz erzeugt, scheint die neue in einem bestimmten Kontext noch auf Constants der ersten Instanz zuzugreifen. Ich gehe davon aus, das in der Vererbung irgendwo der Bock steckt. Ich habe jedoch bereits mit allen möglichen Kombinationen von this, base und virtual rumgespielt - jedoch ohne positives Ergebnis.


    class Program
    {
    static void Main(string[] args)
        {
            Test();

            Debug.Print("\n\n\n\nZweiter Durchlauf:");
            Test();

        }

        private static void Test()
        {
            Dictionary<string, string> constantsIn = new Dictionary<string, string>();
            constantsIn.Add("a", "0");
            constantsIn.Add("b", "0");
            constantsIn.Add("c", "0");
            constantsIn.Add("d", "0");
            constantsIn.Add("e", "0");
            constantsIn.Add("f", "0");
            constantsIn.Add("g", "0");
            constantsIn.Add("h", "0");
            constantsIn.Add("i", "0");

            List<string> rules = new List<string>();
            rules.Add("\"a\" = \"1\"");
            rules.Add("\"b\" = \"2\"");
            rules.Add("\"c\" = \"3\"");
            rules.Add("\"d\" = \"4\"");
            rules.Add("\"e\" = \"5\"");
            rules.Add("\"f\" = \"6\"");
            rules.Add("\"g\" = \"7\"");
            rules.Add("\"h\" = \"8\"");
            rules.Add("\"i\" = \"9\"");

            ConditionalAssignmentParser<String, FctParser<Object>, Object> Parser = new ConditionalAssignmentParser<String, FctParser<Object>, Object>();

            // Hinzufügen der benötigten der VariDataObjecte als Konstanten in den Parser - LeftFile
            foreach (string s in constantsIn.Keys)
            {
                try
                {
                    Parser.AddConstant(s, constantsIn[s]);
                }
                catch (Exception ex)
                {
                    Debug.Print("Die Konstante " + s + " konnte dem Parser nicht hinzugefügt werden.", ex);
                }
            }

            foreach (string rule in rules)
            {
                try
                {
                    Parser.Parse(rule);
                }
                catch (Exception ex)
                {
                    Debug.Print("Fehler beim Ausführen einer Regel!", ex);
                }
            }

            // Die Geänderten Konstanten in die TransformFile übernehmen
            foreach (FctParser<String>.Constant result in Parser.Constants.Values)
            {
                Debug.Print(result.sConst + ": " + result.value);
            }
        }
    }

Die Methode Test() zweifach aufgerufen führt zu der folgenden Ausgabe:


Create FctParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 45653674
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 41149443
Create ConditionalParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 45653674
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 41149443
Create FctParser: MathParser.FctParser`1 [System.Object] Hash: 39785641
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.Object]] Hash: 45523402
Create ConditionalAssignmentParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 45653674
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 41149443
[color]
Calculate Vorher a: 00 Constant Hash:41149443
Assign Vorher a: 000 Constant Hash:41149443
Assign sId Hash: -842352705 Constant Hash:41149443
Assign Nachher a: 111 Constant Hash:41149443
Calculate Nachher a: 11 Constant Hash:41149443
Calculate Hash in Calculate a: -842352705 Constant Hash:41149443
[/color]
Calculate Vorher b: 00 Constant Hash:41149443
Assign Vorher b: 000 Constant Hash:41149443
Assign sId Hash: -842352706 Constant Hash:41149443
Assign Nachher b: 222 Constant Hash:41149443
Calculate Nachher b: 22 Constant Hash:41149443
Calculate Hash in Calculate b: -842352706 Constant Hash:41149443
Calculate Vorher c: 00 Constant Hash:41149443
Assign Vorher c: 000 Constant Hash:41149443
Assign sId Hash: -842352707 Constant Hash:41149443
Assign Nachher c: 333 Constant Hash:41149443
Calculate Nachher c: 33 Constant Hash:41149443
Calculate Hash in Calculate c: -842352707 Constant Hash:41149443
Calculate Vorher d: 00 Constant Hash:41149443
Assign Vorher d: 000 Constant Hash:41149443
Assign sId Hash: -842352708 Constant Hash:41149443
Assign Nachher d: 444 Constant Hash:41149443
Calculate Nachher d: 44 Constant Hash:41149443
Calculate Hash in Calculate d: -842352708 Constant Hash:41149443
Calculate Vorher e: 00 Constant Hash:41149443
Assign Vorher e: 000 Constant Hash:41149443
Assign sId Hash: -842352709 Constant Hash:41149443
Assign Nachher e: 555 Constant Hash:41149443
Calculate Nachher e: 55 Constant Hash:41149443
Calculate Hash in Calculate e: -842352709 Constant Hash:41149443
Calculate Vorher f: 00 Constant Hash:41149443
Assign Vorher f: 000 Constant Hash:41149443
Assign sId Hash: -842352710 Constant Hash:41149443
Assign Nachher f: 666 Constant Hash:41149443
Calculate Nachher f: 66 Constant Hash:41149443
Calculate Hash in Calculate f: -842352710 Constant Hash:41149443
Calculate Vorher g: 00 Constant Hash:41149443
Assign Vorher g: 000 Constant Hash:41149443
Assign sId Hash: -842352711 Constant Hash:41149443
Assign Nachher g: 777 Constant Hash:41149443
Calculate Nachher g: 77 Constant Hash:41149443
Calculate Hash in Calculate g: -842352711 Constant Hash:41149443
Calculate Vorher h: 00 Constant Hash:41149443
Assign Vorher h: 000 Constant Hash:41149443
Assign sId Hash: -842352696 Constant Hash:41149443
Assign Nachher h: 888 Constant Hash:41149443
Calculate Nachher h: 88 Constant Hash:41149443
Calculate Hash in Calculate h: -842352696 Constant Hash:41149443
Calculate Vorher i: 00 Constant Hash:41149443
Assign Vorher i: 000 Constant Hash:41149443
Assign sId Hash: -842352697 Constant Hash:41149443
Assign Nachher i: 999 Constant Hash:41149443
Calculate Nachher i: 99 Constant Hash:41149443
Calculate Hash in Calculate i: -842352697 Constant Hash:41149443
T: 1
F: 0
true: 1
false: 0
a: 1
b: 2
c: 3
d: 4
e: 5
f: 6
g: 7
h: 8
i: 9




Zweiter Durchlauf:
Create FctParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 35287174
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 44419000
Create ConditionalParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 35287174
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 44419000
Create FctParser: MathParser.FctParser`1 [System.Object] Hash: 52697953
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.Object]] Hash: 22597652
Create ConditionalAssignmentParser: MathParser.ConditionalAssignmentParser`3 [System.String,MathParser.FctParser`1 [System.Object],System.Object] Hash: 35287174
Constants: System.Collections.Generic.Dictionary`2 [System.String,MathParser.FctParser`1+Constant [System.String]] Hash: 44419000
[color]
Calculate Vorher a: 00 Constant Hash:44419000
Assign Vorher a: 111 Constant Hash:41149443
Assign sId Hash: -842352705 Constant Hash:41149443
Assign Nachher a: 111 Constant Hash:41149443
Calculate Nachher a: 00 Constant Hash:44419000
Calculate Hash in Calculate a: -842352705 Constant Hash:44419000
[/color]
Calculate Vorher b: 00 Constant Hash:44419000
Assign Vorher b: 222 Constant Hash:41149443
Assign sId Hash: -842352706 Constant Hash:41149443
Assign Nachher b: 222 Constant Hash:41149443
Calculate Nachher b: 00 Constant Hash:44419000
Calculate Hash in Calculate b: -842352706 Constant Hash:44419000
Calculate Vorher c: 00 Constant Hash:44419000
Assign Vorher c: 333 Constant Hash:41149443
Assign sId Hash: -842352707 Constant Hash:41149443
Assign Nachher c: 333 Constant Hash:41149443
Calculate Nachher c: 00 Constant Hash:44419000
Calculate Hash in Calculate c: -842352707 Constant Hash:44419000
Calculate Vorher d: 00 Constant Hash:44419000
Assign Vorher d: 444 Constant Hash:41149443
Assign sId Hash: -842352708 Constant Hash:41149443
Assign Nachher d: 444 Constant Hash:41149443
Calculate Nachher d: 00 Constant Hash:44419000
Calculate Hash in Calculate d: -842352708 Constant Hash:44419000
Calculate Vorher e: 00 Constant Hash:44419000
Assign Vorher e: 555 Constant Hash:41149443
Assign sId Hash: -842352709 Constant Hash:41149443
Assign Nachher e: 555 Constant Hash:41149443
Calculate Nachher e: 00 Constant Hash:44419000
Calculate Hash in Calculate e: -842352709 Constant Hash:44419000
Calculate Vorher f: 00 Constant Hash:44419000
Assign Vorher f: 666 Constant Hash:41149443
Assign sId Hash: -842352710 Constant Hash:41149443
Assign Nachher f: 666 Constant Hash:41149443
Calculate Nachher f: 00 Constant Hash:44419000
Calculate Hash in Calculate f: -842352710 Constant Hash:44419000
Calculate Vorher g: 00 Constant Hash:44419000
Assign Vorher g: 777 Constant Hash:41149443
Assign sId Hash: -842352711 Constant Hash:41149443
Assign Nachher g: 777 Constant Hash:41149443
Calculate Nachher g: 00 Constant Hash:44419000
Calculate Hash in Calculate g: -842352711 Constant Hash:44419000
Calculate Vorher h: 00 Constant Hash:44419000
Assign Vorher h: 888 Constant Hash:41149443
Assign sId Hash: -842352696 Constant Hash:41149443
Assign Nachher h: 888 Constant Hash:41149443
Calculate Nachher h: 00 Constant Hash:44419000
Calculate Hash in Calculate h: -842352696 Constant Hash:44419000
Calculate Vorher i: 00 Constant Hash:44419000
Assign Vorher i: 999 Constant Hash:41149443
Assign sId Hash: -842352697 Constant Hash:41149443
Assign Nachher i: 999 Constant Hash:41149443
Calculate Nachher i: 00 Constant Hash:44419000
Calculate Hash in Calculate i: -842352697 Constant Hash:44419000
T: 1
F: 0
true: 1
false: 0
a: 0
b: 0
c: 0
d: 0
e: 0
f: 0
g: 0
h: 0
i: 0
Der Thread 0x16cc hat mit Code 0 (0x0) geendet.
Der Thread 0xd0 hat mit Code 0 (0x0) geendet.
Das Programm "[5004] MathParserTest.vshost.exe: Verwaltet" wurde mit Code 0 (0x0) beendet.

Ich habe zwei equivalente Ausgaben rot Markiert, wie man beim zweiten im Assign Teil sieht, sind die Werte aus dem ersten Teil schon vorbelegt, haben aber außerhalb der Methode andere Werte. Im Debugger kann ich dies jedoch nicht nachvollziehen. Auch der HashValue von Constants ist in diesem Bereich der des Wertes vom ersten durchlauf.

Leert man das Constants Dictionary im Destruktor, erhält man beim Assign immer KeyNotFound.

04.02.2010 - 17:38 Uhr

So, leider komme ich erste Heute dazu das hier weiter zu verfolgen.

Soweit ich das verstanden habe, dient bs.ResumeBinding() nach einem bs.EndEdit() dazu, auch danach noch weitere Änderungen zu akzeptieren, also die Bindung aufrecht zu erhalten.

Hintergrund des ganzen:
Das Formular (sfcTypeLine) hat verschiedene Eingabefelder die an veschiedene Eigenschaften einer gemeinsamen Datenquelle (Objekt) gebunden sind. Nach erfolgreicher Gültigkeitsprüfung lösen die Validated-Ereignisse der einzelnen Eingabefelder das Validated-Ereignis des Formulars aus in dem die Datenquelle über EndEdit() aktualisiert wird. Das ResumeBinding() dient dazu auch weitere Änderungen zu akzeptieren.

Ich kann in meinem Verständins der Funktionalität aber auch falsch liegen. Das Formular war meiner erster Versuch eienr etwas komplexeren Datenbindung.

Anfangs hatte ich auch noch den ToolTip im Validated Ereignis aktualisieren wollen, dadurch kam es zu den beschriebenen Fehlern beim anzeigen des ToolTips. Also zur Konkretisierung meiner ersten Beschreibung:
Der ToolTip wurde im Validated-Ereignis geändert, der Absturz kam aber erst beim Anzeigen des Tooltips (den gelben Rahmen hat es noch gezeichnet). Der Ereignishandler PopUp war in dieser ersten Version nichteinmal implementiert.

01.02.2010 - 13:05 Uhr

MST ist meine Wenigkeit.
Leider ist es schon etwas her (Anfang November), dass das Thema aktuell war. Ich komme nur leider erst jetzt dazu, es auf zu arbeiten.

Und das da zusätzliche Validierungen ausgelöst werden, war auch meine Vermutung. Daher ja der Stack overflow. Allerdings gehe ich eher davon aus, dass die Validierung durch das SetToolTip ausgelöst wird, als durch das DataBinding. Schließlich ist das DataBinding beim funktionierenden Code noch an der selben Stelle.

Gerade, beim Stichpunkt Exception verschluckt, kommt mir aber noch ein Gedanke: wenn sich das Ereignis immer wieder selbst auslöst und es so zum einem StackOverflow kommt, an welcher Stelle muss dann die Exception dann abgefangen werden? An der Stelle, an der der Oberflächenthread erzeugt wird? Der Handler um das Application.Run(new MainForm()) sieht jedenfalls nichts.

Und in wie weit kann das Verhalten in diesem Punkt systemspezifisch sein? Auf diesem Rechner hier, tritt der Absturz weder im Debugger, noch im fertigen, unabhängig laufenden, Release auf. Auf anderen Rechnern habe ich es damals aber nicht im Debugger laufen lassen.

01.02.2010 - 11:29 Uhr

Hallo

ich habe mal wieder eine Frage aus der Kategorie "warum".

Ich habe in einer Windows.Form eine Textbox, deren Inhalt beim Mouseover auch als ToolTip angezeigt werden soll.

Der erste Versuch sah so aus:


    public partial class sfcTypeLine : UserControl, IDisposable
    {
        private void sfcTypeLine_Validated(object sender, EventArgs e)
        {
            try
            {
                this.secureFileCopyTypeBindingSource.EndEdit();
                this.secureFileCopyTypeBindingSource.ResumeBinding();
                sfcTypeLineToolTip.SetToolTip(textBoxSearchPath, textBoxSearchPath.Text);
                sfcTypeLineToolTip.SetToolTip(textBoxTargetPath, textBoxTargetPath.Text);

            }
            catch (Exception ex)
            {
                Logging.LogException("", ex);
            }
        }
     }


Soweit so schön, es hat wunderbar funktioniert - dachte ich. Auf einigen anderen Rechnern jedoch, führte das Anzeigen des Tooltips zum sofortigen beenden des Programms. Auch der Eintrag in die Logdatei wurde nicht mehr geschrieben.
An meinem Entwicklungsrechner konnte ich das Verhalten beim besten Willen nicht nachvollziehen oder reproduzieren.

Danach habe ich ein wenig in den weiten des Netzes gestöbert und schließlich den Code derart geändert.


    public partial class sfcTypeLine : UserControl, IDisposable
    {
        private void sfcTypeLine_Validated(object sender, EventArgs e)
        {
            try
            {
                // MST 26.01.2010 Das ändern des ToolTips im Validated Ereignis 
                // führt auf manchen Systemen zu exceptionlosen Programmabstürzen.
                // Mögliche Ursache: Stack Overflow durch sich wiederholende Validated-Ereignisse?

                this.secureFileCopyTypeBindingSource.EndEdit();
                this.secureFileCopyTypeBindingSource.ResumeBinding();
            }
            catch (Exception ex)
            {
                Logging.LogException("", ex);
            }
        }

        private void sfcTypeLineToolTip_Popup(object sender, PopupEventArgs e)
        {
            try
            {
                ToolTip tt = (ToolTip)sender;
                tt.Popup -= new PopupEventHandler(sfcTypeLineToolTip_Popup);
                sfcTypeLineToolTip.SetToolTip(e.AssociatedControl, e.AssociatedControl.Text);
                tt.Popup += new PopupEventHandler(sfcTypeLineToolTip_Popup);
            }
            catch (Exception ex)
            {
                Logging.LogException("", ex);
            }
     }


Nun funktioniert es wunderbar. Kann mir jemand erklären, warumd das eine funktioniert, das andere nicht?

26.01.2010 - 13:49 Uhr

Hallo Th69,

erstmal - ein wunderbares Tool hast du da erstellt. Auch die Erklärungen dazu sind schön verständlich und wecken wohlige Erinnerungen ans Studium. 👍

20.08.2009 - 19:28 Uhr

Das Reflection nicht die schnellste Technik ist, ist mir wohl bekannt. Wenn ich mich recht entsinne war da irgendwo ein Faktor 1000 für das SetValue gegenüber einem einfachen =.
An den entsprechenden Stellen ist dies aber recht egal. In der konkreten Anwendung muss teilweise mehrere Minuten auf thermische Abkühlungen gewartet werden. Die Methode wird pro Konfigurationsdatei einmal pro Programmablauf aufgerufen - von häufig kann also nicht die Rede sein. Beim Laden einer etwas umfangreicheren Datei muss man tatsächlich eine spürbare Zeit warten (etwa fünf Sekunden).

Ursprünglich habe ich die Methode zum einfachen und allgemeinen einlesen unseres Datenformats für Konfigurationsdateien geschrieben. Dadurch kann ich die Datei an eine Beliebige Klasse "binden". Und nun trat halt der Fall auf, dass ich dne Mechanismus auch für eine Struktur verwenden wollte. Die kleine aber feine Stelle beim SetValue habe ich schlicht übersehen.

Vielen Dank euch für die Erklärung.

Zu der Methode:
Darin ist noch ein wenig Dateiformat spezifisches zum einlesen der Daten, erkennen der Datentypen der Felder des übergebenen Typs und ein paar Convert.To...() und schließlich das SetValue(..)
Weitergehendes würde vermutlich gegen die Datenschutzrichtlinien der Firma verstoßen 😉

20.08.2009 - 11:35 Uhr

Ich habe interessantes unterschiedliches Verhalten zwischen Klassen und Strukturen festgestellt. Dieses ist in DemoClass1.getStructFromFile(..) demonstriert.

Ich gehe davon aus, dass es sich bei einem struct um einen Wertetyp handelt, bei einer class allerdings um einen Referenztyp. Mich wundert nun vor allem Fall 2.

Die Methoden DemoClass2.ParseSektion(..) verrichten wie gewünscht ihren Dienst, nur wird die übergebene Struktur nicht wie erwartet modifiziert. Verwendet man beliebige Klassen für den ersten Parameter, funktioniert der Code wunderbar.

using System;
using System.Reflection;

namespace Demo
{
    public class DemoClass1
    {
        public DemoClass1()
        {
        }
        /// <param name="pathFile">Pfad zur VariDatei mit den Parametern</param>
        /// <param name="Sektion">Einzulesende Sektion</param>
        /// <returns>Der gewünschte struct</returns>
        public static statusstruct getStructFromFile(String pathFile, String Sektion)
        {
            DemoClass2 vari = new DemoClass2();
            // Fehlerhaft, Fall 1
            statusstruct sstruct = new statusstruct();
            DemoClass2.ParseSektion(sstruct, vari, Sektion, 0);
            // Die Felder von sstruct sind nicht modifiziert
            // Ich vermute, es liegt daran, dass es sich bei statusstruct um einen Wertetyp handelt

           // Fehlerhaft, Fall 2
            // Allerdings sollte es dann doch zumindest funktionieren, wenn ich die Struktur explizit 
            // als referenz übergebe?
            DemoClass2.ParseSektion<statusstruct>(ref sstruct, vari, Sektion, 0);
            // Die Felder von sstruct sind nicht modifiziert


            // So funktioniert es, Fall 3
            // Die Struktur explizit "boxen"
            Object sstructObj = new statusstruct();
            DemoClass2.ParseSektion(sstructObj, vari, Sektion, 0);
            statusstruct sstruct2 = (statusstruct)sstructObj;
            // Die Felder von sstructObj sind korrekt modifiziert
            return sstruct;
        }
    }

    public class DemoClass2
    {
        // Funktionierdende Methoden

        /// <summary>
        /// Mit Hilfe dieser Funktion lassen sich Felder eines Beliebigen Objekts mit den Variablen eienr Sektion in
        /// einer Vari Datei füllen. Die Zugriffsmodifizierer (public, protected, etc.) des Objekts werden dabei ignoriert.
        /// Ist die VariDatei noch nicht geöffnet, so wir sie geöffnet und im Anschluss wieder geschlossen.
        /// Gegebenenfalls ruft sich die Funktion rekursiv auf, um auch die Felder der Basistypen des Zielobjects zu 
        /// füllen.
        /// 
        /// "Dank" Reflection ist diese Funktion relativ langsam.
        /// Rekursive Funktion 
        /// </summary>
        /// <param name="targetObject">Das Object, dessen Felder gefüllt werden sollen.</param>
        /// <param name="VariClass">Informationen der VARI Datei die verwendet werden soll.</param>
        /// <param name="Sektion">Die Sektion in der VARI Datei, die durhsucht werden soll.</param>
        /// <param name="traversBaseLevel">traversBaseLevel gibt die maximale Rekursionstiefe der Funktion an.</param>
        public static void ParseSektion(Object targetObject, DemoClass2 VariClass, String Sektion, Int32 traversBaseLevel)
        {
            ParseSektion(targetObject, VariClass, targetObject.GetType(), Sektion, traversBaseLevel);
            // Die Felder von targetObject sind in jedem Fall richtig gesetzt
        }

        /// <summary>
        /// Mit Hilfe dieser Funktion lassen sich Felder eines Beliebigen Objekts mit den Variablen eienr Sektion in
        /// einer Vari Datei füllen. Die Zugriffsmodifizierer (public, protected, etc.) des Objekts werden dabei ignoriert.
        /// Ist die VariDatei noch nicht geöffnet, so wir sie geöffnet und im Anschluss wieder geschlossen.
        /// Gegebenenfalls ruft sich die Funktion rekursiv auf, um auch die Felder der Basistypen des Zielobjects zu 
        /// füllen.
        /// </summary>
        /// <param name="targetObject">Das Object, dessen Felder gefüllt werden sollen.</param>
        /// <param name="VariClass">Informationen der VARI Datei die verwendet werden soll.</param>
        /// <param name="TypeToSearch">Entweder typeof(targetObject) oder typeof(targetObject).BaseType </param>
        /// <param name="Sektion">Die Sektion in der VARI Datei, die durhsucht werden soll.</param>
        /// <param name="traversBaseLevel">traversBaseLevel gibt die maximale Rekursionstiefe der Funktion an.</param>
        public static void ParseSektion(Object targetObject, DemoClass2 VariClass, Type TypeToSearch, String Sektion,
            Int32 traversBaseLevel)
        {
            // Erledige einiges an Formatspezifischen Zeug, 
            // setze schließlich die Felder des Zieltyps mittels FileInfo.SetValue(...)

            FieldInfo[] fi = TypeToSearch.GetFields();
            fi[0].SetValue(targetObject, 1234);

            // Die Felder von targetObject sind in jedem Fall richtig gesetzt
        }



        // Fehlerhafte Methoden

        /// <summary>
        /// Mit Hilfe dieser Funktion lassen sich Felder eines Beliebigen Objekts mit den Variablen eienr Sektion in
        /// einer Vari Datei füllen. Die Zugriffsmodifizierer (public, protected, etc.) des Objekts werden dabei ignoriert.
        /// Ist die VariDatei noch nicht geöffnet, so wir sie geöffnet und im Anschluss wieder geschlossen.
        /// Gegebenenfalls ruft sich die Funktion rekursiv auf, um auch die Felder der Basistypen des Zielobjects zu 
        /// füllen.
        /// 
        /// "Dank" Reflection ist diese Funktion relativ langsam.
        /// Rekursive Funktion 
        /// </summary>
        /// <param name="targetObject">Das Object, dessen Felder gefüllt werden sollen.</param>
        /// <param name="VariClass">Informationen der VARI Datei die verwendet werden soll.</param>
        /// <param name="Sektion">Die Sektion in der VARI Datei, die durhsucht werden soll.</param>
        /// <param name="traversBaseLevel">traversBaseLevel gibt die maximale Rekursionstiefe der Funktion an.</param>
        public static void ParseSektion<T>(ref T targetObject, DemoClass2 VariClass, String Sektion, Int32 traversBaseLevel)
        {
            ParseSektion<T>(ref targetObject, VariClass, targetObject.GetType(), Sektion, traversBaseLevel);
            // Die Felder von targetObject sind in jedem Fall richtig gesetzt
        }

        /// <summary>
        /// Mit Hilfe dieser Funktion lassen sich Felder eines Beliebigen Objekts mit den Variablen eienr Sektion in
        /// einer Vari Datei füllen. Die Zugriffsmodifizierer (public, protected, etc.) des Objekts werden dabei ignoriert.
        /// Ist die VariDatei noch nicht geöffnet, so wir sie geöffnet und im Anschluss wieder geschlossen.
        /// Gegebenenfalls ruft sich die Funktion rekursiv auf, um auch die Felder der Basistypen des Zielobjects zu 
        /// füllen.
        /// </summary>
        /// <param name="targetObject">Das Object, dessen Felder gefüllt werden sollen.</param>
        /// <param name="VariClass">Informationen der VARI Datei die verwendet werden soll.</param>
        /// <param name="TypeToSearch">Entweder typeof(targetObject) oder typeof(targetObject).BaseType </param>
        /// <param name="Sektion">Die Sektion in der VARI Datei, die durhsucht werden soll.</param>
        /// <param name="traversBaseLevel">traversBaseLevel gibt die maximale Rekursionstiefe der Funktion an.</param>
        public static void ParseSektion<T>(ref T targetObject, DemoClass2 VariClass, Type TypeToSearch, String Sektion,
            Int32 traversBaseLevel)
        {
            // Erledige einiges an Formatspezifischen Zeug, 
            // setze schließlich die Felder des Zieltyps mittels FileInfo.SetValue(...)

            FieldInfo[] fi = TypeToSearch.GetFields();
            fi[0].SetValue(targetObject, 1234);

            // Die Felder von targetObject sind in jedem Fall richtig gesetzt
        }
    }

    public struct statusstruct
    {
        int field1;
        int field2;
        // ...
    }
}

14.04.2008 - 12:30 Uhr

Ohne es gerade ausprobieren zu können, aber eine dieser beiden Varianten sollte funktionieren:
Entweder du setzt an den Anfang und an das Ende des Pfades ein " oder aber du arbeitest mit der Path Klasse (evtl. auch Directory). Diese sollte dir einen legalen UNC Pfad bilden können.

07.01.2008 - 13:16 Uhr

Das WebBrowser-Steuerelement ist von den Filetype Einstellungen des lokalen IEs abhängig. D.h. wenn im IE eingetragen ist, dass Word Dateien mit der registrierten Anwendung (also Word) geöffnet werden sollen, wird das WebBrowser-Steuerelement genauso verfahren.

02.07.2007 - 14:37 Uhr

Ansonsten gibt es noch:
( Excel = Microsoft.Office.Interop.Excel )
Excel.Range.Find(...)
Excel.Range.FindNext(...)
Excel.Range.FindPrevious(...)

02.07.2007 - 14:27 Uhr

Der Eintrag dazu inder MSDN (Ausschnitt):

Returns a Microsoft.Office.Core.FileSearch object for use with file searches. This property is available only in Microsoft Windows.
Namespace: Microsoft.Office.Interop.Excel

Ich hab selbst noch nicht mit gearbeitet, bin nur vorhin darüber gestolpert, scheint mir aber das zu sein dass du suchst.

Und ja, ist eine Property, da hatte ich nicht genau aufgepasst.

02.07.2007 - 13:53 Uhr

Schau dir mal Excel._Application.FileSearch an.

02.07.2007 - 12:47 Uhr

Wie nin schon sagte, benötigst du an jener Stelle ein mehrdimensionales Array der Form: Array(AnzahlZeilen,AnzahlSpalten). In deinem Fall ist AnzahlSpalten==1.

Zuvor solltest du deine Range allerdings mit get_Resize einschränken da sonst ungültige Werte in dein Arbeitsblatt geschrieben werden könnten.


// Deklaration von s
String[] s = { {"Koeln",""}, {"Wasser",""}, {"Rhein",""} };

// In deiner Funktion SchreibeZellbereich()
// ..
Range = Range.get_Resize(Werte.GetLength(0), Werte.Rank);
// ..
30.05.2007 - 13:33 Uhr

Ich lade in meiner C# Anwendung eine DLL A zur Laufzeit. Diese DLL ist eine Wrapper DLL für eine DLL B zum Steuern einer externen Hardware. Naturgemäß können hierbei eine Menge Fehler auftreten.
DLL A + B sind in C++ geschrieben, DLL A muss auch von ANSI-C Programmen geladen werden können.

Nach Möglichkeit möchte ich schon in der DLL A die Fehler soweit aufarbeiten dass sie in der Anwendung leicht verarbeitet werden können. Mein Lösungsansatz hierzu war den StandardError Stream für Fehlermeldungen zu nutzen.
Nun scheint es aber so dass ich auf diesen in C# nur über die Process Klasse zugreifen kann.

Gibt es eien Möglichkeit auf den StdErr der eigenen Anwendung umzuleiten oder sonst wie einen StreamReader darauf verweisen zu lassen?

16.05.2007 - 09:58 Uhr

Danke für die Ratschläge,
habe mein Problem inzwischen mit dem Webbrowser Element gelöst. Es geht im Prinzip nur darum ein vom Programm erzeugtes Excel Dokument anzuzeigen ohne dass der Benutzer die Anwendung wechseln muss. (Zur Plausibilitätsprüfung von Ergebnissen)

Es gibt doch sicher eine Möglichkeit abzufragen wie der IE mit bestimmten Dateitypen umzugehen, oder?

11.05.2007 - 13:07 Uhr

Grad nochmal einen Test gemacht, hab die Microsoft.Office.Interop.Excel.dll aus dem GAC heraus in mein Anwendungsverzeichnis kopiert und den Verweis darauf gelegt.

Lässt sich nun kompilieren, allerdings bekomme ich zur Laufzeit diesen Fehler:
Unbehandelte Ausnahme: System.Security.SecurityException: Die Assembly lässt keine Aufrufer zu, die nicht voll vertrauenswürdig sind.

Und selbst wenn es ging wär es ja keine adequate Lösung.

11.05.2007 - 12:51 Uhr

Hab nun einaml die Vollversion installiert, ändert aber nocih iemmr nichts an der Sache.

Ich nehme an, die installierten PIAs sollten dann bei Verweis hinzufügen unter dem Reiter .Net erscheinen, oder?
Assemblys aus dem GAC scheinen sich über den Durchsuchen Reiter garnicht auswählen lassen. Dort befindet sich

Microsoft.Office.Interop.Excel

zumindest

10.05.2007 - 14:07 Uhr

Ich hab ein Problem mit einigen Beispielen bezüglich C# und Excel. In der Regel wird der Namespace
Microsoft.Office.Interop.XXX
eingebunden.

Problem ist nur dass ich den entsprechenden Verweis nicht hinzufügen kann und ich daher den Fehler CS0234 bekomme: Der Typ- oder Namespacename Interop ist im Namespace Microsoft.Office.Interop nicht vorhanden. (Fehlt ein Assemblyverweis?)

Ich habe die primary interop assemblies (PIAs) für Office 2003 installiert

Wenn ich allerdings auf Verweis hinzufügen gehe wird mir nichts in Richtugn Interop angeboten. Angeboten zu Office werden mir:

Microsoft.Office.Tools.Common
Microsoft.Office.Tools.Common2007
Microsoft.Office.Tools.Excel
Microsoft.Office.Tools.Outlook
Microsoft.Office.Tools.Word

Allers in Version 8.0.0.0

Also per Hand nach der entsprchenden DLL im GAC suchen, siehe da die ganzen Interop sachen sind in Version 11.0.0.0 vorhanden, also schnell ausgewählt.
Jedoch...
VS schafft für grob 2 Sekunden etwas, danachbin ich ohne rückmeldung wieder im selben Auswahlfenster, schließe ich es sehe ich dass die dll nicht eingebunden wurde.

Umgebung:
WinXP SP2
VS2005 Professional
VSTO 2005 SE installiert
MS Office 2003
PIA für 2003

Interessant ist dass es auch bei einem in der MSDN mitgelieferten Beispiel nicht geht.

10.05.2007 - 13:02 Uhr

Ist es möglich eine Exceldatei auf einfache Art in einer C# Anwendung anzuzeigen, zu bearbeiten und zu speichern?

Ich will durchaus die normalen Excel Kontrollelemente behalten.

Dazu zwei Lösungsansätze:
Per Internet Explorer Plugin
Ich meine es gibt die Möglichkeit Exceldokumente direkt im Internet Explorer anzuzeigen (Browser Plugin). Allerdings finde ich das Plugin gerade nicht. Vielleicht hat da ja jemand einen Link?

Mit installiertem Plugin würde ich also eine WebBrowser Instanz in meiner Anwendung verwenden und das Dokument so einladen. Funktioniert auf diese weise wunderbar mit PDF Dokumenten.

Nachteil: Es wird wohl nicht möglich sein mit dem Browser Plugin direkt zu speichern, nur speichern unter...

Zweiter Ansatz:
Mittels VSTO irgendwie eine ähnliche Funktionalität erzeugen - vielleicht gibt es ja auch schon Lösungen dafür.

Wäre mir ansich lieber, hab allerdings keine Ahnung ob das realisierbar ist.