Laden...

Forenbeiträge von mosspower Ingesamt 456 Beiträge

20.03.2009 - 11:26 Uhr

Naja, ich würde es weniger von der Größe der Anwendung abhängig machen als vielmehr von der Häufigkeit der zu erwartenden Aktualisierungen. Wenn eine Anwendung nur ein- zweimal im Jahr aktualisiert werden muss, dann darf der Deploymentaufwand auchmal etwas höher sein. Wenn du jetzt was hast, was sich quasi täglich ändern kann dann Webanwendung.

Hm, das verstehe ich jetzt aber nicht so ganz. Es ist doch völlig egal, ob ich fünfmal am Tag Änderrungen beim Webserver einspiele (ggf. ist der Server down, je nach Art der Einspielung) oder fünfmal am Tag der Client sich das neuste Update zieht, wo ist denn hier der Unterschied?

@All,

nehmen wir mal ein Beispiel. Im Lager soll es mehrere Abfertigungsprogramme für den Versand geben. Jetzt gibt es drei Möglichkeiten.

a) Webanwendung
b) Interne Remoting-Anwendung (Logik auf dem Server), wie nennt man das eigentlich? "Thin-Client" in Hochkommata? 😁
c) Fat Client (Logik auf dem jeweiligen Client)

B und / oder C sind dann doch, vorausgesetzt die arbeiten alle mit Windows, die bessere Lösung, da der Entwicklungszeitraum für Windowsapplikationen doch imo schneller geht. Auch sind die nötigen Funktionalitäten, bzw. Anforderungen schneller umzusetzen, als wenn man da zig AJAX-Lösungen reinmacht.

Imo sollte dann doch b hier immer die bevorzugte Lösung sein, oder? Bis vor kurzem hätte ich mich immer für Webanwendung entschieden.

20.03.2009 - 11:15 Uhr

Hallo Lion1984,

alle hier von mir geposteten Controls sind private Entwicklungen, die aber auch, nach Rücksprache, in der Arbeit eingesetzt werden dürfen.

20.03.2009 - 09:30 Uhr

Hallo "Kollegen",

ich möchte hier einmal eine Diskussion eröffnen, wann man und ob man überhaupt Clickonce-Anwendungen (hier meine ich eigenlich Windows-Applikationen, ob nun Forms oder WPF sollte egal sein) und wann ASP.NET-Anwendungen einsetzt werden sollten.

Bis vor kurzem habe ich ja immer Webanwendungen bevorzugt. Ich kannte zwar Java-Webstart, das sich aber nie so ganz durchgesetzt hat. Als ich soetwas bei uns im Unternehmen benötigte, bin ich auf Clickonce gestoßen. Auch habe ich privat zwei Anwendungen laufen, die mit Clickonce arbeiten. Also, ich finde diese Technologie als sehr gute Alternative für Webanwendungen. (Eigentlich sind ja die Clickonce-Anwendungen auch teilweise Webanwendungen).

Was spricht eigentlich dafür und was dagegen? Ich würde mal sagen, dass kleine Programme, wie z.B. Downloadprogramme, Administratoren usw. nicht unbedingt eine Webanwendung sein müssen, hier reicht doch völlig eine Clickonce-Anwendung aus.

Der Nachteil ist sicherlich, dass man, je nach Komplexität der Anwendung, einen eigenen kleinen Server programmieren muss, der die Anfragen entegennimmt und verarbeitet, wie es ja ein Webserver bei Webanwendungen macht.

Wie würdet ihr eigentlich auf die Datenbank gehen? Über "Umweg" einen Server (kleine Konsoleanwendung via z.B. WCF ect.) oder direkt auf eine Datenbank gehen (was ich noch nie gemacht habe, bzw. das sollte doch imo nicht gemacht werden, oder? ... das doch eine große Sicherheitslücke)

OK, die Diskussion ist eröffnet. Ich freue mich auf rege Teilnahme .. und danke schon einmal für eure Meinungen und Erfahrungsberichte im Voraus.

20.03.2009 - 09:15 Uhr

Hallo tom-essen,

na, das ging aber schnell, hatte schon drauf gewartet, dass das kommt 😉 ...

ich gehe eigentlich davon aus, dass das klar sein sollte, dass man den .NET Reflector nicht verwenden sollte. Das Teil wird von mir privat genutzt und auch in der Firma um etwas mal schnell zu verschlüsseln - wenn jemand da unbedingt rankommen will, was ich nicht glaube, denn ich bin ja der Unbekannte User mosspower, dann nutzen ja auch Obfuscatoren nix.

Lass Dir aber versichert sein, dass die wichtigste Codezeile bei mir (also in der Firma und privat) anders aussieht und die zugrundeliegenden Coding-Files auch andere sind.

19.03.2009 - 15:22 Uhr

Alles wurde mit 3.5 compiliert. Testbeispiel anhängend. Entgegen meinen bisherigen Gepflogenheiten werde ich den Quellcode hier nicht posten, warum sollte nachvollziehbar sein 😉 ... viel Spass damit

19.03.2009 - 15:16 Uhr

Anmeldung des Controls:
Im Ordner Codec die DLL UI.Control.Codec.dll auswählen und dann einfach wie gewohnt auf Form ziehen.

**Propery Locale **(Englisch, Deutsch) kann ausgewählt werden, für Anzeigetexte und Fehlertexte.

**Datum-Textfeld **setzt bei Shortcut t(Englisch) und h(Deutsch) heutiges Datum - bei Zahl, z.B. 5, wird der 5. des aktuellen Monats verwendet.
Es kann auch ohne Datumsangabe verschlüsselt werden, jedoch ist dann natürlich die Verschlüsselung immer gleich, bei Datumsangabe muss beim Rückverschlüsseln das richtige Datum angegeben werden.

Format Deutsch: dd.MM.yyyy
Format Englisch: yyyy/MM/dd

Button Encode
Hiermit wird verschlüsselt

Button Decode
Hiermit wird entschlüsselt

CheckBox enable line feeds:
Unterstützt Zeilenumbrüche, also für längere Texte (hier kann dann beim Verschlüsseln auch Zeilenumbrüche gecoded werden)

CheckBox adjust for XML:
Das ist nützlich, wenn man z.B. in XML Dateien verschlüsselten Code hat, wie z.B. &amp;Bla, dann wird das so angepasst, bzw, wenn ><& oder '-Zeichen gecoded wird, dann wird das gleich in XML-Code umgewandelt.

CheckBox match length
Die Länger der Eingabe ist gleich die Länge der Ausgabe, also immer gleich lang

19.03.2009 - 15:10 Uhr

Ver- und Entschlüssler

Wenn man schnell mal einen Text (z.B. Connectionstrings, Zugangsdaten ect.) verschlüsseln muss (und diese auch wieder entschlüsseln), könnt ihr in Zukunft dieses Control benutzen. Screenshot sieht so aus:

Coding, Codec, Verschlüsseln, Entschlüsseln

19.03.2009 - 09:04 Uhr

Also ich mische auf keinen Fall. Da ich aus der "Java-Welt" komme, schreibe ich lediglich Object und String mit großem Anfangsbuchstaben (also .NET-spezifisch) und die primitiven Datentypen (int, double ect.), also C#-spezifisch, bleiben dann wie sie sind. Ist in Java auch so. Was mich ein bißchen aufregt, sind die Generatoren, z.B. bei einer Dictionary<String, String>, dann wird immer = new Dictionary<string, string> in den Quellcode "reingebuttert". 😜

19.03.2009 - 08:56 Uhr

@herbivore,

super, vielen Dank, dann hat sich das geklärt

@marsgk,

war auch ein sehr interessanter Link - vielen Dank!

18.03.2009 - 12:23 Uhr

Hallo "Kollegen",

eine kleine Verständnisfrage. Beim Threading mit Windows Forms Applikations, bzw. beim Zugriff auf Controls, ist es ja nötig, dass man an den Hauptthread, bzw. and den, der das Control erstellt hat, Manipulationen an dem Control durchführt. Jetzt hatte ich hier immer gelesen, dass man das dann mittels Control.Invoke durchführen muss. Was geht da eigentlich "ab" im Hintergrund. Bedeutet dies, dass man "nur" an diesem Control Manipulationen durchführen darf (das glaube ich nicht) oder läuft im Hintergrund "einfach" ein this.Invoke ab, wobei this eine Instanz des Controlerstellers ist. Was ist der Unterschied, wenn es denn überhaupt einen gibt.

