Hintergrund In einer Windows Form Applikation wird in einer Form ein DataGridView verwendet das je nach Bedarf Schreibgeschützt (Alle Spalten) oder zur Eingabe von Daten (nicht Schreibgeschützt) verwendet werden soll. Soweit würde die Anforderung auch kein Problem darstellen, wenn - aber dazu lest einfach den Rest des Beitrags. Nun ist es aber so, dass auch in der Variante indem das DataGridView als Eingabe (also nicht Schreibgeschützt) verwendet wird, die eine oder andere Spalte des DataGridView sehr wohl (zur Zeit der Entwicklung, also nicht zur Laufzeit) auf Schreibgeschützt gesetzt wurden. Eigentlich sollte das ganze kein Problem darstellen, also "Einfach sein", wie es im Lied der FANTA4 beschrieben ist, aber wie geht es im Lied weiter; "is es aber nicht", und genau so ist es auch in dem hier beschriebenen Fall. Das Problem liegt daran, dass durch das setzen der ReadOnly Eigenschaft des DataGridView auf True das DataGridView "vergisst" welche Spalten des DataGridView während der Entwicklungszeit auf ReadOnly gesetzt wurden. Nachdem man während der Laufzeit die ReadOnly Eigenschaft des DataGridView einmal auf True und dann später wieder zurück auf False gesetzt hat, sind alle Spalten, auch diese die vorher ReadOnly waren plötzlich nicht mehr Schreibgeschützt. Die Idee Die Idee der nachfolgenden Lösung war schnell entstanden und wird im folgenden beschrieben. Beim Start der Windows Form (am besten direkt im Konstruktor) muss man die Information der Schreibgeschützten Spalten sichern, und diese nachdem die ReadOnly Eigenschaft des DataGridView zurück auf False geändert wurde, einfach wieder auf die ursprünglichen Werte herstellen. Die Lösung Um die Information, welche Spalten beim Start der Windows Form Schreibgeschützt sind zu speichern, verwende ich ein typisiertes Array von int Werten um den Spalten Index der Schreibgeschützten Spalten zu sichern. private List<int> saveReadOnlyColumn;
Die Logik zum Speichern der Informationen habe ich in eine eigene Methode (siehe nachfolgenden Code der Methode saveReadOnlyColumnInformation) ausgelagert, welche ich dann einfach am Ende des Konstruktor aufrufe.
private void saveReadOnlyColumnInformation()
{
saveReadOnlyColumn = new List<int>();
foreach (DataGridViewColumn o in dgVerladung.Columns)
{
if (o.ReadOnly)
{
saveReadOnlyColumn.Add(o.Index);
}
}
}
So nun haben wir die Informationen der Schreibgeschützten Spalten im typisierten Array für den späteren Gebrauch gespeichert.
Dann kümmern wir uns nun noch darum, diese gespeicherte Information immer dann wenn die ReadOnly Eigenschaft auf False gesetzt wurde, wieder auf die Startwerte zurücksetzen.
Hierzu können wir einfach den ReadOnlyChanged Event des DataGridView verwenden, welcher immer dann ausgelöst wird, wenn man im Code die ReadOnly Eigenschaft ändert.
Nachfolgend ist der Code dargestellt um die vorher gespeicherten Schreibgeschützten Spalten wieder herzustellen
private void dgVerladung_ReadOnlyChanged(object sender, EventArgs e)
{
if (dgVerladung.ReadOnly == false)
{
foreach (int i in saveReadOnlyColumn)
{
dgVerladung.Columns[i].ReadOnly = true;
}
}
}
Der Vollständigkeit halber hier noch der Code des Konstruktor's (Hier erklärt die Zeile 4) dargestellt.
public formPlanungen()
{
InitializeComponent();
saveReadOnlyColumnInformation();
Application.Idle += new EventHandler(Application_Idle);
}
Programmierung | Code | Tips und Tricks | C#
Seite Heute ist die neue Version 4.1 des Software Tools ReSharper verfügbar. Die Version kann man hier herunterladen. Die wichtigsten Feature dieser Version sind: - Support für Microsoft Visual Studio 2008 SP1
- Verbesserte ASP.NET Bearbeitung
- Geschwindigkeit Verbesserungen
- ... und vieles mehr
++ Nachtrag vom 05.12.2008 ++ Bitte die Hinweise im Kommentar zur Veröffentlichung dieses Beitrags beachten. Eigentlich sollte dieser Beitrag am 02.09.2008 und nicht am 02.12.2008 an den Blog übertragen werden. Nun ist aber wie von Geisterhand dieser Beitrag in einem Mail Server hängen geblieben um dann exakt 3 Monate später ebenfalls wie von Geisterhand doch an den Blog übertragen zu werden.
Programmierung | Tools
Problemstellung Die Daten eines DataGridView sollen über die Zwischenablage in verschiedene andere Anwendungen (unter anderem auch in Excel) kopiert und eingefügt werden können. Hierbei soll es wahlweise Möglich sein, beim kopieren in die Zwischenablage, die Spaltenköpfe des DataGridView mit in die Zwischenablage zu kopieren. Außerdem soll es neben der Tastenkombination Strg + C auch über Programmcode (Button und oder Contextmenü) möglich sein, die Daten in die Zwischenablage zu kopieren. Umsetzung - Teil 1 Spaltenüberschriften einschließen Ob die Spaltenköpfe beim kopieren der DataGridView Daten mit einbezogen werden kann über die Eigenschaft ClipboardCopyMode des DataGridView gesteuert werden. Die Eigenschaft kann auf folgende Werte gesetzt werden: Der Standardwert lautet EnableWithoutHeaderText (Kopieren der markierten Daten ohne Spaltenköpfe). Um sicherzustellen, dass auch die Spaltenköpfe mit in die Zwischenablage kopiert werden, muss der Wert EnableAlwaysIncludeHeaderText gesetzt werden. DataGridView1.ClipboardCopyMode = EnableAlwaysIncludeHeaderText ;
Umsetzung - Teil 2 Programmatisches kopieren der Zwischenablage
Um per Programmcode die Daten (mit oder ohne Spaltenköpfe) in die Zwischenablage zu kann der folgende Code verwendet werden:
Clipboard.SetDataObject(DataGridView1.GetClipboardContent(), true);
Programmierung | Code | C#
Wer sich mit Drag und Drop innerhalb des DataGridView beschäftigt wird unter Umständen ermitteln wollen, ob die Maus auf einem Spalten oder Zeilenkopf gedrückt und festgehalten wurde. Zu diesem Zweck verwende ich die nachfolgende Methode "IsCellOrRowHeader". Hier die C# Methode IsCellOrRowHeader: private bool IsCellOrRowHeader(int x, int y)
{
DataGridViewHitTestType dgt = dgTodo.HitTest(x, y).Type;
return (dgt == DataGridViewHitTestType.Cell ||
dgt == DataGridViewHitTestType.RowHeader);
}
Ich glaube ich hatte die Methode als Grundgerüst mal in Internet gefunden, heute wo ich diesen Beitrag verfasse, habe ich die Suchmaschine angeworfen um einen Link auf den ursprünglichen Autor bzw, den Beitrag zu finden. Ich konnte aber die Seite nicht mehr finden, wenn also jemand dieses Beitrag liest und weiß wo der Ursprung dieser Methode herkommt, dann hinterlasst doch bitte einen Kommentar.
Eine VB Variante dieser Funktion habe ich gerade doch noch gefunden.
Hier der Link: IsCellOrRowHeader in VB
Programmierung | Code | C#
Auf meinem Beitrag "Erweiterungsmethoden am Beispiel einer isNumeric Methode für die String Klasse - C#" habe ich einige Interessante Reaktionen gehabt. Auch wenn die Reaktionen mal wieder über E-Mail und im Gespräch gemacht wurden und nicht über Kommentare direkt am Beitrag gemacht wurden. Ich glaube, soweit sind wir im Deutschsprachigen Raum noch immer nicht. Es gibt immer noch ganz viele die sich einfach nicht trauen einen öffentlichen Kommentar zu hinterlassen. In USA und England ist das ganz anders. Aber gut das ist ein anderes Thema. Unter anderem wurde darauf hingewiesen, dass die Erweiterung der String Klasse um die Methode isNumeric ganz nett sei, aber nicht das gleiche wie eine Statische Methode wie ich sie im Beitrag "isNumeric - c# oder csharp" beschrieben habe. Die isNumeric Methode aus dem letztgenannten Beitrag lässt sich auf jedes Objekt und nicht nur auf Strings anwenden. Und natürlich habe diese Kollegen recht, mit dieser Aussage. Dann wollen wir doch einfach unsere Implementierung aus dem erst genannten Beitrag so ändern, dass Sie auch auf jedes Objekt angewendet werden kann. Schauen wir uns dazu doch einmal die Herkunft der Datentypen an. Alle Objekt die wir erzeugen wurde irgendwann man vom Typ object abgeleitet, also warum erweitern wir nicht einfach die Basis aller Klassen und Strukturen. Hier nun die Erweiterungsmethode isNumeric für den Basis Objekt Type object: public static class ObjectExtensions
{
public static bool isNumeric(this object o)
{
double retNum;
bool isNum = Double.TryParse(Convert.ToString(o), System.Globalization.NumberStyles.Any,
System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
return isNum;
}
}
Und wie man nachfolgend sehen kann, ist es nun möglich die Methode isNumeric auf nahezu jedes Objekt anzuwenden:
static void Main()
{
int i = 10;
long l = 313131311;
double d = 12.12;
string s = "100";
test(i);
test(l);
test(d);
test(s);
}
private static void test(object o)
{
if (o.isNumeric())
{
Console.WriteLine("Ja das geht auch");
}
}
Programmierung | Tips und Tricks | C#
Erweiterungsmethoden sind statische Methoden die in statischen Klassen definiert werden und als ersten Parameter das Schlüsselwort this mit nachfolgendem Type der zu erweiternden Klasse an die Methode übergibt. Diese Erweiterungsmethoden werden dann als Instanz Methode (wie z.B. die ToString() Methode) des jeweiligen Objekts verwendet. Vor einiger Zeit hatte ich einen Beitrag geschrieben der sich mit der C# Implementierung der unter VB verfügbaren Funktion isNumeric beschäftigte. In diesem Beitrag hatte ich eine public static Methode in einer Helper Klasse geschrieben, ich dachte es wäre anschaulich wenn wir nun diese Implementierung als Erweiterungsmethode der String Klasse vornehmen. Hier zuerst noch einmal die Klassische Implementierung vor C# 3.0 public static class MathHelper
{
public static bool IsNumeric(object Expression)
{
bool isNum;
double retNum;
isNum = Double.TryParse(Convert.ToString(Expression), System.Globalization.NumberStyles.Any,
System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
return isNum;
}
}
Diese konnte man wie folgt verwenden:
string o = "100";
if (MathHelper.IsNumeric(o))
Console.WriteLine("Ja die Eingabe ist Numerisch");
else
Console.WriteLine("Keine numerischen Eingabe");
So und nun zur Implementierung der isNumeric Funktionalität als Erweiterungsmethode der String Klasse:
Nun kann man mit dem C# 3.0 Feature der Erweiterungsmethoden (Extension Methods) diese aber auf viel elegantere weise lösen. Ich möchte dies hier an einem Beispiel der String Klasse demonstrieren. Wir erweitern also unsere String Klasse durch eine Erweiterungsmethode isNumeric.
Hierzu müssen wir zuerst eine Statische Klasse erstellen, in welcher wir dann die Erweiterungsmethode(n) deklarieren können.
public static class StringExtensions
{
public static bool isNumeric(this string s)
{
double retNum;
bool isNum = Double.TryParse(s, System.Globalization.NumberStyles.Any,
System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
return isNum;
}
}
Verwendet wird das dann so:
string o = "100";
if (o.isNumeric())
Console.WriteLine("Ja die Eingabe ist Numerisch");
else
Console.WriteLine("Keine numerischen Eingabe");
Programmierung | Code | C#
In einem Projekt musste ich mit Daten arbeiten welche durch eine PHP Anwendung erstellt wurden (werden). Unter anderem wird dabei die IP Adresse des Besuchers in einer Datenbank gespeichert. Bei der Speicherung der IP wird diese aber nicht als String sondern als long Wert gespeichert. PHP bietet hierzu folgende Funktionen an: Obwohl, oder gerade weil ich kein PHP Guru bin, stellt sich mir nun die Aufgabe diese Long Werte wieder in IP Adressen zurück zu übersetzen. Und eben nicht mit PHP sondern mit C#. Hierzu habe ich mir 2 statische Methoden in einer IP-Helper Klasse erstellt die genau diese Funktionalität der PHP Funktionen in C# nachempfindet. Nachfolgend meine beiden C# Methoden: public static long ip2long(string ipAddress)
{
System.Net.IPAddress ip;
if (System.Net.IPAddress.TryParse(ipAddress,out ip))
{
return (((long) ip.GetAddressBytes()[0] << 24) | (ip.GetAddressBytes()[1] << 16) |
(ip.GetAddressBytes()[2] << 8) | ip.GetAddressBytes()[3]);
}
return -1;
}
public static string long2ip(long ipAddress)
{
System.Net.IPAddress ip;
if (System.Net.IPAddress.TryParse(ipAddress.ToString(), out ip))
{
return ip.ToString();
}
return "";
}
Eventuell braucht das ja auch noch jemand anderes !
Programmierung | C#
Wer sich mit WinForms Entwicklung beschäftigt wird wohl auch mit dem DataGridView Control arbeiten. Wenn man nun für die Spalte ein Checkbox Control "DataGridViewCheckBoxColumn" verwendet kommt es häufig vor, dass man auch auf das CheckedChanged Ereignis reagieren möchte. Leider gibt es mit dem DataGridView Control nicht direkt die Möglichkeit einen solchen Event zu nutzen. Außerdem wird bei der Verwendung von Datengebundenen Informationen an das DataGridView Control erst nach verlassen der aktiven Zelle die Dirty Eigenschaft der Zelle gesetzt, so dass dies nicht mehr zulässt direkt auf Änderung der Checked Eigenschaft zu reagieren. Es gibt aber, wie fast immer, Abhilfe für das Problem. Um bereits vor dem Verlassen der Zelle, die Änderung der Checked Eigenschaft auszuwerten, kann man dem DataGridView den Event CurrentCellDirtyStateChanged hinzufügen und in diesem Event den nachfolgend dargestellten Code hinzufügen: Als Einfaches Beispiel (Allgemeingültig): private void dgVerladung_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (((DataGridView)sender).CurrentCell.OwningColumn is DataGridViewCheckBoxColumn)
{
((DataGridView)sender).CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
Ein etwas komplexeres Beispiel (nur als Beispiel):
private void dgWorkTime_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgWorkTime.CurrentCell.OwningColumn.Name == disabledDataGridViewCheckBoxColumn.Name)
{
dgWorkTime.CommitEdit(DataGridViewDataErrorContexts.Commit);
Day meinTag = (Day)dgWorkTime.CurrentCell.OwningRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Value;
bool disabled = (bool)dgWorkTime.CurrentCell.Value;
dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].ReadOnly = disabled;
dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].ReadOnly = disabled;
if (disabled)
{
dgWorkTime.CurrentRow.Cells[wochentagDataGridViewTextBoxColumn.Name].Style.BackColor =
Properties.Settings.Default.WorkingTimeDisabledColor;
dgWorkTime.CurrentRow.Cells[disabledDataGridViewCheckBoxColumn.Name].Style.BackColor =
Properties.Settings.Default.WorkingTimeDisabledColor;
dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
Properties.Settings.Default.WorkingTimeDisabledColor;
dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
Properties.Settings.Default.WorkingTimeDisabledColor;
}
else
{
dgWorkTime.CurrentRow.Cells[wochentagDataGridViewTextBoxColumn.Name].Style.BackColor =
dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
dgWorkTime.CurrentRow.Cells[disabledDataGridViewCheckBoxColumn.Name].Style.BackColor =
dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
dgWorkTime.CurrentRow.Cells[startWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
dgWorkTime.CurrentRow.Cells[endWorkingTimeDataGridViewTextBoxColumn.Name].Style.BackColor =
dgWorkTime.CurrentRow.Cells[dayEnumDataGridViewTextBoxColumn.Name].Style.BackColor;
}
}
Programmierung | Tips und Tricks | C#
Lambda Expressions in C# umfassen sowohl Ausdrücke (expressions) als auch Anweisung's Blöcke (statement blocks). Egal welche Art von Lambda Expressions verwendet werden, der Lambda Operator => wird jeweils dazu verwendet um die Expression zu beschreiben. Was ist der Unterschied zwischen einem Ausdruck und einem Anweisung's Block. Exemplarische Lambda Expression Ausdrücke x => x * 5
(int x) => x * 5
o => o.FirstName == "Peter"
(Order o) => o.FirstName == "Peter"
Exemplarische Lambda Expression Anweisung's Blöcke
Wo überall setzt man Lambda Expressions ein?
Ein großes Einsatzgebiet von Lambda Expressions sind zum Beispiel zusammen mit den ebenfalls in C# 3.0 neuen Extension Methods.
Programmierung | C#
Da sich Microsoft nicht einig ist wie man die Tage einer Woche in einer Aufzählung hinterlegt, muss man damit klarkommen, dass es verschiedene Auflistungen in verschiedenen Namensräumen gibt. In einem meiner Projekte war ich nun auch damit konfrontiert, dass ich mit den beiden Aufzählungen: - DayOfWeek aus dem Namensraum System
und - Day aus dem Namensraum System.Windows.Forms
gleichzeitig zu tun hatte. Dabei ergab sich die Notwendigkeit dass DayOfWeek in Day umwandeln musste. Nachfolgend nun eine kleine Methode die aus DayOfWeek ein Day zurückgibt. public static Day DayOfWeekToDay(DayOfWeek dow)
{
return (Day)Enum.Parse(typeof(Day), dow.ToString());
}
Programmierung | Tips und Tricks | C#
Auch wenn es auf den ersten Blick absurd erscheint, dass man eine Funktion benötigt um den ersten Tag eines Monats zu ermitteln, aber es gibt Situationen in welchen man Datumsbereiche bestimmen möchte in denen man aus einem beliebigen Datum den ersten Tag eines Monats ermitteln muss. Nachfolgen eine C# Methode die genau dies macht: public static DateTime FirstDateOfTheMonth(DateTime date)
{
return date.AddDays(-(date.Day - 1));
}
Programmierung | Tips und Tricks | C#
Ein einzelnes Control auf Readonly zu setzen ist einfach, aber was ist wenn man alle Controls einer Winforms Anwendung auf Readonly setzen möchte. Und ich meine wirklich auf Readonly nicht auf Enabled indem man ein Panel als Container der Controls verwendet und dann die Panel Eigenschaft Enabled auf false setzt. Zu diesem Zweck habe ich mir eine kleine Routine erstellt, welche Rekursiv alle Controls (die ich möchte) einer ControlCollection auf Readonly setzt. Hier nun die Methode setReadOnly: private void setReadOnly(Control.ControlCollection Controls)
{
foreach (Control control in Controls)
{
if (control is TextBox)
{
((TextBox)control).ReadOnly = true;
}
if (control is ComboBox)
{
((ComboBox)control).Enabled = false;
}
if (control is DateTimePicker)
{
((DateTimePicker)control).Enabled = false;
}
if (control.Controls.Count > 0)
{
setReadOnly(control.Controls);
}
}
}
Programmierung | Tips und Tricks | C#
In Gesprächen mit Entwicklern taucht immer wieder die Frage auf was Controls von Components unterscheidet. Die Kurzbeschreibung hierzu lautet: Im Gegensatz zu Controls haben Components keine Visuellen Eigenschaften. Es wird also nichts auf einer Form angezeigt. Das was Components mit Controls gemeinsam haben, ist, dass man Sie zu einer Form hinzufügen kann und dass sie beide zur Design Time konfiguriert werden können.
Programmierung
Vor einiger Zeit hatte ich in meinem Beitrag: "enum Werte an ComboBox binden" beschrieben wie man die Werte von Aufzählungen an eine ComboBox binden kann. Nun habe ich bereits mehrfach die Frage gestellt bekommen (zum letzten mal Gestern in einem Kommentar zu diesem Beitrag) wie man denn die Wert auch wieder auslesen kann. Das geht zum Beispiel so: private void cmbEnum_SelectedIndexChanged(object sender, EventArgs e)
{
switch ((AppHeightDrawMode)cmbEnum.SelectedItem)
{
case AppHeightDrawMode.TrueHeightAll:
break;
case AppHeightDrawMode.FullHalfHourBlocksAll:
break;
case AppHeightDrawMode.EndHalfHourBlocksAll:
break;
case AppHeightDrawMode.FullHalfHourBlocksShort:
break;
case AppHeightDrawMode.EndHalfHourBlocksShort:
break;
default:
break;
}
}
Wobei der folgende Code das entscheidende ist: (AppHeightDrawMode)cmbEnum.SelectedItem
Wir könnte damit auch so arbeiten: AppHeightDrawMode myDrawMode = (AppHeightDrawMode)cmbEnum.SelectedItem;
Dabei steht dann in myDrawMode der Enum Wert der mit der ComboBox ausgewählt wurde.
Dieses Beispiel bezieht sich auf den Beitrag: enum Werte an ComboBox binden
Kurz und Bündig | Programmierung | Code | C#
Mit Visual Studio 2008 hat man zum ersten mal die Möglichkeit des sogenannten Multi-Targeting bekommen. Das bedeutet, dass man bereits beim anlegen eines Projekts den gewünschten Zielframework angeben kann/muss. Die zur Auswahl stehenden Framework's sind: Natürlich stehen dann auch nur die im gewählten Framework enthaltenen Funktionalitäten zur Verfügung. So kan man zum Beispiel bei der Auswahl vom Framework 2.0 kein Linq einsetzen usw. Nun gut, aber das ist nicht das Thema dieses Beitrags. Nehmen wir an wir haben ein Windows Forms Projekt erstellt das für den Framework 2.0 sein soll. Nun erstellen wir für unser Projekt noch ein Setup Projekt. Achten auch schön drauf, dass wir auch beim Setup Projekt das richtige Zielframework auswählen. Und nachdem wir noch alle benötigten Einstellungen für das Setup Projekt gemacht haben, müssen wir dann beim Test feststellen, dass obwohl wir das NET Framework 2.0 als Zielframework gewählt haben die Setup Routine das Framework 3.5 als Voraussetzung verlangt. Was haben wir falsch gemacht? --> Nichts. Aus welchem Grund auch immer (na ja irgend jemand hat wohl einen Fehler gemacht) wird beim erstellen eines Setup Projekten mit VS2008 die Voraussetzungen (Prerequisites) so gesetzt, dass der NET Framework 3.5 vorausgesetzt wird, egal was man ausgewählt hat. Aber, das kann man einfach manuell ändern. Also Setup Projekt wie beschrieben erstellen, dann in die Eigenschaften des Setup Projektes gehen, dort den Button "Prerequisites" betätigt und in diesem Fenster nun das richtige Framework auswählen. Auf OK Klicken --> Fertig !!
Programmierung | Tips und Tricks | Visual Studio
Bedingte Kompilierung oder Kleines Präprozessor ABC für C# Einleitung Der Präprozessor unter C# ist nicht zu vergleichen mit den wesentlich komplexeren Präprozessoren der C und C++ Compiler. Es gibt z.B. weder eine Makroverarbeitung noch die Möglichkeit mit #include sogenannte Header Dateien zu inkludieren. Da dieser Beitrag "Kleines Präprozessor ABC" heißt, möchte ich darauf verzichten auf die Einzelheiten solcher Unterschiede einzugehen. Verfügbare Direktiven des C# Präprozessor | Direktive | Typ | Verwendung | | #define | Bezeichner | Mit #define wird ein Bezeichner gesetzt. Stellen Sie sich einen Bezeichner wie eine boolsche Variable vor. #define ist dann so wie das setzen einer Variablen auf true. Einem Bezeichner können keine Werte zugewiesen werden | | #undef | Bezeichner | Mit #undef heben Sie die Gültigkeit eines Bezeichners auf. Sie können dies auch mit dem setzen einer Variablen auf false setzen vergleichen. | | #if | Ausdruck | Der Code innerhalb des mit #if eingeleiteten Abschnittes wird ausgeführt wenn der Ausdruck wahr ist (Bei einfachen #if Ausdrücken ist das der Fall wenn der auf #if folgende Bezeichner gesetzt ist. | | #else | | Ergibt der diesem #else vorangegangene Ausdruck false, dann wird der Bereich des #else ausgeführt. | | #elif | Ausdruck | Anstelle des #else kann man mit Hilfe des #elif Ausdrucks diesen Bereich nur dann ausführen lassen, wenn der Angegebene Ausdruck true ergibt | | #endif | | Zeigt das Ende eines Ausdrucks bzw. einer zusammengehörenden Reihe von Ausdrücken an. | | #warning | Ausdruck | Ausgabe einer Warnung während des Kompilieren's | | #error | Ausdruck | Ausgabe einer Fehlermeldung während des Kompilieren's | | #line | Ausdruck | Ermöglicht die Manipulation und Ausgabe von Zeilennummern, nähere Informationen hierüber kann man hier nachlesen | | #region | Direktive | Bezeichnet den Anfang einer Region im Quelltext. Beispiel #region Events | | #endregion | | Ende einer mir #region eingeleiteten Region im Quelltext | | #pragma | Direktive | Mit #pragma gibt man spezielle Anweisungen für das Verhalten des Compilers. Beispiel: #pragma warning disable 123, 4567 zum Abschalten bestimmter Compiler Warnungen. Nähere Informationen über die #pragma Direktiven gibt es hier | Operatoren für die Verwendung mit Ausdrücken | Operatoren | Beispiel | Funktionsweise | | ! | #if !DEBUG | Das ! negiert den Wert von var. Im Beispiel bedeutet das, ist DEBUG nicht definiert, wird der #if Bereich ausgeführt. | | == | #if DEBUG == true | == Prüft auf Gleichheit. Im Beispiel bedeutet dies, wenn DEBUG definiert ist, dann wird der Bereich des #if Ausdrucks ausgeführt. | | !== | #if DEBUG !== true | == Prüft auf Ungleichheit. Im Beispiel bedeutet dies, wenn DEBUG definiert ist, dann wird der Bereich des #if nicht Ausdrucks ausgeführt. | | && | #if DEBUG && TRACE | Logisches Und. Wenn DEBUG und TRACE definiert sind, wird der #if Ausdruck wahr, also ausgeführt. | | || | #if DEBUG || TRACE | Logisches Oder (kein Exklusiv Oder). Der #if Ausdruck ist wahr wenn DEBUG und/oder TRACE definiert sind. | Beispiele Einfaches Beispiel mit einem Define und einer Bedingten Kompilierung #define DEBUG #if DEBUG // Diese Bereich wird nur kompiliert wenn DEBUG definiert ist System.Diagnostics.Debug.WriteLine("DEBUG ist definiert"); #endif Beispiel einer Anwendung mit einem error Ausdruck und einem #elif #define RELEASE #define DEMO #if RELASE && DEMO #error RELEASE und DEMO dürfen nicht zusammen verwendet werden #elif DEMO // HIER zum Beispiel der Code, der eine DEMO Version beschränkt, oder was auch immer #endif
Programmierung | Tips und Tricks | C#
Vor einiger Zeit hatte ich in diesem Beitrag beschrieben wie man Transaktionen mit netTiers verwenden kann. In diesem Beitrag nun möchte ich eine weiter fortgeschrittene Technik zeigen mit welcher man die Transaktion auch in verschachtelten und rekursiven Methoden verwenden kann. Hintergrund Obwohl es keine "echten" verschachtelten Transaktionen sind, habe ich die Überschrift so gewählt, da das Ergebnis dem von verschachtelten Transaktionen gleicht. Der nachfolgende Beitrag erläutert die notwendigen zusätzlichen Vorkehrungen die man treffen muss um Transaktionen in verschachtelten und rekursiven Methodenaufrufen zu verwenden. Lösungsansatz Um Transaktionen in verschachtelten Aufrufen verwenden zu können, ist es notwendig festzustellen, ob bereits eine Transaktion vorhanden ist und ob diese geöffnet (BeginTransaktion) ist. Gibt es eine aktive (äussere) Transaktion, so ist innerhalb der geschachtelten Methode dafür zu sorgen, dass keine lokale Transaktion erzeugt wird, bzw. dass keine globalen Methoden Aufrufe (wie Commit und Rollback) der Transaktion aufgerufen werden (Die Transaktion gehört der aufrufenden Methode, die sich darum kümmern muss ob ein Commit oder Rollback gemacht wird). Lösung - Erweiterte Version mit localTransaction int retval = 0; // Definieren einer int variablen die einen Rückgabewert der Methode aufnehmen kann. bool localTransaction = false; // Flag welches verwendet wird um die Verwendung eine lokalen Transaktion zu signalisieren if (ConnectionScope.Current.TransactionManager == null) { ConnectionScope.Current.TransactionManager = ConnectionScope.CreateTransaction(); localTransaction = true; // Es gab keine äussere Transaktion also haben wir eine lokale erstellt und auch gleich geöffnet (impliziter Aufruf von BeginTransaction) } TransactionManager tm = ConnectionScope.Current.TransactionManager; if (!tm.IsOpen) // jetzt prüfen ob auch eine geöffnete Transaktion vorhanden ist (wenn nicht, dann eine lokale Transaktion aufmachen) { tm.BeginTransaction(); localTransaction = true; } // Hier nun der eigentlich Code // Wenn alle Datenbankoperationen durchgeführt werden konnten, wird 0 zurückgegeben, sonst ein int Wert aus dem die Aufrufende Methode den Grund des Fehlers erkennen kann und dadurch entscheiden kann ob ein Commit oder ein Rollback durchgeführt werden soll. if (retval == 0) { if (localTransaction ) // Nur wenn eine lokale Transaktion, dann ein Commit, wenn alles OK war. { tm.Commit(); } } else { if (localTransaction ) // Nur wenn eine lokale Transaktion, dann ein Rollback wenn ein Fehler auftritt { tm.Rollback(); } } return retval;
Programmierung | Tips und Tricks | C#
Jeder der sich mit der Entwicklung von Programmen beschäftigt kommt früher oder später um die Verwendung eines Debuggers zur Fehlersuche nicht umhin. Doch bevor ich weiter schreibe zuerst eine kurze Begriffserklärung der wichtigsten Begriffe: Was bedeutet denn Debuggen und was machen wir um einen Fehler zu finden? - Das Programm wird in der Entwicklungsumgebung im Debug Modus gestartet.
- Es werden Breakpoints gesetzt.
- Inhalte von Variablen werden angesehen (evtl. verändert)
- Der Ablauf des Programms wird verfolgt
Um Komplexere Abläufe zu beobachten bietet es sich an Werte einfach über nachfolgende Methoden - System.Diagnostics.Debug.WriteLine("")
- System.Diagnostics.Trace.WriteLine("")
im Ausgabefenster von Visual Studio auszugeben. Soweit sind die Möglichkeiten recht umfangreich um einem Fehler auf die Spur zu kommen. Was aber wenn der Fehler, der beim Kunden auftritt, einfach nicht auf unserem Entwicklungsrechner reproduzierbar ist. Wie kann man vorgehen um mehr Informationen über das Problem zu erhalten, ohne dem Kunden eine Entwicklungsumgebung auf seinen Rechner zu bügeln. Eventuell würde es ja schon helfen wenn wir die Originaldaten (Datenbank, Dokumente etc.) vom Kunden hätten, auch das können wir uns in vielen Fällen einfach abschminken und darüber sollten wir in den meisten Fällen auch gar nicht nachdenken. Man kann aber bereits während der Entwicklung eine Menge dazu beitragen, später einmal eine recht gute Fehleranalyse auch vor Ort beim Kunden zu erhalten. Wenn wir während der Entwicklung, an Stellen an von welchen wir glauben Informationen für eine Fehleranalyse bekommen zu können bereits vorsorglich über die Write... Methoden der Klassen Debug und Trace des System.Diagnostics Namespace Informationen bereitstellen, dann können wir später diese Informationen dazu verwenden einen möglichen Fehler zu analysieren. Damit wir aber später in einer Anwendung, die dann nicht im Debugger ausgeführt wird, an diese Informationen herankommen, können wir durch Einrichtung eines TraceListener dafür sorgen, dass diese Informationen auf einem beliebigen (na ja fast, es muss ein TraceListener für die gewünschte Ausgabe vorhanden sein, oder wir müssen uns einen eigenen TraceListener schreiben) Ausgabemedium wie Standard Output, einer Textdatei, einer XML Datei eine ... usw. einfach ausgeben werden. Schauen wir uns doch kurz nachfolgende Aufstellung an welche TraceListener uns das NET Framework (ab 2.0) bereits zur Verfügung stellt: Die Abstrakte Basis Klasse im Namespace System.Diagnostics (Von der können wir eigene TraceListener ableiten): Folgende Implementierungen sind im Namespace System.Diagnostics verfügbar: - ConsoleTraceListener
- DefaultTraceListener
- DelimitedListTraceListener
- Eventing.EventProviderTraceListener
- EventLogTraceListener
- EventSchemaTraceListener
- TextWriterTraceListener
- XmlWriterTraceListener
Was mach nun ein solcher TraceListener und wie können wir Ihn verwenden. In einer .NET Anwendung wird in der Klasse Trace des Namespace System.Diagnostics unter anderem eine statische Eigenschaft Listeners vorgehalten, diese Auflistung enthält alle TraceListener die bei den Debug und Trace Methoden aufgerufen werden. Wird nun in unserem Code ein Methodenaufruf wie System.Diagnostics.Debug.WriteLine(txtName.Text) durchgeführt, dann führt das dazu, dass für jede in der Auflistung enthaltenen Listener die implementierte Methode WriteLine aufgerufen wird. Wenn wir das in einem Visual Studio Projekt im Debugger Mode machen ohne irgendwelche Änderungen an der Konfiguration oder Manipulation der Auflistung in unserem Code vorzunehmen, dann enthält die Auflistung Listeners lediglich den Default TraceListener, und das ist der Listener, welche die Ausgabe im Ausgabefenster von Visual Studio vornimmt. Wie können wir nun einen anderen TraceListener, als Beispiel den TextWriterTraceListener (Dieser Listener schreibt die Daten in eine Textdatei) verwenden um die Ausgaben für uns verwendbar zu machen, auch ohne die Anwendung im Visual Studio auszuführen? Es gibt 2 Arten wie wir den/die Listener angeben können: - Aus dem Source Code
- Aus der App.config
Angabe eines Listener aus dem Source Code Hier im Beispiel einer Windows Forms Anwendung: Wir fügen einfach in den Konstruktor der Windows Form folgenden Code hinzu: System.Diagnostics.TextWriterTraceListener tListener = new System.Diagnostics.TextWriterTraceListener("Trace.Log"); System.Diagnostics.Trace.Listeners.Add(tListener); Dieser Code fügt einen neuen Listener, in unserem Fall den TextWriterTraceListener zur Auflistung des Listener hinzu (siehe nachfolgende Abbildung): Es geht aber noch einfacher, ja wirklich "noch einfacher". Angabe des Listener in der App.Config Um einen oder auch mehrere Listener hinzuzufügen oder auch um einen Listener (z.B. den Default Listener) zu entfernen können wir innerhalb des Bereich "Config" einen eigenen Unter-Bereich System.Diagnostics hinzufügen. In diesem Bereich können wir dann über die App.Config Datei auf die Listeners Auflistung zugreifen und Veränderungen durchführen. Hier ein Beispiel, welche den TextWriterListener hinzufügt und außerdem noch den Default Listener entfernt. <configuration> <system.diagnostics> <trace autoflush="true" indentsize="4"> <listeners> <add name="FileListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Trace.log" /> <remove name="Default" /> </listeners> </trace> </system.diagnostics> </configuration> Soweit zur Einrichtung von TraceListener zur besseren Fehleranalyse beim Kunden. Übrigens sind Trace und Debug Statements eigentlich vollkommen gleich, ob ein Trace oder ein Debug Aufruf ausgeführt wird, hängt davon ab, ob die vordefinierten Konstanten für bedingte Compilierung während der Erstellung des Programms gesetzt waren (In Visual Studio findet man diese Schalter übrigens in den Projekt Eigenschaften auf dem Karteireiter Build (Erstellen)). Und nun viel Spaß bei der Suche nach den Käfern ! Ich finde die Geschichte um die Herkunft der Bezeichnung Bug für einen Programmfehler trotz der Behauptung das es sich um eine moderne Legende handeln soll, schön und lesenswert. Wer möchte kann hier mehr über diese Legende lesen PS: Ich Denke, in nicht so ferner Zukunft, werde ich dann mal über die Erstellung eines eigenen Listener berichten.
Programmierung | Tips und Tricks | C#
Auch wenn schon ... was weiß ich wie viele diese Nachricht ". NET Source Code veröffentlicht" seit Gestern veröffentlicht haben, mache ich meinen Beitrag unter dem Motto "Eigene Notizen". Hier also jetzt vor allem aber nicht ausschließlich für mich selbst: Der Source Code für das NET Framework (wenigstens einen Teil davon, siehe nachfolgende Auflistung) wurde von Microsoft veröffentlicht. Für diese Assemblies steht der Source Code zur Verfügung: - Mscorlib.DLL
- System.DLL
- System.Data.DLL
- System.Drawing.DLL
- System.Web.DLL
- System.Web.Extensions.DLL
- System.Windows.Forms.DLL
- System.XML.DLL
- WPF (UIAutomation*.dll, System.Windows.DLL, System.Printing.DLL, System.Speech.DLL, WindowsBase.DLL, WindowsFormsIntegration.DLL, Presentation*.dll, some others)
- Microsoft.VisualBasic.DLL
Der Source Code steht zur Zeit nur in Verbindung mit dem Debugger in VS2008 zur Verfügung, ein Download des gesamten Source Code ist "noch" nicht verfügbar, soll aber wohl auch noch folgen. Wie man VS2008 konfigurieren muss, damit man in das Framework hinein Debuggen kann, wir ausführlich in dem Blog Beitrag von Shawn Burke beschrieben. Der Source Code ist unter der Microsoft Referenz Lizenz (oder wie ich sie nenne Peep Show Lizenz) veröffentlicht. Die sagt hauptsächlich aus: - Gucken -> Erlaubt !
- Alles andere verboten
Ich bin gespannt ob dieses Lizenzmodell richtig verstanden wird, es gibt leider viele die eine Verfügbarkeit des Source Code gleichsetzen mit Open Source. Und selbst bei Open Source wird häufig die Lizenz nicht wirklich beachtet. Na mal sehen wie das ausgeht! PS: Ich habe das übrigens schon getestet, ist wirklich eine schöne Sache, mal schauen wann ich es dann auch mal wirklich brauchen kann. Na ja auf jeden Fall können wir jetzt MS mit der Meldung eines Fehlers auch gleich einen Vorschlag für die Behebung schicken! 
Programmierung | Visual Studio
Unter der Rubrik Kurz und Bündig mal wieder ein VB --> C# Vergleich Was unter VB (Visual Basic) das Schlüsselwort Friend ist, ist unter C# der Zugriffsmodifizierer Internal. Der Zugriff auf Objekte mit dem Zugriffsmodifizierer sind auf Objekte innerhalb des gleichen Assembly beschränkt Mehr Informationen über C# Zugriffsmodifizierer gibt es hier
Kurz und Bündig | Programmierung | C#
Spätestens beim Debuggen von WPF Anwendungen sollte man eine Default Einstellung des Visual Studio 2008 ändern. In den Option kann man dem Debugger sagen, er soll nur Code, den man selbst geschrieben hat Debuggen, Code der von einem Designer in Visual Studio erzeugt wird, wird dann nicht mehr beim Debuggen berücksichtigt. Ein eventuell auftretender Fehler kann dadurch nicht konsequent verfolgt werden. Beim Debuggen von WPF Anwendungen geht das soweit, dass selbst Fehler im XAML Code nicht mehr mit dem Debugger verfolgt werden können. Also sollte man in die Optionen gehen und dort die folgende Einstellung vornehmen. Die Option Enable Just My Code (Managed only) ist per Vorgabe aktiviert. Also einfach die Option deaktivieren und schon verfolgt der Debugger auch Code, der durch Designer erstellt wurden.
Programmierung | Tips und Tricks
Ich habe bereits in einem früheren Beitrag über die Automatischen Eigenschaften als Spracherweiterung (oder Compilererweiterungen) in C# 3.0 bzw. Visual Studio 2008 berichtet. Als ich mir das Konstrukt für die sogenannten "Auto-Implemented Properties" näher betrachtet habe, war ich im ersten Moment ratlos wie die Implementierung einer Schreibgeschützten Eigenschaft funktionieren soll, bzw. wie diese Schreibgeschützten Eigenschaften jemals einen Wert zugewiesen bekommen können. Aber wie gesagt, meine Ratlosigkeit beschränkte sich auf den ersten Moment  Schauen wir uns das Thema doch in Beispielen an. Beispiel Klasse: class Customer { public double TotalPurchases { get; set; } public string Name { get; private set; } // Schreibgeschützt public int CustomerID { get; private set; } // Schreibgeschützt } Der Compiler erzeugt bei diesem Konstrukt eine private aber anonyme variable, auf diese können wir, weil anonym ja nicht zugreifen. Wie also kann man diese Schreibgeschützten Eigenschaften verwenden? Da kam mit zuerst der Gedanke, da kommt natürlich eine weitere Neuerung die "Objekt Initialisierer" zum Zuge. So zum Beispiel: Customer c = new Customer { CustomerID = 1, Name = "Max Mustermann"} Aber nein, das geht nicht ! Wenn man dann aber etwas genauer hinsieht, dann liegt die Lösung eigentlich ganz offensichtlich schon in der Art der Definition der Schreibgeschützten Eigenschaft. Bei der Definition: public string Name { get; private set; } Heißt es nicht { get; read only set; } sondern { get; private set; } Das bedeutet, man erlaubt nur den Private Zugriff auf den Setter. Das wiederum bedeutet, Zugriffe bzw. Wertzuweisungen, wie die nachfolgenden funktionieren einwandfrei. Beispiel Konstruktoren: public Customer() { CustomerID = 1; } public Customer(int id) { CustomerID = id; } Beispiel einer Methode der Klasse: public void incCustomerID() { CustomerID++; } Zusammenfassend bedeutet dies, dass wir auf die Schreibgeschützten Automatischen Eigenschaften einer Klasse, innerhalb der Klasse, also private, schreibenden Zugriff auf die Eigenschaften selbst haben. Wir benötigen also gar kein Wissen über die vom Compiler erzeugte private anonyme Variable um schreibenden Zugriff auf die ansonsten Schreibgeschützte Eigenschaft zu haben.
Programmierung | C#
Bei einigen der unter C# 3.0 Spracherweiterungen geführten Dinge handelt es sich mehr um Compilererweiterungen des C# Compilers der mit Visual Studio 2008 ausgeliefert wird, als um echte Spracherweiterungen für C#. So funktionieren die nachfolgenden Erweiterungen auch dann wenn man Code für das NET Framework 2.0 erzeugt: - Implizit typisierte lokale Variablen
- Objekt Initialisierer
- Anonyme Typen
- Automatische Eigenschaften
Wenn diese Dinge "Spracherweiterungen" und nicht "Compilererweiterungen" wären, dann wäre es nicht möglich ein Programm welches diese Feature verwendet für das NET 2.0 Framework zu erstellen und auf einem Rechner mit "nur" installiertem NET 2.0 Framework auszuführen. Hier ein Beispiel, welches ich mit VS2008 als Konsolenanwendung für den Framework 2.0 erstellt habe und auf einem Rechner mit NET 2.0 Framework ausgeführt habe: class Program
{
// Automatische Eigenschaften
static public double AutoDouble { get; set; }
// Klasse zur Verwendung mit Objekt Initialisierung ohne Konstruktoren
public class Address
{
public string Name { get; set; }
public string Street { get; set; }
public int ZIP { get; set; }
public string City { get; set; }
public override string ToString()
{
return this.Name + "," + this.Street + "," + this.ZIP + "," + this.City ;
}
}
static void Main(string[] args)
{
// Implizit typisierte lokale Variable
var i = 10;
// Verwendung der Eigenschaft AutoDouble
AutoDouble = 12.22;
// Anonymer Typ
var figur = new { Name = "Rechteck", Breite = 1024, Hoehe = 768 };
// Objekt Initialisierung mit Feldbezeichnungen ohne Konstruktor
Address adr = new Address { Name = "Max Musterman", Street = "Platanenweg", ZIP = 4711, City = "Musterhausen" };
// Ausgabe der Werte
Console.WriteLine(i.ToString());
Console.WriteLine(AutoDouble.ToString());
Console.WriteLine(String.Format("Die Adresse lautet {0}",adr.ToString()));
Console.WriteLine(String.Format("Hier der anonyme Typ figur {0}",figur.ToString()));
Console.Read();
}
}
Nicht von diesem Beitrag betroffen sind die echten Spracherweiterungen von C# 3.0 wie:
- Erweiterungsmethoden
- Lambda Expressions
- LINQ
Diese erfordern aber das die Programme für das NET 3.5 Framework erstellt werden.
Programmierung | C#
Ich bin mir sicher, dass bereits ganz viele solcher oder ähnlicher Blog Beiträge darüber berichten das VS2008 veröffentlicht ist. Ich habe auch erst überlegt ob ich trotzdem noch einmal in meinem Blog einen Beitrag darüber erstellen soll, aber ich Denke: - Es wird ja niemand damit geschadet, wenn auch ich darüber berichte
- Ich habe auch vor dem Schreiben dieses Beitrags kurz um Internet recherchiert und konnte dabei keinen Beitrag finden, welcher sich auch mit der Problematik Deinstallation der Beta Version beschäftigt, wenigstens nicht in dem er über seine eigenen Erfahrungen berichtet hat.
- Aus diesem Grund also hier auch mein Senf zu diesem Thema
 So nun aber zum eigentlichen Beitrag: Die Englische(n) Version(en) von Visual Studio 2008 sind ab sofort verfügbar. MSDN Abonnenten können die endgültigen Versionen über Ihren MSDN Download Bereich herunterladen. Was für viele Interessant sein dürfte ist, dass es auch mit Visual Studio 2008 wieder eine Express Version gibt, also eine frei Version die man einfach herunterladen kann. Die Express Version kann man unter nachfolgendem Link finden und herunterladen: http://www.microsoft.com/express/download/ Nach den ersten Gerüchten die man so hört, soll die Deutsche Version tatsächlich noch dieses Jahr veröffentlicht werden. Aber das sind wie gesagt zur Zeit noch, nur Gerüchte. Wer die Beta Version installiert hatte sollte diese vor der Installation der offiziellen Version richtig deinstallieren. Hinweise wie das zu bewerkstelligen ist findet man im Original hier in diesem Link Da ich aber bei der Durchführung dieser Aufgabe viele Abweichungen festgestellt habe, hier trotzdem eine eigene Aufstellung mit meinen tatsächlich zu deinstallierten Programmen und deren Bezeichnungen: "MSDN Library for Visual Studio 2008 Beta" (hatte ich nicht installiert) "Microsoft SQL Server Compact Edition 3.5" "Microsoft SQL Server Compact Edition 3.5 Design Tools" "Microsoft SQL Server Compact Edition 3.5 for Devices" "Microsoft Visual Studio Performance Collection Tools" (war bei mir nicht installiert) "Windows Mobile 5.0 SDK R2 for Pocket PC" "Windows Mobile 5.0 SDK R2 for Smartphone" "Crystal Reports for Visual Studio 2008 Beta 2" "Visual Studio Asset System" (nur wenn installiert ist) "Microsoft Visual Studio Web Authoring Component / Microsoft Web Designer Tools" "Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System Runtime" (war bei mir nicht installiert) "Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System Runtime Language Pack" (non-English editions only) (war bei mir nicht installiert) "Microsoft Visual Studio Tools for Office Runtime 3.0" "Microsoft Document Explorer 2008" "Microsoft Device Emulator 3.0 ENU" "Microsoft .NET Compact Framework 3.5 Pre-Release" "Microsoft .NET Compact Framework 2.0 SP1 oder SP2" ".NET Framework 2.0 SDK" (nur wenn installiert ist) "Microsoft Visual Studio Codename Orcas Remote Debugger" (hatte ich nicht installiert) "Microsoft Visual Studio 64bit Prerequisites Beta" (64-bit platforms only) "Microsoft .NET Framework 3.5 Pre-Release Version" Und dann natürlich nicht zu vergessen "Microsoft Visual Studio 2008 Beta 2" selbst Danach konnte ich ohne Probleme den Final Release von Visual Studio 2008 installieren. Wer Visual Studio 2008 einfach mal probieren möchte kann unter dem nachfolgenden Link eine Testversionen (zeitlich begrenzte Versionen - 90 Tage) herunterladen. Momentan noch "nur in Englisch". http://msdn2.microsoft.com/en-us/vstudio/products/aa700831.aspx Ich habe übrigens das Problem welches ich in meinem Blog Beitrag beschrieben habe, auch noch nachdem ich die Beta Version deinstalliert und die Final Release Version installiert habe, immer noch. Ich werde diesem Problem somit noch mal meine ganze Aufmerksamkeit widmen und VS2008 in den nächsten Tagen auf einigen Testrechnern unter verschiedenen Systemumgebungen installieren. Mal sehen ob ich damit das Problem und meine Vermutung bestätigen oder widerlegen kann. Ich werde auf jeden Fall über die Ergebnisse berichten.
Programmierung | Visual Studio
Nachdem ich bereits vor einiger Zeit auf meiner Workstation die mit einem englischen Windows XP ausgestattet ist die Visual Studio Beta 2 (VS2008) ohne Probleme installieren und auch verwenden konnten, habe ich vor einigen Tagen zum weiteren Test, VS2008 Beta 2 noch zusätzlich auf einem Notebook (Windows XP in Deutsch) installiert. Die Installation verlief dabei auch ohne Probleme, danach konnte ich auch VS2008 ohne Probleme starten und ein Projekt, egal welches erstellen und auch speichern. Wenn ich nun aber F5 zum ausführen/starten des Projektes betätigt habe, wurde kurz mit dem Übersetzen des Projektes gestartet und dann hat sich das ganze Visual Studio einfach ohne weitere Meldung verabschiedet (geschlossen). Gerade so, als hätte man Beenden ausgewählt. Nachdem ich mir das etwas näher angeschaut habe ist mir aufgefallen, dass das Problem nicht an der Deutschen Installation liegt, sondern daran dass VS2008 Beta 2 ein Problem damit hat, wenn der Pfad/Dateiname des Projektes mehr als 64 Zeichen lang ist. Das wiederum ist auf einer Deutschen Maschine in der Standard-Installation leicht der Fall, wie im folgenden Beispiel zu sehen ist: C:\Dokumente und Einstellungen\Testuser.TESTDOMAIN\Eigene Dateien\Visual Studio 2008\WindowsFormsApplication1\WindowsFormsApplication1.sln. Also einfach darauf achten, dass die Pfade für die Projekte nicht zu lang werden. Zum Beispiel C:\Projekte\...
Programmierung | Visual Studio | Tips und Tricks
Der Titel dieses Beitrages sollte eigentlich besser lauten: EventType clr20r3 - oder starte niemals einen Windows Dienst ohne eine globale Fehlerbehandlung. Ich hatte einen neuen Windows Dienst geschrieben, der wie schon so viele Dienste vorher die Aufgabe zur Systemüberwachung auf eingehende Interfacedateien per FTP durchführen sollte. Der Dienst bezieht aus einer SQL Datenbank die Informationen für welche Mandanten dieser Dienst zu verrichten ist und überwacht dann die ebenfalls in der SQL angegebenen FTP Verzeichnisse auf eingehende neue Daten. Sind neue Daten vorhanden lädt der Dienst diese Dateien herunter, verarbeitet diese und löscht dann wenn alles erfolgreich war die Daten auf dem FTP Server. So ungefähr in dieser Art habe ich schon unzählige Dienste erstellt. Auch dieser Dienst, der in der Entwicklungsumgebung einwandfrei funktioniert, macht nach der Installation auf dem W2K3 Server des Kunden Probleme. Ich starte den Dienst, es dauert ein paar Sekunden und es kommt die Meldung, dass der Dienst nicht gestartet werden konnte. Also schaue ich im Ereignisprotokoll nach und stoße auf eben diese Meldung : Ich habe eine ähnliche Fehlermeldung schon öfter gehabt, und die hat eigentlich immer auf falschen Einstellungen in der ´Konfigurationsdatei hingedeutet. Aber warm muss ich eigentlich im trüben Wasser fischen, wieso gibt mir das System keine ordentliche Fehlermeldung geben. Da kan doch nur Bill Gates (Ich meine Microsoft) dran schuld sein, haben wohl wieder mal vergessen ordentliche Fehlermeldungen einzubauen. Andere werden doch das gleiche Problem haben, was machen die denn gegen eine solche Abstrakte Fehlermeldung, was fängt man damit an. Also einfach mal nach EventType clr20r3 gegoogelt (oh man was für ein Wort ). Die Suchergebnisse sind weniger als zufriedenstellend. Das einzige was sicher zu sein scheint, der Fehler tritt wohl immer mit Windows Diensten auf, nicht auf allen Maschinen (was sich später als Logisch herausstellt) und er tritt auf Systemen mit dem NET Framework 2 auf. Das alles ist und war mir einfach zu wenig und da ich bereits einige male über einen Fehler dieser Art gestolpert war, habe ich mir dieses mal die Zeit genommen, dass ganze mal etwas mehr Global zu betrachten und nicht nur den speziellen Fehler in diesem Fall im trüben Wasser zu suchen (und hoffentlich auch zu finden). Ich möchte auch jetzt nicht den ganzen Weg beschreiben wie ich hier vorgegangen bin und was ich alles getestet habe, da man ja einen Service der im Start nicht funktioniert nicht einfach so Debuggen kann. Die Essenz meiner Untersuchungen haben folgendes Ergeben: Diese Blöde Fehlermeldung wird vom NET Framework ausgegeben wenn keine ordentlich Exception innerhalb des beim Start ausgeführten Codes enthalten ist. Dabei kann der Fehler eine Fehlende Datei, die falsche oder fehlende Konfiguration, ein Problem mit einem verwendet Port oder wie es in meinem Fall war, einfach das nicht Vorhandensein einer Tabelle in der DB sein. Ja ich hatte einfach vergessen, nach der Installation des Dienstes auch das SQL Skript zur Erstellung des Tabellen und Gespeicherten Prozeduren auszuführen. Und was macht man, damit man nicht mehr diese nichtssagende Fehlermeldung bekommt, sondern etwas mit dem man dann auch etwas anfangen kann: Lösung: Man muss in der ersten, was auch immer, die durch die OnStart() Methode aufgerufen wird unbedingt darauf achten dass man dort eine try - catch Anweisung einbaut, die dann eine ordentlich Fehlermeldung erzeugt und am besten auch in das Ereignisprotokoll schreibt.
Programmierung | Tips und Tricks | Windows Dienste
Im Zusammenhang mit einem DotNetNuke Update (Version 3.1.1 --> Version 4.5.3) welches ich für einen Kunden durchführe ist folgendes Problem aufgetaucht: In der DotNetNuke Installation 3.1.1 war unter anderem das FAQ Module von SpohnSoftware in der Version 1.06.00 eingesetzt. Während des Update des DotNetNuke Portals auf die Version 4.5.3 traten auch keine merklichen Probleme mit diesem Modul auf. Selbst bei den ersten Tests (es handelt sich um ein umfangreiches Portal) wurden zuerst noch keine Probleme festgestellt. Dann aber stellte man fest, dass beim Versuch durch anklicken der Frage die Antwort zu öffnen nur eine Meldung "Fehler auf dieser Seite" vom Browser ausgegeben wurde, aber der Antwort nicht angezeigt wurde. Nach einer kurzen Recherche im Internet war klar, es gibt eine neue Version (mittlerweile die Version 03.00.00) die auch laut Angaben des Herstellers für DotNetNuke 4.X funktioniert. Also neue Version des Moduls gekauft (wie sich kurz drauf herausstellen sollte, Gott Sei Dank gleich die Source Version) und auf einem Test-Portal installiert. - Frage und Antwort eingegeben
- Seite aufgerufen
- Auf die Frage geklickt um die Antwort anzuzeigen
Und der Browser zeigt in der Statuszeile an: Fehler auf dieser Seite Wie bereits erwähnt, ich hatte die Source Version gekauft. Also das Modul in einem Entwicklungsportal installiert und die Source in das Projekt integriert. Fehlersuche......! Nach einigen Minuten war klar, das Problem liegt daran dass beim Aufruf von Java Script Funktionen kein gültiges Objekt der Seite an diese als Parameter übergeben wurden, aber warum. Aber auch diese Frage war nach kurzer Zeit geklärt: SpohnSoftware hat in seinem Modul eine Funktion GetTableName die dazu verwendet wird den durch das APS.NET Framework generierten Namen für das Control zu ermitteln. Die Funktion sieht so aus: Public Function GetTableName() As String
Try
Return Replace(UniqueID & "_tblQA", ":", "_")
Catch ex As Exception
ProcessModuleLoadException(Me, ex)
End Try
End Function
Wenn wir uns nun den Replace anschauen sehen wir auch sehr schnell das eigentliche Problem:
Die UniqueID enthält den eindeutigen Namen, welcher vom NET Framework erzeugt wurde. Das NET Framework 2.0 verwendet hierbei zur Trennung zwischen den einzelnen Namen das $ Zeichen und nicht das : Zeichen.
Das kann nicht gehen und ich frage mich wie das Modul jemals funktioniert haben soll.
Hier nun die Funktion wie sie richtig ist und auch funktioniert: Public Function GetTableName() As String
Try
Return Replace(UniqueID & "_tblQA", "$", "_")
Catch ex As Exception
ProcessModuleLoadException(Me, ex)
End Try
End Function
Ich habe hierzu auch sehr intensiv im Internet recherchiert, konnte aber keine Erklärung finden, warum das jemals so funktioniert haben soll.
Mal sehen, eventuell gibt es ja jemand der meinen Artikel liest und mir darauf eine Antwort geben kann.
DotNetNuke | Module | Programmierung | Code
Da war ich mir doch sicher, in den letzten Jahren, bereits mehrfach über eines der unglaublichsten Tools den .NET Reflector von Lutz Roeder geschrieben zu haben, und muss gerade, als ich auf die vorherigen Berichte verweisen will, feststellen, da gibt es ja noch gar keine Berichte! Ob das am Alter liegt 
Aber gut kommen wir zum eigentlich Grund dieses Beitrags, ich bin gerade auf die neue Seite für die .NET Reflector Add-Ins gestoßen.
Auf der Seite, die auf Codeplex gehostet ist, wird von Lutz Roeder persönlich die Veröffentlichung der Add-Ins koordiniert.
Und hier der Link zur .Net Reflector Add-Ins Homepage auf Codeplex
Programmierung | Tools
Ein Fehlermeldung wie die nachfolgende: Fehler 1 Ungültige Resx-Datei. Der angeforderte Wert Ctrl konnte nicht gefunden werden. Zeile XXX Position Y. Deutet darauf hin, dass man versucht mit der Deutschen Version von Visual Studio 2005 ein Projekt, welches mit einer englischen Version der Entwicklungsumgebung erstellt wurde, zu übersetzen versucht. Der Fehler tritt auf, wenn in den Resource Dateien von Windows Forms, Tastenkürzel (Shortcuts) zur Schnellanwahl von Menüfunktionen vergeben sind. In den Resource Dateien steht dann so etwas wie: <data name="editDeleteToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>Ctrl+D</value>
</data>
Das Ziel dieses Eintrags ist es mit der Tastenkombination <Steuerung>+D das Edit Menü aufzurufen.
Im englischen wäre der Eintrag auch korrekt.
Im Deutschen jedoch muss jedoch für Ctrl, was für Control - dem englischen Ausdruck für Steuerung steht, die Deutsche Abkürzung Strg verwendet werden.
Der Eintrag muss also für die Verwendung in einer Deutschen Visual Studio Version wie folgt aussehen: <data name="editDeleteToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>Strg+D</value>
</data>
Programmierung | Tips und Tricks
Heute erst entdeckt. "Perpetual Motion Interactive Systems" hat einen Module Upgrade Wizard erstellt und veröffentlicht. Die Veröffentlichung des Moduls hat zeitgleich mit dem letzten Release Version 4.6.0 stattgefunden. Der Grund warum ich und bestimmt auch viele andere dieses Modul nicht sofort entdeckt haben liegt wohl in der Tatsache begründet, das der Link zum Download nicht in der Kategorie der neuen Version (4.6.0) hinterlegt wurde, sondern in die Kategorie der Version 4.5.5 eingefügt wurde. Um die Verwirrung komplett zu machen ist in dieser Kategorie auch ein neuer Download Link für eine Dokumentation (4.5.5) leider gibt es diese Dokumentation aber nicht, und man bekommt die "alte" 4.4.1 Dokumentation, was man sich dann sparen kann. Nun aber eine erste kurze Information zum Module Upgrade Wizard. Der Wizard soll es Entwicklern ermöglichen Ihre Module, welchem mit VS2003 unter dem NET Framework 1.1 entwickelt wurden, auf das NET 2 Framework zu migrieren. Um das positive vorweg zu nehmen, der Wizard funktioniert prinzipiell sowohl für Module die in VB als auch in C# entwickelt sind. Leider setzt er die Module in das neue Webseiten Modell von VS2005 um (OK der Vorteil ist das man diese Module auch mit dem kostenlosen Visual Web Developer bearbeiten kann), man hat aber keine Wahl (Option) das er das Modul in das Web-Anwendung's-Modell (wie in VS2003) umsetzt was ich für die Entwicklung von DotNetNuke Modulen vorziehe. Und mit dieser Meinung stehe ich nicht alleine da, alle mehr oder weniger professionellen Module siehe auch das DMX Modul arbeiten mit diesem Modell. Was meiner Meinung nach aber noch negativer ist, ist dass dieser Wizard ohne Source Code Daher kommt, obwohl er unter NET 2, vermutlich mit VS2005, entwickelt wurde. Da der Wizard ohne Source kommt kann man keine Anpassungen und Erweiterungen an dem Wizard vornehmen. Aber bildet euch selbst euer Urteil, den Download findet man nicht wie üblich auf Sorcefourge sondern nur wenn man auf DotNetNuke registriert ist auf der Download Seite. Hier der Link
DotNetNuke | Programmierung
Vermutlich ist das für die meisten nichts neues, aber ich habe nun schon einige male nach dem enrsprechenden Befehl gesucht und dabei Zeit vergeudet, aus diesem Grund mach ich nun für mich selbst diesen Blog Eintrag.
Hintergrund:
In einer Datenbank (MS-SQL) sind in einem Tabellenfeld Werte die gegen andere Werte ausgetauscht werden müssen.
Ein simples Update wie das hier: UPDATE [TABLENAME] SET [FIELDNAME] = NEWVALUE
Nun ist es aber so, dass ich nur einen Teil des Inhaltes ändern muss, im speziellen Fall wird in der Spalte der Tabelle Informationen über den Speicherort einer Datei vorgehalten.
Hier ein Beispielinhalt:
C:\Program Files\hMailServer\Data\ihrportal\AB\{AB88191E-9A94-4C08-90B8-597A4B2FA3E2}.eml
Nun befinden sich die Daten aber nicht mehr auf dem Laufwerk C: sondern die Daten befinden sich nun auf Laufwerk D: und dort in dem Verzeichnis hMailServer\Data also auf D:\hMailServer\Data. Somit muss der Inhalt des Feldes wie folgt geändert werden:
D:\hMailServer\Data\ihrportal\AB\{AB88191E-9A94-4C08-90B8-597A4B2FA3E2}.eml
Dies muss nun bei tausenden von Einträgen durchgeführt werden.
Lösung:
Um nun die Daten in der Spalte messagefilename der Tabelle hm_messages zu ändern habe ich folgedes Query ausgeführt: update hm_messages
set messagefilename = REPLACE(messagefilename,'C:\Program Files\hMailServer\Data','D:\hMailServer\Data')
So und nun hoffe ich das ich das nächste mal wenn ich so etwas machen möchte, daran denke dass ich dazu einen Blog Eintrag verfasst habe.
Programmierung | SQL
Immer wieder geschieht es dass ich während der Programmierung über den zu verwendenden Zugriffsmodifizierer für Klassen und Methoden nachdenken muss (manchmal länger als ich eigentlich möchte ). Aus diesem Grund mach ich mir hier wieder mal eine kleine Gedächtnisstütze in Form dieses Blog Eintrags.
Hier nun eine kleine Tabelle in welcher man einfach den benötigten Zugriffsmodifizierer ablesen kann:
| public |
Ja |
Ja |
Ja |
Ja |
Ja |
| protected internal |
- |
Ja |
Ja |
Ja |
Ja |
| internal |
- |
Ja |
Ja |
- |
Ja |
| protected |
- |
- |
Ja |
Ja |
Ja |
| private |
- |
- |
- |
- |
Ja |
Programmierung | Tips und Tricks | Sprachen | C#
Auch dieser Blog Eintrag ist wieder so etwas wie ein Post It für mich selbst, Ich denke aber das bestimmt der eine oder andere genau das Problem hat und schon mit diesem kleinen Hinweis weiter kommt.
Hintergrund:
Bei der Verwendung von CodeSmith und den netTiers 2.0 Templates ist es möglich mit Transaktionen zu arbeiten. In diesem Blog Eintrag wird kurz beschrieben wie man diese unter C# und im Domain Komponenten Modell einsetzen kann.
Lösung (c#): if (ConnectionScope.Current.TransactionManager == null)
ConnectionScope.Current.TransactionManager = ConnectionScope.CreateTransaction();
TransactionManager tm = ConnectionScope.Current.TransactionManager;
if (!tm.IsOpen)
{
tm.BeginTransaction();
}
// Hier nun alle notwendigen Datenbankoperationen
if (allesOK)
tm.Commit();
else
tm.Rollback();
Mehr ist nicht zu berücksichtigen !!
Programmierung | C# | Tips und Tricks
Jetzt reicht es und ich mache mir selbst ein Geschenk indem ich nun endlich dieses Problem einmal in einem Blog Eintrag festhalte.
Hintergrund:
Immer wieder stehe ich vor der Aufgabe das ich von irgendwoher Daten aus einer Datei verarbeiten (meistens Importfunktionen) muss, die unter anderem auch Datums und oder Zeitwerte enthalten.
Die Werte werden in den verschiedensten Formaten übergeben, eine der beliebtesten Formate ist es aber dass beideInformationen (Datum und Uhrzeit) in zwei verschiedenen Felder übergeben werden. Dabei wird häufig (jedenfalls ist das bei mir so) das nachfolgende Format für die Speicherung der Werte verwendet:
Datum als String z.B. : 20070815 also im Format yyyyMMdd
Uhrzeit als String z.B. : 120310 also im Forma HHmmss
Nun besteht die Aufgabe darin diese Daten zu lesen und dann in ein DateTime Objekt zu konvertieren.
Lösung:
Nachfolgend nun die von mir favorisierte und schon dutzende male verwendet Lösung: // Source ist in C#
string dateStr = s[2]; // Hier steht einfach ein Datumswert wie 20070815 drin
string timeStr = s[3]; // Hier steht eine Uhrzeit wie 120310 drin
string dateTimeStr = dateStr + " " + timeStr; //Da steht nun 20070815 120310 also Datum mit Uhrzeit drin
string dateTimeStrFormat = "yyyyMMdd HHmmss"; // Genau hier das ist das Format
// Und hier wird nun der zusammen gebastelte Datum / Uhrzeit String in ein DateTime Objekt konvertiert
objInfo.TransDate = DateTime.ParseExact(dateTimeStr, dateTimeStrFormat, DateTimeFormatInfo.InvariantInfo);
So und nun hoffe ich das ich beim nächsten mal nicht wieder nach diesem Stückchen Quellcode suche wenn ich mal wieder vor der Aufgabe stehe, und eventuell stolpert ja auch der eine oder andere über diese Information wenn er / sie mal so etwas machen möchte.
Programmierung | Code | Tips und Tricks | C#
Ich weiß nicht wie oft ich schon da stand, und mir mal wieder die Frage gestellt habe, wie unter c# das äquivalent für die in vb verfügbare Funktion isnumeric lautet.
Und immer wieder benötige ich einiges an Zeit um wieder mal herauszufinden dass es kein direktes äquivalent gibt, aber es gibt halt doch eine ganz einfache Lösung.
Man implementiert sich eine solche Funktion mal schnell selbst. Es gibt dazu eine ganze Reihe von ansätzen, so nach dem Motto; viele Wege führen nach Rom.
Ich möchte nun hier meinen persönlichen Favorit dokumentieren, so dass ich Ihn nicht wieder vergesse, ganz nach dem Motto, was du mal geschrieben hast, vergisst du nicht mehr so schnell.
Und hier der Ersatz für die aus vb bekannte isnumeric Funktion:
- public static bool IsNumeric(object Expression)
- {
- bool isNum;
- double retNum;
- isNum = Double.TryParse(Convert.ToString(Expression), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
- return isNum;
- }
public static bool IsNumeric(object Expression)
{
bool isNum;
double retNum;
isNum = Double.TryParse(Convert.ToString(Expression), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
return isNum;
}
Programmierung | Code | C#
Nachdem ich selbst das Problem am Anfang hatte und auch immer wieder von Freunden, Bekannten und Kollegen auf das Thema Debuggen von Web Services unter Visual Studion 2005 angesprochen werde, hier nun ein kleiner Blog Eintrag der beschreibt wie man dabei am besten vorgeht.
Hier zuerst noch einmal das Szenario:
Ich möchte einen Web Service erstellen und während der Entwicklung auf einfachem Wege in der Lage sein die einzelnen Web Methoden debuggen zu können.
Also einfach ein zweites Projekt (Windows Form) in der Projektmappe erstellt, referenz auf den Web Service in der Projektmappe hinzufügen, als Startprojekt festlegen, und fertig.
Anmerkung zum Szenario:
So sollte es gehen, leider geht es aber "noch" nicht"
Und hier nun die Lösung:
Zuerst einmal genau so vorgehen wie es im Szenario beschrieben ist und dann folgende Einstellungen zusätzlich vornehmen:
In den Projektmappen Eigenschaften in den Allgemeinen Eigenschaften Startprojekt auswählen.
Dort die Option mehrere Startobjekte auswählen und bei den beiden Projekten (der Web Service und das Windows Forms Projekt) die Aktion auf Starten stellen.
Das ganze noch kurz mit OK bestätigen und fertig.
Wenn ich nun in der Windows Form eine Web Methode verwende, kann ich innerhalb der web methode einfach mit F9 einen Haltepunkt (Breakpoint) setzen.
Hoffe das die (schnelle und kurze) Beschreibung vielen Helfen wird.
Programmierung | Tips und Tricks | Web Services
Nachdem ich in einem Projekt mit dem Problem kämpfen musste, das einer der für dieses Projekt entwickelten Windows Dienste durch eine aufwendige Initialisierung bis zu einer Minute in der OnStart Methode verweilt hat bis er endlich den Dienst als gestartet anzeigt habe ich mir überlegt wie man diese Problem anders lösen könnte.
Eigentlich war das auch kein wirklich großes Problem, aber da ich auch erst einmal gezielt darüber Nachdenken musste um auf die Lösung zu kommen, denke ich dass vielleicht auch andere erst einmal auf das Problem aufmerksam werden müssen um dann einen anderen Ansatz zum Start der Windows Dienste zu verwenden.
Im Nachfolgenden Beschreibe ich eine einfach Lösung wie man einen Windows Dienst mit Hilfe eines Timers dazu bringt, sofort zu starten, unabhängig davon, ob aufwendige Initialisierungsprozesse für den Dienst durchgeführt werden müssen oder nicht.
Schauen wir und doch einfach mal wie der normale Startvorgang eines Dienstes aussieht:
Hier ein Beispiel wie es normalerweise aussehen kann, wobei osc irgendeine Klasse ist deren Methoden nach deren Initialisierung vom Service verwendet werden: using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Timers;
namespace Namespace.Service
{
publicpartialclassMeinService : ServiceBase
{
MeineKlasse osc;
public MeinService()
{
InitializeComponent();
Osc = new Osc();
}
protectedoverridevoid OnStart(string[] args)
{
Osc.Init();
Osc.StartAll();
}
protectedoverridevoid OnStop()
{
osc.StopAll();
}
protectedoverridevoid OnContinue()
{
osc.StartAll();
}
protectedoverridevoid OnPause()
{
osc.StopAll();
}
protectedoverridevoid OnShutdown()
{
osc.StopAll();
osc.Dispose();
osc = null;
}
}
Wenn nun davon ausgehen dass Osc.Init() und Osc.StartAll() Methoden sind die lange dauern können, dann befindet sich dieser Dienst in der Gesamt Zeit in welcher diese beiden Methoden ausgeführt werden im Dienst- Status StartPending. Dies kann in bestimmten Situationen aber mehr als unerwünscht sein. Und für diesen Fall habe ich folgende Änderungen vorgenommen, die dazu führen, dass der Service sofort nach dem Start wirklich in den Status Running übergeht.
Nachfolgend nun die Lösung für das Problem: using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Timers ;
namespace Namespace.Service
{
publicpartialclassMeinService : ServiceBase
{
Timer startTimer = newTimer();
MeineKlasse osc;
public MeinService()
{
InitializeComponent();
startTimer.Interval = 1000; // 1 seconds
startTimer.Elapsed += newElapsedEventHandler(startTimer_Elapsed);
startTimer.Enabled = false;
}
void startTimer_Elapsed(object sender, ElapsedEventArgs e)
{
startTimer.Enabled = false;
osc = MeineKlasse.loadActiveSystems();
osc.StartAll();
}
protectedoverridevoid OnStart(string[] args)
{
startTimer.Enabled = true;
}
protectedoverridevoid OnStop()
{
osc.StopAll();
}
protectedoverridevoid OnContinue()
{
osc.StartAll();
}
protectedoverridevoid OnPause()
{
osc.StopAll();
}
protectedoverridevoid OnShutdown()
{
osc.StopAll();
osc.Dispose();
osc = null;
}
}
}
Ich habe die Änderungen in Fettschrift dargestellt damit man diese gleich auf Anhieb erkennen kann. Und die Erklärung ist eigentlich auch selbstredend. Ich verwende also einen Timer, der vom OnStart Event eine Sekunde nach dem der Service gestartet wurde die eigentlichen Funktionen des Service aktiviert.
Manchmal ist einfach, einfach Einfach !
Programmierung | C# | Windows Dienste
Um in einer App.config Datei Enum Werte wieder in eine Variable des Augzählungstypes einzulesen kann folgende Funktion verwendet werden.
Enum.Parse()
Beispiel:
In diesem Beispiel lesen wir den NotifyFilter eines FileSystemWatcher ein:
Der Key in der App.config sieht wie folgt aus: <add key="fdwNotiFyFilter" value="FileName, DirectoryName, LastWrite" />
Und hier die Vewendung: fdw.NotifyFilter = (NotifyFilters)Enum.Parse(typeof(NotifyFilters), ConfigurationSettings.AppSettings["NotifyFilter"]);
Programmierung | Tips und Tricks | C#
|
Copyright © 2010 Hans-Peter Schelian - Schelian IT Beratung. All rights reserved.
|
|