Gruß und danke schon einmal für eure Antworten im Voraus.

18.03.2009 - 10:04 Uhr

Ich, als "Nicht-Voll-Atheist", was immer das jetzt auch sein mag, musste wirklich herzlich lachen, als ich den Satz in den Klammern gelesen habe ... [Mit an Sicherheit grenzender Wahrscheinlichkeit] ... LOL, also doch nicht so ganz sicher 😁

17.03.2009 - 02:15 Uhr

Meiner sieht seit Jahren so aus ...

14.03.2009 - 20:18 Uhr

Hallo "Kollegen",

mir ist gerade aufgefallen, dass gestartete Threads, die bereits abgelaufen sind, also Thread-Methode ist zu Ende, trotzdem im Windows Task Manager erscheinen. Ist das OK so? Sorgt hier irgendwann der GC dafür, dass die nicht mehr angezeigt werden? Blöd ist schon, wenn ich meine Applikation zwei Minuten laufen lasse, dass da schon mal über 100 stehen und dann immer weiter hochgezählt werden obwohl die längst nicht mehr laufen, bzw. der Status auf ThreadState.Stopped steht. Ist das alles OK so oder läuft da was "aus dem Ruder"?

Gruß und Danke für etwaige Antworten schon einmal im Voraus.

14.03.2009 - 12:29 Uhr

Ich habe meherere Layoutcontrols, hier das LayoutComboBoxControl. In diesem ist eine Combobox, ein Label für den Header und ein Label für eine Fehlernummer links neben der Combobox untergebracht. Ferner gibt es mehrere Möglichkeiten für Propertyeinstellungen, u.a. die Möglichkeit, dir im Header-Label zum Schluß die Gesamtanzahl der Einträge anzeigen zu lassen, z.B. Header Bla (13) ... jezt hatte ich nach einen Weg gesucht, dass wenn diese Option gesetzt ist, dass dies automatisch gesetzt wirde - leider vergebnes.

Ich kann jetzt immer manuell das aufrufen ... oder festschreiben, dass jeder Bindinglist verwenden muss ... oder ich lasse einen Thread laufen, der die Anzahl abchecked und immer bei Änderrungen die Handlung durchführt 🙁

13.03.2009 - 23:57 Uhr

Hallo "Kollegen",

ich habe jetzt fast eine halbe Stunde "vergurkt". Ist es wirklich so, dass es kein Event gibt, wenn ein Item einer ComboBox hinzugefügt wird, aber für jeden "Bimbes", wie z.B. MarginChangend und Bla, gibt es ein Event.

Danke schon einmal für etwaige Antworten im Voraus.

13.03.2009 - 14:44 Uhr

Ich musste übrigens auch hier im Forum explizit den HTML-Code &#160 ; angeben, sonst wären die Blanks nicht abgebildet worden
Außer du hättest sie in Code-Tags gepackt 😉

s     t    i     mm     t!

😁

13.03.2009 - 11:05 Uhr

Hallo "Kollegen",

gibt es beim XmlDocument-Objekt eine Möglichkeit einzustellen, dass Blanks nicht automatisch zu einem werden, wie z.B. in (X)HTML?

Problem ist, dass wenn ich den String "Firma Bla           --- E" habe, dann macht XML (auch (X)HTML) "Firma Bla --- E" draus.

Muss ich jetzt wirklich immer explizit String.Replace(" ", "&#160 ;") in jedem Elementvalue setzen oder gibt es da Optionseinstellungen?

Gruß und danke schon einmal für etwaige Antworten im Voraus.

P.S. Ich musste übrigens auch hier im Forum explizit den HTML-Code &#160 ; angeben, sonst wären die Blanks nicht abgebildet worden 😁

12.03.2009 - 20:16 Uhr
Response.Redirect("nameMeinerSeite.aspx");
10.03.2009 - 03:31 Uhr

Alles wurde mit .NET 3.5 entwickelt und compiliert (läuft aber auch mit 2.0).

Hier noch der Quellcode ...

10.03.2009 - 03:24 Uhr

Die Ausgabe für obigen Test, im Debugmodus, mit zwei Textdateien im aktuellen Verzeichnis, sieht dann so aus:


Sending command 'USER ftppower'
331 Password required for ftppower.
Sending command '*******'
230 User ftppower logged in.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,190).
Sending command 'STOR Text1.txt.tmp'
125 Data connection already open; Transfer starting.
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,191).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:56AM                 8128 Text1.txt.tmp
03-10-09  02:56AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,192).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:56AM                 8128 Text1.txt.tmp
03-10-09  02:56AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'DELE Text1.txt'
250 DELE command successful.
Sending command 'RNFR Text1.txt.tmp'
350 File exists, ready for destination name
Sending command 'RNTO Text1.txt'
250 RNTO command successful.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,193).
Sending command 'STOR Text2.txt.tmp'
125 Data connection already open; Transfer starting.
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,194).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:56AM               780290 Text2.txt
03-10-09  02:57AM               780290 Text2.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,195).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:56AM               780290 Text2.txt
03-10-09  02:57AM               780290 Text2.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'DELE Text2.txt'
250 DELE command successful.
Sending command 'RNFR Text2.txt.tmp'
350 File exists, ready for destination name
Sending command 'RNTO Text2.txt'
250 RNTO command successful.
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,196).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:57AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,197).
Sending command 'RETR Text1.txt'
125 Data connection already open; Transfer starting.
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,198).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:56AM                 8128 Text1.txt
03-10-09  02:57AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'DELE Text1.txt'
250 DELE command successful.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,199).
Sending command 'RETR Text2.txt'
125 Data connection already open; Transfer starting.
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,200).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'DELE Text2.txt'
250 DELE command successful.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,201).
Sending command 'STOR Text1.txt.tmp'
125 Data connection already open; Transfer starting.
[02:57:08.656] -> Download progress '12' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '25' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '37' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '50' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '62' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '75' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '88' % for file 'Text1.txt'
[02:57:08.656] -> Download progress '100' % for file 'Text1.txt'
Download completed for file 'Text1.txt'
Download for file 'Text1.txt' took '0' seconds
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,202).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM                 8128 Text1.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,203).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM                 8128 Text1.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'RNFR Text1.txt.tmp'
350 File exists, ready for destination name
Sending command 'RNTO Text1.txt'
250 RNTO command successful.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,204).
Sending command 'STOR Text2.txt.tmp'
125 Data connection already open; Transfer starting.
[02:57:10.250] -> Download progress '1' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '2' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '3' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '4' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '5' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '6' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '7' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '8' % for file 'Text2.txt'
[02:57:10.250] -> Download progress '9' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '10' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '11' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '12' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '13' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '14' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '15' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '16' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '17' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '18' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '19' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '20' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '21' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '22' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '23' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '24' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '25' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '26' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '27' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '28' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '29' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '30' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '31' % for file 'Text2.txt'
[02:57:10.265] -> Download progress '32' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '33' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '34' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '35' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '36' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '37' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '38' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '39' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '40' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '41' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '42' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '43' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '44' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '45' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '46' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '47' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '48' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '49' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '50' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '51' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '52' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '53' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '54' % for file 'Text2.txt'
[02:57:10.281] -> Download progress '55' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '56' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '57' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '58' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '59' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '60' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '61' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '62' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '63' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '64' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '65' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '66' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '67' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '68' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '69' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '70' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '71' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '72' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '73' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '74' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '75' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '76' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '77' % for file 'Text2.txt'
[02:57:10.296] -> Download progress '78' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '79' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '80' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '81' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '82' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '83' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '84' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '85' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '86' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '87' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '88' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '89' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '90' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '91' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '92' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '93' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '94' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '95' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '96' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '97' % for file 'Text2.txt'
[02:57:10.312] -> Download progress '98' % for file 'Text2.txt'
[02:57:10.328] -> Download progress '99' % for file 'Text2.txt'
Download completed for file 'Text2.txt'
Download for file 'Text2.txt' took '0,078125' seconds
Sending command 'PWD'
226 Transfer complete.
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,205).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM                 8128 Text1.txt
03-10-09  02:57AM               780290 Text2.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,206).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM                 8128 Text1.txt
03-10-09  02:57AM               780290 Text2.txt.tmp
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'RNFR Text2.txt.tmp'
350 File exists, ready for destination name
Sending command 'RNTO Text2.txt'
250 RNTO command successful.
Sending command 'PWD'
257 "/" is current directory.
Sending command 'TYPE I'
200 Type set to I.
Sending command 'PASV'
227 Entering Passive Mode (192,168,0,198,19,207).
Sending command 'LIST'
125 Data connection already open; Transfer starting.
226 Transfer complete.
03-08-09  07:04PM       <DIR>          Kopie (2) von Neuer Ordner
03-08-09  07:04PM       <DIR>          Kopie von Neuer Ordner
03-08-09  05:37PM       <DIR>          Neuer Ordner
03-10-09  02:57AM                 8128 Text1.txt
03-10-09  02:57AM               780290 Text2.txt
03-09-09  02:05AM       <DIR>          Tmp
03-10-09  01:07AM               326875 XXXDownload1.wmv
03-10-09  01:07AM            108331316 XXXDownload2.wmv
03-10-09  01:07AM            108331316 YYYDownload2.wmv
Sending command 'QUIT'
0 Quit on FTP server

10.03.2009 - 03:22 Uhr

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ftp;
using System.Text.RegularExpressions;
using System.Threading;

namespace Test.Ftp {
  public class FtpClientTester {
    static FtpClient ftpClient = null;
    static bool debugMode = true;
    static int transferPercentage = 0;

    static void Main(string[] args) {
      try {
        ftpClient = new FtpClient("192.168.0.198", @"ftppower", "*****", debugMode);
        ftpClient.Login();

        // **********************
        // Supported FTP commands
        // **********************

        // ftpClient.ChangeDirectory("fo");
        // ftpClient.RemoveDirectory("foo");
        // ftpClient.MakeDirectory("foo");
        // ftpClient.RemoveFile("blub.txt");
        // ftpClient.RemoveFiles("pattern");
        // ftpClient.Rename("from", "to");
        // ftpClient.DownloadFile("text.txt");
        // ftpClient.DownloadFileAsync("text.txt");
        // ftpClient.DownloadFiles("txt$");
        // ftpClient.DownloadFilesAsync("txt$");
        // ftpClient.UploadFile("text.txt");
        // ftpClient.UploadFileAsync("text.txt");
        // ftpClient.UploadFiles("txt$");
        // ftpClient.UploadFilesAsync("txt$");
        // ftpClient.GetWorkingDirectory());
        
        // *************
        // Sync examples
        // *************

        // Uploads all text files from the application startup path
        // to the working directory on the FTP server and deletes
        // all matching local files after downloading each file in sync mode
        IList<FtpTransferInfo> ftpTransferInfos = 
          ftpClient.UploadFiles("txt$", RegexOptions.IgnoreCase, true);

        // Downloads all text files in the working directory 
        // on the FTP server and deletes all matching files 
        // on the FTP server after uploading each file in sync mode
        ftpTransferInfos = 
          ftpClient.DownloadFiles("txt$", RegexOptions.IgnoreCase, true);

        // FtpTransferInfo ftpTransferInfo;
        // FtpTransferInfo with transfer (upload or download) infos

        // ftpTransferInfo.BytesSize 
        // => The size of a file 

        // ftpTransferInfo.StartAt
        // => The time when the transfer started

        // ftpTransferInfo.EndAt 
        // => The time when the transfer ended 

        // ftpTransferInfo.FileDeleted 
        // => Whether the file has been deleted after transfer

        // ftpTransferInfo.FtpException 
        // => Exception while transfer, this is important in async mode

        // ftpTransferInfo.LastModification 
        // => The last modification of the directory or file

        // ftpTransferInfo.Name 
        // => The name of the file

        // ftpTransferInfo.SourceDirectory 
        // => The source directory, download = working directory on FTP server, 
        // => upload = source directory on client

        // ftpTransferInfo.SuccessBytes
        // => The amount of bytes successfully transferred

        // ftpTransferInfo.TargetDirectory
        // => The target directory, download = path on client, upload = working
        // => directory on FTP server

        // ftpTransferInfo.TransferType
        // => Upload or download

        // ftpTransferInfo.GetSuccessPercentage() 
        // => The percentage of transferred bytes

        // ftpTransferInfo.GetTransferTime()
        // => Timespan from startup to end of the transfer

        // ftpTransferInfo.IsComplete()
        // => Whether the transfer has completed

        // **************
        // Async examples
        // **************

        // Uploads all text files from the FTP server in the working directory
        // to the application startup path in async mode
        ftpTransferInfos = ftpClient.UploadFilesAsync("txt$", RegexOptions.IgnoreCase);

        // Assign event handlers for all files
        foreach(FtpTransferInfo ftpTransferInfo in ftpTransferInfos) {
          // Dwonloaded file size has changed
          ftpTransferInfo.StatusExchange += 
            new FtpTransferInfo.StatusExchangeEventHandler(OnStatusExchange);
          // Error happened
          ftpTransferInfo.TransferError += 
            new FtpTransferInfo.TransferErrorEventHandler(OnTransferError);
          // Transfer, here download, has been completed
          ftpTransferInfo.TransferComplete += 
            new FtpTransferInfo.TransferCompleteEventHandler(OnTransferComplete);
        }

        // !!! Do not fire FTP commands in async mode, this will cause an error, !!!
        // !!! just wait till async mode is exiting                              !!!
        // !!! One can handle that with the AsyncComplete event handler or       !!!
        // !!! the ugly way with looping and time outs                           !!!

        // => The ugly way
        //while(ftpClient.IsAsyncMode()) {
        //  Thread.Sleep(1000);
        //}
        //
        // Here you can fire FTP commands again

        // => The tidy way
        // Download for all files have been completed, exit async mode
        ftpClient.AsyncComplete += new FtpClient.AsyncCompleteEventHandler(OnAsyncComplete);
      }
      catch(Exception ex) {
        Console.WriteLine("Exception ['{0}'] - '{1}'",
          new Object[] { ex.GetType().FullName, ex.Message });
      }

      Console.ReadLine();
    }

    static void OnAsyncComplete() {
      ftpClient.ListWorkingDirectory();
      ftpClient.Logout();
    }

    static void OnTransferComplete(FtpTransferInfo ftpTransferInfo) {
      Console.WriteLine(String.Format("Download completed for file '{0}'", ftpTransferInfo.Name));
      Console.WriteLine(String.Format("Download for file '{0}' took '{1}' seconds",
        ftpTransferInfo.Name, ftpTransferInfo.GetTransferTime().TotalSeconds));
      transferPercentage = 0;
    }

    static void OnTransferError(FtpTransferInfo ftpTransferInfo) {
      Console.WriteLine(String.Format("Download error happened for file '{0}' with message '{1}'",
        ftpTransferInfo.Name, ftpTransferInfo.FtpException.Message));
    }

    static void OnStatusExchange(FtpTransferInfo ftpTransferInfo) {
      if((int)ftpTransferInfo.GetSuccessPercentage() != transferPercentage) {
        Console.WriteLine("[{0}] -> Download progress '{1}' % for file '{2}'", 
          DateTime.Now.ToString("HH:mm:ss.fff"), (int)ftpTransferInfo.GetSuccessPercentage(), ftpTransferInfo.Name);
        transferPercentage = (int)ftpTransferInfo.GetSuccessPercentage();
      }
    }
  }
}

10.03.2009 - 03:21 Uhr

Hallo "Kollegen",

Hintergrund für die Erstellung des FTP Clients war dieser Thread.

Mit diesem Client bleibt die Connection offen. Ich habe alle gängigen FTP-Befehle implementiert. Uploads und Downloads können auch mittels einer Regular Expression durchgeführt werden. Desweiteren wird das Up- und Downloaden auch asynchron unterstützt (wichtig, um z.B. bei einer GUI-Anwendung den Status anzeigen zu können). Asynchron bedeutet, dass immer eine Datei nach der anderen (hier synchron) asynchron abgefragt werden kann. Das bedeutet, dass bei einem Pattern, das z.B. 20 Files matched alle 20 nacheinander in einem Thread abgearbeitet werden, aber immer für die aktuelle Datei kann der Status abgeholt werden und die Anwendung wartet nicht auf das Ende, bis für alle 20 Dateien die Arbeiten abgeschlossen wurden, sondern läuft weiter. Wichtig ist, dass im "Async-Modus" keine folgenden Befehle abgefeuert werden, denn dann kracht es, man muss warten, bis der Async-Modus wieder verlassen wurde, bzw. fertig ist. Näheres ist im Beispiel-Quellcode unten beschrieben.

Bei Kritik, Wünschen und Anregungen bitte an mich wenden. Weiterentwicklung ist gewünscht. Ferner will ich unbedingt "das Teil" noch SFTP-fähig machen. Vielleicht bekomme ich von euch Unterstützung bzw. helfende Hinweise dafür. So, ich poste jetzt einfach mal mein Test-Projekt mit den Beschreibungen (auf Englisch) ...

08.03.2009 - 16:30 Uhr

OK, ich bin jetzt wieder ein bißchen weiter ...
das Problem wurde gelöst, indem ich im "Hauptsocket" den Status nach 226, also Transfer complete) abfrage. Der Listbefehl benötigt ja, neben dem (Up- und Downloaden) eigene Verbindungen, die mittels sog. "Untersockets" realisiert werden.

Hier mal der angepasste Quellcode ...


        
        byte[] buffer = new byte[FtpClient.BUFFER_SIZE];
        DateTime timeout = DateTime.Now.AddSeconds(FtpClient.MAX_TIMEOUT.TotalSeconds);
    
        while(DateTime.Now < timeout) {
          // Check whether data on
          // transfer socket are available
          if(transferSocket.Available > 0) {
            // Read data
            int readBytes = transferSocket.
              Receive(buffer, buffer.Length, SocketFlags.None);
            transferData.Append(Encoding.ASCII.GetString(buffer, 0, readBytes));
          }
          else {
            // Check whether status message 
            // on main socket is available
            if(this.socket.Available > 0) {
              this.ReadResponse(FtpStatusCode.ClosingData);

              if(debug) {
                Console.WriteLine(this.response);
              }

              // Exit loop
              if(this.statusCode == FtpStatusCode.ClosingData.GetHashCode()) {
                break;
              }
            }
          }
        }

Jetzt wird solange die Schleife durchlaufen, bis entweder ein Timeout auftritt oder aber das Hauptsocket 226 meldet.

Jetzt habe ich trotzdem noch eine FTP-Verständnisfrage. Immer, wenn eine Aktion im passiven Modus erfolgreich abgeschlossen wurde (Download, Upload, Listing von Dateien und Verzeichnissen), gibt das Hauptsocket eine Meldung raus, z.B. 226 Transfer complete. Wie kann ich jetzt herausfinden, welche Meldung zu welchem Socket im passiven Modus gehört?

Kann mir da jemand helfen, bzw. einen Link posten? Problem ist, wenn ich z.B. 10 Downloads starte (synchron in Threads) und ich währenddessen ein Listing durchführe (siehe Quellcode oben), dann weiss ich ja nicht, wenn 226 auf der Console landet, auf was sich das bezieht, auf Download oder Listing. Das muss man doch irgendswie auseinanderhalten können?

Gruß und Danke schonmal für etwaige Antworten im Voraus.

P.S. Das mit den mehreren Verbindungen geht, denn Filezilla macht das auch so. Sogar laufen alle Downloads weiter, auch wenn beim Hauptsocket Verbindung geschlossen wurde.

08.03.2009 - 02:14 Uhr

Hallo,

ich habe heute einen dreiviertel Tag "verbraten" und komme nicht weiter. Es ist wirklich so, dass manchmal einfach nix übermittelt wird und dann nach einer Unterbrechung wieder Daten verfügbar sind ....

dieser Code z.B. macht mich echt wahnsinnig ...


 byte[] buffer = new byte[512];

        while(true) {
          int length = transferSocket.
            Receive(buffer, buffer.Length, SocketFlags.None);
           transferData.Append(Encoding.ASCII.GetString(buffer, 0, length ));

           if(transferSocket.Available == 0) {
             Thread.Sleep(2000);

             if(transferSocket.Available == 0) {
               break;
             }
           }
        }


Bei Thread.Sleep 1500 fehlt die Hälfte der Daten ... das kann doch nicht sein. Wie kann man denn das lösen. Alle Tutorials im Inet machen das genauso oder fragen am Ende der Schleife die Länge der gelesenen Bytes und vergleichen die mit dem Char-Array .. jedoch ist das bei mir leer oder kleiner und dann, wenn im Debugmodus mit Breakpoint, füllt sich der "Käse" dann wieder. Wie soll ich da ein lauffähiges Produkt programmieren? Ich kann doch nicht jedesmal 2-5 Sekunden unterbrechen, nur weil noch Daten kommen könnten.

Kann mir da jemand helfen? Ich schaue noch mal, ob das mit Asynch auch so aussieht. Wenn ja, dann kann ich meinen "Socket-Ausflug" gleich wieder vergessen. 🙁

07.03.2009 - 12:32 Uhr

Hallo herbivore,

zu 2) Ich habe passives FTP verwendet. Ich denke es geht nicht, wenn man bei einem Hauptsocket mehrere Datensockets (die benötigt werden für passiven Modus) die gleichzeitig in Threads Up-oder Download durchführen.

Ich habe mir mal kurz Gedanken darüber gemacht. Es macht imo glaube ich keinen Sinn, dass man z.B. mittels einer Regex Dateien in Threads gleichzeitig downloaded (das macht nicht mal Filezilla). Angebracht erscheint mir (was bei mir auch geht in meinem Client), dass alle Files synchron downgeloaded werden, aber jedes File einzeln asynchron, soll bedeuten, dass es eine Schnittstelle gibt, um z.B. eine Fortschrittsanzeige zu implementieren.

Nehmen wir mal ein Beispiel. Ich verbinde mich auf einen FTP Server und im Hauptverzeichnis matchen 30 Dateien meine übergebene Regex. Dann downloade ich die doch nacheinander runter (mit eben der Möglichkeit für Fortschrittsanzeige) aber doch nicht alle gleichzeitig, oder? Kennt ihr Programme, die so vorgehen, und wenn ja, was ist zu tun? In dem letzteren Fall müssten doch dann immer eigene Verbindungen aufgebaut werden oder läuft das über das "Hauptsocket" - was bei mir aber gegenwärtig kracht.

Ich kenne das Beispiel z.B. von Firefox, wenn ich 20 große Dateien downloade, dann werden die alle asynchron downgeloaded und ich sehe das, bei Filezilla ist es synchron.

Macht es Sinn, hier asynchron downzuloaden? Eigentlich nicht, oder?

P.S. Sorry wegen dem falschen Eingangspostthema, bzw. Nichteinhaltung von Postinregeln

07.03.2009 - 11:43 Uhr

Hallo "Kollegen",

ich hätte bezüglich Sockets zwei Fragen. Ich habe hier erst angefangen, mit Sockets zu programmieren.

1). Warum laufen bei mir auf meinem System, sowohl in der Arbeit als auch zu Hause meine Programmcodes nicht, wie in vielen Beispielen aus dem Internet? Das Problem ist, dass mein Programm läuft, wenn ich ein Thread.Sleep reinbaue, teilweise sogar auf 500 Millisekunden setzen muss, dann funzzt meine Anwendung einwandfrei. Natürlich ist das nicht die Lösung, ich werde das asynchron machen, jedoch wüsste ich schon gerne, warum bei allen Beispielen im Netz das so angegeben wird (Socket.Available, bzw. in Verbindung mit readBytes, nach dem letzten Receive) und es bei mir aber nicht funzzt, ohne den Timeout.

2). Thema gleichzeitiger Down- und Upload von mehreren Dateien. Ich dachte, ich kann hier immer das gleiche Socket nehmen, da ich ja ein extra Transfersocket aufmachen muss (Befehl PASV), aber das scheint nicht zu gehen. Ich möchte mich hier nur vergewissern. Ist es wirklich so, dass ich für jeden Up- bzw. Download ein eigens Socket instanzieren muss (inklusive Verbindungsaufbau)?

Gruß und danke schon einmal für etwaige Antworten im Voraus.

03.03.2009 - 22:05 Uhr

OK, alles klar, vielen Dank!

03.03.2009 - 19:55 Uhr

Vielen Dank, ich werde mich da mal einlesen.

Eine Frage dann doch noch - ist mir heute im Laufe des Tages eingefallen. Man kann ja Assemblys auch mittels Assembly.LoadFile laden. Die Plug-Ins machen demnach ähnliches? Oder soll man die einfach bei diesem Sachverhalt, bzw. Problemstellung dem klassischen Assembly.LoadFile vorziehen?

03.03.2009 - 09:00 Uhr

Hallo "Kollegen",

gibt es eine Möglichkeit, Quellcode, der später entwickelt wurde in ein System im laufenden Betrieb einzuspielen?

Mal ein Beispiel. Es läuft ein Service. Hier laufen verschiedene Threads, die immer eine, nennen wir sie mal Hauptaufgabe, durchführen. Nun ist der Service so programmiert, dass er z.B. in einem XML-File nachsehen soll, welche "Vorbereitungsklasse" er aufrufen soll und dann erst mit der "Hauptaufgabe" beginnt. Nach diesem Prozess, ruft er "Nachräumarbeiten" auf, die ebenfalls konfiguriert werden können. Das bedeutet, dass die Hauptaufgabe immer ausgeführt wird und je nach Anforderung eine Klasse für Vorarbeiten und Nacharbeiten aufgerufen werden kann. Nun ist das ja kein Problem umzusetzen, wenn man das Programm als ganzes neu kompiliert - die Frage ist, ob sowas auch geht, ohne den Service (oder Hauptprogramm) neu zu compilieren (oder sogar nicht mal neu zu starten).

Hintergrund ist, man verkauft ein "Standardprogramm" und kann so (Hinzufügen der Vor- bzw. Nacharbeitsklassen) manuelle Anpassungen durchführen.

Gruß und Danke schon einmal für etwaige Antworten im Voraus.

02.03.2009 - 01:37 Uhr

Hallo herbivore,

das Problem bei einem sog. Mittelweg ist, dass dann jeder für sich selber argumentieren kann und jeder nach seinem Geschmack "drauf los coded" ... und unter dem Stricht schaut es dann applikationsweit so aus, dass es auf den verschiedenen Seiten (oder Controls) von 20 bis 80 Prozent variiert zwischen sofortiger, enableter Anzeige aller Steuerelemente oder disableten, bzw. keiner Anzeige dieser Elemente. Das ist ein sehr bitterer Beigeschmack.

Du hast absolut Recht mit den Fehlermeldungsboxen. Diese habe ich schon immer gehasst und deshalb benutze ich jetzt immer ein Console Control. Ich benutze das als eine Art Kommunikationsschnittstelle mit dem Benutzer - je mehr Infos, desto besser (aber nicht damit "erschlagen"). Auserdem spart sich der Benutzer immer nach einer Meldung ein Buttonklick, denn er muss kein Fenster wegklicken, was total nervig sein kann.

Wie gesagt, ich tendiere gerade zum vollen Gegenteil von früher (hier immer die totale Logik eingebaut). Wer kennt denn das nicht, da sind auf einer Seite zig Steuercontrols abgebildet, die meißten deaktiviert und man kommt nicht weiter und alle "Herrgotssekunden", nach einer Aktion, poppt ein häßliches Fenster auf mit teilweise nichtssagenden Meldungen - das kennen wir doch alle, oder?

P.S. Deine zusätzlich hinzugefügten Punkte bestätigen mir persönlich, dass ich mit meinem "Umdenken" eher doch auf dem richtigen Wege bin 😉 - wie gesagt, einer "Zwitterlösung" stehe ich eher abgeneigt entgegen.

01.03.2009 - 16:05 Uhr

Hallo "Kollegen",

aktuell (eigentlich schon länger) treibt mich eine Frage um. Wie gehe ich mit der (funktionellen) Anzeige von Steuerelementen bei GUI-Anwendungen um.

In der Vergangenheit habe ich hier immer die Logik mit eingebaut, soll heißen, dass z.B. Buttons, Checkboxen ect. nur dann enabled (oder auch als ganzes angezeigt) werden, wenn das auch erlaubt ist. Zum Beispiel muss Benutzer in einer bestimmten Combox den 3. Eintrag auswählen, dann wird eine Checkbox angezeigt, die checked (vorbelegt) ist und es werden andere Steuerelemente disabled ... usw. Das Problem ist, wenn man so vorgeht und eine sehr komplexe Logik in den Steuerelementen hat, dass nach Anpassungen sich die ganze Logik nicht mehr so verhällt, wie eigentlich gewünscht, bzw. man Fehler immer sehr spät (wenn überhaupt) findet, weil sehr komplex und manche Fälle sehr selten auftreten. Dann ist z.B. ein Steuerelement aktiv, das disabled sein sollte usw.

Eigentlich kann das imo nur das totale Chaos werden. Jetzt tendiere ich in die Richtung, dass sofort alles mögliche angezeigt wird, und dem User halt dann die demensprechnenden Fehlermeldungen angezeigt werden, z.B. "Kann nicht gespeichert werden, weil ... bla" ....

Wie geht ihr mit dieser Problematik um? Vielleicht ist ja auch der Mittelweg (teilweise deakitivieren, bzw. Nichtanzeigen) von Controls der Königsweg.

Grüße und schon mal vielen Danke für euere Antworten im Voraus.

01.03.2009 - 04:54 Uhr

Hallo Nils,

hier trifft man sich also wieder, nun, die Welt ist ja verdammt klein 😁 ...

Ich würde das Tab Control nur dann verwenden, wenn der Benutzer Einstellungen vornehmen kann, also ganz einfach Werte setzen und diese müssen auch im Context zusammengehören - aber ich würde das Teil nie verwenden um irgendswelche (aufwendige) Businesslogik reinzubauen. Also aktivieren, wenn Tab 1 und dann deaktivieren und nicht anzeigen wenn ... usw.

Probiere es doch einfach mit User Controls, Benutzer jaensen hat ja schon den Link gepostet. Ich schlage Dir auch mal meine Lösung vor, klickst Du >> hier <<

Ferner sind für ein Login immer sog. Splash-Screens interessant. Gibt es hier im Forum auch zig Einträge und auch vorgefertigte Klassen.

Gruß von Hallstadt rüber nach Bamberg

28.02.2009 - 17:10 Uhr

Hallo mosspower,

siehe auch
>

herbivore

Danke für den Hinweis,

ein sehr heikles Thema. Wenn ich es wirklich so machen würde, dass ich einen Fehler (z.B. mit Fehlercode) in der GUI übersetze und dann den Fehler in der jeweiligen Sprache anzeige, dann kann manchmal doch nur rauskommen, dass in der Fehlermeldung "fast nix" steht, z.B. Ein Fehler Validierungsfehler beim Einlesen der Datei ist passiert, Fehlercode ERR_00123XYZ ... Jetzt kann der Anwender nix weiter machen, wenn er Glück hat, wird gelogged und kann im Logfile nachsehen, das erfordert aber schon mehr Kenntnisse. In der Hilfe könnte der Fehler auch beschrieben sein. Das ist jetzt aus Kundensicht.

Wenn ich jetzt aber die gekapselte Funktionalität (nehmen wir einfach mal das obige Beispiel zum Lesen einer Datei mit Validierungsprüfung) bei mehreren Projekten verwende (also mehrere GUI-Projekte), dann müsste ich doch jedesmal die gleiche oder eine ähnliche Fehlermeldung in meinen Sourcecode (oder eigenes Resourcefile) schreiben, das wiederholt sich doch dann so oft, wie ich die Komponente entwickle.

Was spricht denn dann dagegen, dass man das gleich so aufbaut wie vorgeschlagen? Hier hätte ich einen resource-key und x-beliebige Parameter, und nach Anforderungen (GUI-Anwendung) wird dann, nach übergabe der CultureInfo die Meldung generiert. Das bedeutet, ich muss die Meldung nur einmal "eincompilieren" ... ein schwieriges Thema 😁 ... naja, werde mir das erst noch mal durch den Kopf gehen lassen.

@feadur ,

an so eine ähnliche Lösung hatte ich gedacht, wobei ich herkömmliche Propertyfiles nehmen, mit Key-Value-Paaren und keine .NET Resourcefiles. Ich benutze lieber die Keys und nicht die vom Framework generierten Resourcenamen. Aber es läuft im Endefekt aufs gleiche hinaus - nur bin ich mir gegenwärtig nicht sicher ob ich es überhaupt so machen soll, denn eigentlich sind Exceptionmeldungen, wie oben schon beschrieben, für Entwickler gedacht. (Obwohl hier, Stichwort Mehrsprachigkeit, Microsoft auch einen eigenen Weg geht.)

28.02.2009 - 14:01 Uhr

Ich kann auch mal meinen Senf dazugeben.

Ich habe über drei Jahre Java programmiert (vorher mit C/C++) angefangen. Hier natürlich die ganze Schiene (JSP, Struts, Tomcat, EJB, Eclipse, Ant usw.). Dann hatte ich zu einer Unternehmung gewechselt, die mittels C# und .NET entwickelt. Ich hatte mich schon teilweise total lausig gefühlt, weil ich auf diese, in der "Java-Welt" verpönte Umgebung umgestiegen bin, bzw. auch musste, es gab halt in dem Ort kein anderes Stellenangebot.

Ich dachte aber auch, da ich ja jahrelang mit Open-Source-Tools gearbeitet hatte, dass vieles jetzt ganz anders wird, denn man kann ja erwarten, so dachte ich jedenfalls, dass die kommerziellen Angebote wesentlich besser sind, als die frei entwickelten, da man ja dafür bezahlen muss.

Die Sprache C# habe ich mir sehr schnell angeeignet, ich habe sehr sehr schnell gemerkt, dass C# als Antwort zu Java entwickelt wurde. Ich würde einfach mal behaupten, dass C# 95 Prozent Java abdeckt. Sehr gut fand ich Indexter, Delegates, und Eventhandling, sonst war ja alles identisch. Mit out und in kann ich nix anfangen, ist imo kein schöner Programmierstil. Sicher gibt es noch ein paar mehr Sachen, die fallen mir aber jetzt gerade nicht ein.

Leider musste ich mich noch mit 1.1 begnügen. Hier ging es dann schon los. Es gab z.B. keine Generics, die es aber in Java schon länger gab. Un die Entwicklungsumgebung 1.1, na, wie soll ich da jetzt sagen, war einfach lausig.

.NET 2.0 war schon ein Megaschritt und kam dann auch wieder etwas an Eclipse ran. Ich muss wirklich sagen, dass ich anfangs total enttäuscht von der IDE war. Ich hatte wirklich gedacht, dass man für Geld mehr bekommt als für das kostenlose Eclipse und was war, Pustekuchen. Der absolute Hammer war auch noch 1.1, da bin ich öfters an die Decke gegangen in der Arbeit. Das ist wie ein Arzt, der jahrelang mit den besten Werkzeugen arbeitet und dann nur noch einen Hammer und eine Schere in der Hand hat.

Mitlerweile habe ich mich daran gewöhnt. Es gibt auch viele kostenlose Plug-Ins für die IDE (z.B. Koda ect.), so dass ich jetzt mitlerweile ganz gut damit arbeiten kann.

Vorteilhaft finde ich auch, dass sehr viele Tools aus der Java-Welt nach C# portiert wurde, um nur mal einige zu nennen: NAnt, NHibernate, NDoc, NUnit und noch viele mehr.

Ich würde nie mehr in Java programmieren, wenn ich weiß, dass das Programm auf einer Windows-Umgebung laufen soll. Auch finde ich es angenehmer in C# zu programmieren als in Java. Die GUI-Entwicklung in .NET ist der absolute Hammer, verglichen mit Java, hier teilweise hardgecoded Swing-Anwendungen programmiert. Jemand, der Jahre lang z.B. mit Struts oder JSF gearbeitet hat und dann mit ASP.NET entwickelt muss dieses Framework einfach nur toll finden. Nix geht schneller. Was meint ihr, wie lange ich immer gebraucht habe, um Struts + Tomcat + Servlet aufzusetzen und dann was zum laufen zu bringen. In ASP.NET einmal Control reingezogen - fertig. Neben den Webanwendungen sind natürlich auch die GUI-Anwendungen mittels WPF und Windows-Forms der Hammer, unschlagbar.

Was ich aber immer noch vermisse, ist Eclipse. Manchmal, wenn ich gewisse Aktionen in VS durchführen möchte, z.B. Add Reference, dann weiss ich, dass ich mal schnell surfen kann. In der Arbeit sagen wir dann immer, ich habe jetzt die Microsoft-Methode ImRightBack aufgerufen. Das ist total nervig, weil dann einfach eine Zeit lang nix mehr geht. Bei Eclipse war es so, dass es eher länger gebraucht hat um zu starten, jedoch nach dem starten ist das Teil gelaufen (wenn nicht vollgepackt mit irgendswelchen PlugIns).

Fazit. Ich werde weiterhin hauptsächlich mittels C# entwickeln und bin super zufrieden damit - trotzdem fehlt mir, nach nun annähernd zwei Jahren C#-Entwicklungszeit, immer noch sehr Eclipse. Es ist so, wie ich im Web mal gelesen habe ...

Visual Studio is a great tool ... but Eclipse is in another league!

28.02.2009 - 12:46 Uhr

Hallo und erst mal vielen Dank für die Antworten,

ich bin offen für Anregungen und Kritik, kein Problem.

Ich schildere mal kurz meine Problematik. Ich habe bisher immer Englisch programmiert (hatte mich da eben einmal entschieden), das bedeutet, dass ich auch das Exceptionhandling in Englisch mache, was ich übrigens, aus Erfahrung, zeitmäßig nie vernachlässige. Jetzt ist es aber so, dass ich in das auch mehrsprachig Anbieten möchte, bzw. weil ich eingesehen habe, dass ich das auch muss, nicht jeder kann Englisch, bzw. reicht nicht immer etwas Schulenglisch aus.

Wie löse ich aber in Zukunft z.B. folgende Fehlermeldung Mehrsprachig?

....


                catch(FormatException) {
                  throw new EdiException(EdiErrorConstants.FILE_VALIDATION_ERROR,
                    String.Format("Cannot cast boolean out of '{0}' for field name '{1}' " +
                      "= mapping name '{2}' in field configuration number '{3}' " + 
                        "for column position in import file number '{4}' in " +
                          "import file line '{5}' for config key '{6}'",
                            new Object[] { fieldValue, 
                                           ediField.Name, 
                                           ediField.MappingName, 
                                           (i + 1),
                                           positionCounter, 
                                           (j + 1), 
                                           ediConfig.Id } )); 
                }

Wie löse ich das jetzt? Ich dachte, dass ich diese Exception "nur noch" mit dem Typen, hier FILE_VALIDATION_ERROR, mit dem Resource key und mit all den Parametern weiterschmeiße, dann kann die Anwendung, je nach CultureInfo, sich den Text holen, oder aber man baut die CultureInfo auch noch in die CustomException (Constructor) ein und bekommt dann die "richtige" Fehlermeldung.

Ich bin jetzt hier dabei, eine Möglichkeit zu suchen, wo ich mehrere kleine Projekte mehrsprachig machen muss. Bisher habe ich immer englische Exceptions geworfen, wie im Beispiel.

28.02.2009 - 00:59 Uhr

Hallo "Kollegen",

Ich habe eine Metaklasse für Exceptions geschrieben, so wie ich die jetzt immer in Zukunft brauche. Die Klasse CustomException ist von Exception abgeleitet und bietet darüber hinaus noch die Möglichkeit, ein errorCode vom Typ T zu definieren, ferner können Argumentobjekte genutzt werden, hier z.B. wenn die Fehlermeldungen internationalisiert werden sollen (dann steht in message der Resource-Key). OK, so sieht das dann aus ....


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

namespace Util {
  /// <summary>
  /// Custom exception class
  /// </summary>
  public class CustomException<T> : Exception {
    private T errorCode;
    private Object[] args = null;

    /// <summary>
    /// Public constructor
    /// </summary>
    /// <param name="errorCode">The error code</param>
    /// <param name="message">The error message</param>
    /// <param name="innerException">The inner Exception object</param>
    /// <param name="args">Error arguments</param>
    public CustomException(T errorCode, String message,
      Exception innerException, params Object[] args)
        : base(message, innerException) {
      this.errorCode = errorCode;
      this.args = args;
    }

    /// <summary>
    /// See <see cref="CustomException&lt;T&gt;(T, String, Exception, Object[])"/>
    /// </summary>
    public CustomException(T errorCode, String message,
      Exception innerException)
        : this(errorCode, message, innerException, null) {
    }

    /// <summary>
    /// See <see cref="CustomException&lt;T&gt;(T, String, Exception, Object[])"/>
    /// </summary>
    public CustomException(T errorCode, String message, params Object[] args)
      : base(message) {
      this.errorCode = errorCode;
      this.args = args;
    }

    /// <summary>
    /// See <see cref="CustomException&lt;T&gt;(T, String, Exception, Object[])"/>
    /// </summary>
    public CustomException(T errorCode, String message)
      : this(errorCode, message, (Object[])null) {
    }

    /// <summary>
    /// Property ErrorCode
    /// </summary>
    public T ErrorCode {
      get {
        return this.errorCode;
      }
    }

    /// <summary>
    /// Property Args
    /// </summary>
    public Object[] Args {
      get {
        return this.args;
      }
    }

    /// <summary>
    /// Gets the full message, replaces all arguments, if any
    /// within the message string
    /// </summary>
    /// <returns>The full message</returns>
    /// <exception cref="FormatException" />
    public String GetFormattedMessage() {
      String formattedMessage = this.Message;

      if(this.args != null) {
        formattedMessage = String.Format(formattedMessage, this.args);
      }

      return formattedMessage;
    }

    /// <summary>
    /// See <see cref="Object.ToString()"/>
    /// </summary>
    public override string ToString() {
      return this.GetType().FullName + " [" + 
        this.errorCode.ToString() + "] > " + this.GetFormattedMessage() + 
          " > stack [" + ExceptionUtil.GetFullStackTrace(this) + "]";
    }
  }
}

Jetzt möchte ich natürlich eigene Exceptions bilden, die (in der Regel) lediglich einen anderen Namen haben. Meine Frage wäre, ob ich wirklich dann immer in dieser eigenen Klasse alle Konstruktoren überschreiben muss. Kann man das nicht anders machen? Ist doch dann eigentlich immer die gleiche Kopiererei, nur der Klassenname (und der Namespace) sind anders.

Hier ein Beispiel ... (nur einmal überschrieben)


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

namespace Coding {
  /// <summary>
  /// Coder exception class
  /// </summary>
  public class CoderException : CustomException<CoderConstants.ErrorCodes> {
    /// <summary>
    /// See <see cref="CustomException&lt;T&gt;(T, String, Exception, Object[])"/>
    /// </summary>
    public CoderException(CoderConstants.ErrorCodes errorCode, String message,
      Exception innerException, params Object[] args)
        : base(errorCode, message, innerException, args) {
    }
  }
} // ... and more constructors ...

OK, ich könnte ja gleich die CustomException hernehmen, und zusätzlich ein Name-Property einbauen, aber das scheint eine sehr schlechte Lösung, denn ich kann dann nicht die spezifischen Exceptions catchen, sondern müsste dann im Catch-Block noch die Namen abfragen, was eine total komisches Exceptionhandling wäre.

Hat jemand eine Idee? Befinde ich mich auf dem Holzweg oder würdet ihr einfach immer wieder den gleichen "Käse", also die Konstruktoren, in jede neue Exception-Klasse kopieren?

Gruß und danke schon mal für etwaige Antworten im Voraus.

26.02.2009 - 14:02 Uhr

Hallo "Kollegen",

ist es möglich, mittels C# die Uhrzeit von einem Rechner im Netzwerk zu ermitteln. Hintergrund ist der, dass wir im Netzwerk drei (ältere) Rechner haben, die Fremdprogramme laufen haben, diese werden benötigt. Nun kommt es sehr oft vor, dass es Zeitunterschiede gibt, die größer als 5 Minuten sind, dann ist ja kein Zugriff auf einen freigegebenen Pfad eines solchen Rechners mehr möglich. Zu meinem Bedauern kommt auch noch hinzu, dass wenn ich ein File copiere vom Client auf den Client (eine Art rename, also copy + delete), dass dann die Zeit vom Client als creation time angelegt wird. Ich brauche hier aber die akutelle Zeit des Servers (bzw. des Rechners, auf dem der Service läuft).

Frage: Gibt es eine Möglichkeit mittels C# auf die aktuelle Zeit des Zielrechners zu kommen?

Danke schon mal für etwaige Antworten im Voraus.

P.S. Es läuft schon ein automatischer Zeitabgleich, jedoch spinnen genau diese Rechner des öfteren, warum weiß kein Mensch - ein Austauschen kommt (leider) nicht in frage.

24.02.2009 - 17:07 Uhr

Ich scheitere gerade auch aktuell an der gleichen Problematik. Gibt es wirklich keine Möglichkeit, die Verbindung zu halten. Es kann doch nicht sein, dass wenn ich z.B. 20 Dateien downloaden möchte, dass ich dann 20 mal einen neuen Request schicke und mich dann vorher auch noch authentifizieren muss.

Gruß und danke schon einmal für etwaige Antworten im Voraus.

22.02.2009 - 00:44 Uhr

Hier ist noch der Code .. habe ich in meine Serializable-Utilityklasse gepackt ...


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using Util;
using System.Runtime.Serialization;
using System.Reflection;

namespace Util {
  /// <summary>
  /// Handles serialization issues
  /// </summary>
  public class SerializationUtil {
    private static readonly String PREFIX_SERIALIZABLE_VALUES_FIELD = "F_";
    private static readonly String PREFIX_SERIALIZABLE_VALUES_PROPERTY = "P_";
    private static readonly BindingFlags SERIALIZABLE_VALUES_BINDINGFLAGS = 
      BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;

    /// <summary>
    /// Sets all fields and properties which are flagged with the 
    /// <see cref="Util.SerializableValue"/> attribute on the passed instance
    /// </summary>
    /// <param name="instance">The instance</param>
    /// <param name="serializedValues"></param>
    public static void SetSerializableValues(Object instance, Dictionary<String, Object> serializedValues) {
      try {
        // Check instance
        if(instance == null) {
          throw new ArgumentException("Parameter instance was passed null");
        }

        // Check serialized values
        if(serializedValues == null) {
          throw new ArgumentException("Parameter serializedValues was passed null");
        }

        foreach(String key in serializedValues.Keys) {
          String removePrefix = String.Empty;

          if(key.StartsWith(SerializationUtil.PREFIX_SERIALIZABLE_VALUES_FIELD)) {
            String fieldName = StringUtil.ReplaceEmpty(key, 
              SerializationUtil.PREFIX_SERIALIZABLE_VALUES_FIELD);

            // Get field info
            FieldInfo fieldInfo = instance.GetType().GetField(fieldName,
              SerializationUtil.SERIALIZABLE_VALUES_BINDINGFLAGS | BindingFlags.GetField);

            // Set value
            fieldInfo.SetValue(instance, serializedValues[key]);
          }
          else if(key.StartsWith(SerializationUtil.PREFIX_SERIALIZABLE_VALUES_PROPERTY)) {
            String fieldName = StringUtil.ReplaceEmpty(key,
            SerializationUtil.PREFIX_SERIALIZABLE_VALUES_PROPERTY);

            // Get property info
            PropertyInfo propertyInfo = instance.GetType().GetProperty(fieldName,
              SerializationUtil.SERIALIZABLE_VALUES_BINDINGFLAGS | BindingFlags.GetProperty);

            // Set value
            propertyInfo.SetValue(instance, serializedValues[key], null);
          }
        }
      }
      catch {
        throw;
      }
    }

    /// <summary>
    /// Gets all instance field and property values for the passed instance
    /// which has been flagged with the attribute <see cref="Util.SerializableValue"/>
    /// </summary>
    /// <param name="instance">The instance</param>
    /// <returns>Dict</returns>
    public static Dictionary<String, Object> GetSerializableValues(Object instance) {
      Dictionary<String, Object> serializableValues = null;

      try {
        if(instance != null) {
          // Get all field infos
          FieldInfo[] fieldInfos = instance.GetType().GetFields(
            SerializationUtil.SERIALIZABLE_VALUES_BINDINGFLAGS | BindingFlags.GetField);

          if(fieldInfos != null) {
            foreach(FieldInfo fieldInfo in fieldInfos) {
              Object[] customAttributes = fieldInfo.GetCustomAttributes(true);

              if(customAttributes != null) {
                foreach(Object customAttribute in customAttributes) {
                  if(customAttribute.GetType().Equals(typeof(SerializableValue))) {
                    // Add to dictionary
                    if(serializableValues == null) {
                      serializableValues = new Dictionary<String, Object>();
                    }

                    serializableValues.Add(SerializationUtil.PREFIX_SERIALIZABLE_VALUES_FIELD +
                      fieldInfo.Name, fieldInfo.GetValue(instance));
                  }
                }
              }
            }
          }

          // Get all property infos
          PropertyInfo[] propertyInfos = instance.GetType().GetProperties(
            SerializationUtil.SERIALIZABLE_VALUES_BINDINGFLAGS | BindingFlags.GetProperty);

          if(propertyInfos != null) {
            foreach(PropertyInfo propertyInfo in propertyInfos) {
              Object[] customAttributes = propertyInfo.GetCustomAttributes(true);

              if(customAttributes != null) {
                foreach(Object customAttribute in customAttributes) {
                  if(customAttribute.GetType().Equals(typeof(SerializableValue))) {
                    // Add to dictionary
                    if(serializableValues == null) {
                      serializableValues = new Dictionary<String, Object>();
                    }

                    serializableValues.Add(SerializationUtil.PREFIX_SERIALIZABLE_VALUES_PROPERTY +
                      propertyInfo.Name, propertyInfo.GetValue(instance, null));
                  }
                }
              }
            }
          }
        }
      }
      catch {
        throw;
      }

      return serializableValues;
    }
  }
}


20.02.2009 - 09:44 Uhr

Hallo kleines_eichhoernchen,

ja, das hätte ich machen können, nur ist mir das im Moment zuviel Overhead, da es idR zwei bis drei Instanzvariablen sind (normaler Typ, also String oder int). Ich habe ja gegenwärtig schon eine lauffähige Lösung, ist auch so eine Art Anpassung an das Objekt, aber das erschien mir zu aufwändig, irgendwie wie mit Kanonen auf Spatzen schießen. Ich denke, dass die Lösung mit Attributes eine saubere (nicht die sauberste) und schnellste Lösung ist.

20.02.2009 - 09:30 Uhr

Warum verwendest du nicht die Attribute Serializable und NonSerialized und die Standardserialisierung von .Net?

Weil man das Attribute nicht bei Felder verwenden kann. Ich möchte nicht das ganze Objekt serialisieren, da es sehr viele und sehr große sind. Es geht hier um Teilservices innerhalb eines Services (also zig Threads), die z.B. Imports, Counter, Fehler, Warnings etc. hochzählen und diese möchte ich nicht verlieren, wenn ich den Service runterfahre. Denn dann würde ich ja z.B. wieder bei 0 anfangen wenn aber z.B. schon X Dateien importiert worden sind an diesem Tag.

20.02.2009 - 09:24 Uhr

Hallo herbivore,

da hast Du recht, nur ist hier das Problem, dass ich einen String brauche um Operationen anzustellen, z.B. wenn Field name ist bla (wobei bla ein String ist, der soz. ein Verweis auf die Variable ist), dann mache das ... usw. ... das braucht man bei Klassen nicht (hier kann man ja Klassentypen vergleichen, bzw. abfragen). Die einzige Lösung, die mir eingefallen wäre (ohne die Attribute, die sind die beste Lösung) wäre gewesen, dass ich allen relevanten Variablen ein Postfix-Flag anhänge, z.B.


private int myTestVariable_ser;

.. dann hätti ich mit FieldInfos nur die mit dem Postfix serialisiert.

Gruß

19.02.2009 - 22:56 Uhr

Naja, ich benutze das Attribute ...


 [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
  class SerializableField : Attribute {
    
  }

und im Code dann benutze ich die halt mit Attributes ... z.B. ...


  [SerializableField]
    private int blub = 0;

    [SerializableField]
    private String bla = String.Empty;

    private Decimal nonSerializable = 0.0m;

Wird die Applikation runtergefahren, dann werden alle mit Attribut geflagten Fields und Properties mittels Reflektion serialisiert, beim Start werden dann die Werte gesetzt (wenn vorhanden), nach Instanzierung mittels Reflektion.

19.02.2009 - 22:13 Uhr

Ne, das war eine "stink-normale" IOException ohne InnerException(s)

19.02.2009 - 22:03 Uhr

Hallo Peter Bucher,

vielen Dank nochmal für Deinen Hint. Codeeinsparung > 95 Prozent 😁 👍

19.02.2009 - 22:00 Uhr

Hallo JAck30lena,

danke für die Antworten.

Naja, bei try/catch gibt es aber keinen Fehlercode, das bedeutet, dass man die Fehlermeldung parsen, bzw. RegExen muss, und dann auch noch aufpassen, auf welcher Umgebung das genze läuft, denn ein deutsches Framework liefert ja deutsche Fehlermeldungen.

Zu 2)
Hm, das bedeutet, dass ich einen Fehler auf einer anderen Umgebung reproduzieren muss. Ist das wirklich "so umständlich"? Ich dachte an eine Webseite, wo alle Fehlermeldungen aufgelistet sind, nach Sprachen - nun, da wird mir leider nix anderes übrigbleiben.

19.02.2009 - 21:36 Uhr

Es geht mit ...


[AttributeUsage(AttributeTargets.Field)]
  class SerializableField : Attribute {
    
  }

braucht man da eigentlich ein eigenes Attribut oder kann man da vordefinierte (welche?) verwenden?

19.02.2009 - 21:06 Uhr

Hallo Peter Bucher,

das mit den Attributen ist eine super Idee, vielen Dank. Jetzt muss ich nur noch mal gucken ob das mit privaten Instanzvariablen auch geht und dann mittels Reflection bei der Instanzierung die serialisierten Werte setzen. 👍

19.02.2009 - 20:48 Uhr

Hallo "Kollegen",

ist es möglich für Variablennamen einen Ausdruck zu verwenden, so dass beim Refactoring Anpassungen sofort mit durchgeführt werden.

Zum Beispiel kann man das ja bei Klassen so machen ...

String className = typeof(TestClass).FullName;

Gibt es da auch eine Möglichkeit für Variablen? Hintergrund ist der, dass ich für verschiedene Instanzvariablen in einer Interfacemethode zur Serialisierung anmelden möchte. Gegenwärtig habe ich das so gelöst, dass man eine Set und Get-Methode überschreibt und hier den Namen der Variablen (als String) und den Wert (als Object) in ein Dictionary-Objekt packt.

Jetzt dachte ich, dass das einfacher gehen soll und ohne Variablennamen als String. Ich dachte an eine Methode IList<???Identifier??, Object> GetSerializableVariables nur welchen Identifier nehme ich hier.

Gibt es eine andere Möglichkeit, z.B. int, String, double ect. überladen und neuen Typ, z.B. SerializableInt, SerializableString zuweisen und mittels Reflektion werden dann diese Typen nach dem Instanzieren manipuliert?

Wie löst ihr das?

Danke schon mal für etwaige Antworten im Voraus?

19.02.2009 - 20:37 Uhr

Hallo "Kollegen",

ihr kennt doch sicherlich die folgende Fehlermeldung:

... Der Prozess kann nicht auf die Datei "\SCSV51001\blablub" zugreifen, da sie von einem anderen Prozess verwendet wird ...

Wie handelt ihr dies, das kommt nämlich sehr oft vor oder anders gefragt, wie bekomme ich raus, dass ich in dem Moment, wo ich drauf zugreife, sich kein anderer Prozess die Datei "gekrallt" hat. Beim lesen sollte das ja egal sein (dachte ich bisher, doch gerade hat es gefunkt, beim Lesen, was so, auch nach Tests, nie der Fall war. Wahrscheinlich geht das immer nur gut, wenn Prozesse "nur" lesen, wenn einer schreibt, dann kracht es).

Danke schon mal für eine etwaige Antwort im Voraus.

Gruß

btw .. wie bekomme ich eigentlich für deutsche Fehlermeldungen die englischen her? Gibt es da eine spezielle Seite .. Fehlercodes is ja leider net 🙁