Sep 092009
 

Bei einer Anwendung werden Mitarbeiter via einer Datei importiert. Namen und Vornamen können durchaus Umlaute enthalten.

Ursprünglich wurden die Namen mit korrekten Umlauten exportiert. Irgend wann stellte der Exporteur aber das Encoding um. Danach waren die Umlaute nicht mehr richtig in der Exportdatei. Das sah wie folgt aus.

103129,Roland,Meier,Gnter H”ller

oder

"000000000  31 30 33 31 32 39 2C 52-6F 6C 61 6E 64 2C 4D 65   |103129,Roland,Me|"
"000000010  69 65 72 2C 47 81 6E 74-65 72 20 48 94 6C 6C 65   |ier,Gnter H”lle|"
"000000020  72                                                                         |r               |"

Jetzt wollte aber die Importroutine natürlich die Umlaute auch nicht mehr richtig importieren. Das führte unweigerlich zu Problemen.

using (StreamReader sr = new StreamReader(file, Encoding.Default))
{
	string row = sr.ReadLine();

	while (sr.Peek() >= 0)
	{
		row = sr.ReadLine();
		string[] fields = row.Split(',');

Mit diesem Code wurden die Umlaute nicht richtig importiert. Bei Encoding.XXX habe ich alle durchprobiert. UTF7, UTF8, UTF32, Default und alles Andere noch. Aber die Umlaute kamen noch immer falsch rein.

Irgend wie kam ich nicht auf die richtige Idee. Also fragte ich einfach mal bei CodeKicker.de nach. Nach kurzer Zeit hatte dort jemand die richtige Idee.

using (StreamReader sr = new StreamReader(file, Encoding.GetEncoding(437)))

Wenn der StreamReader mit diesem Encoding geöffnet wird klappt es wunderbar.

kick it on dotnet-kicks.de

.NET – Fehlermeldung und was sie nicht bedeuten

 Entwicklung  Kommentare deaktiviert für .NET – Fehlermeldung und was sie nicht bedeuten
Mrz 312009
 

Folgende Fehlermeldung hatte ich auf einem Liveserver.

Could not find a part of the path ‚\\advirtual40\catilz2-archiv‘.

Das passierte in einer Zeile wo ich prüfe ob das Verzeichnis existiert.

if (!Directory.Exists(folder))

Das habe ich dann auf meinem PC nachgebaut, aber hier funktionierte es. Konnte also nicht am Pfad selber liegen. Dachte zuerst an das ‘-‘ Zeichen. Aber das war es bestimmt nicht.

Ich habe dann dem Windows Service ein spezielles Konto zugewiesen. Gem. den Admins soll ich doch das verwenden. Und siehe da, danach wurden die Dateien gespeichert.

Die Fehlermeldung gibt also null Hinweis auf den tatsächlichen Fehler. Es lag nur daran das LocalSystem nicht genügend Rechte hatte um in das Verzeichnis zu schreiben.

Na ja, wieder mal was gelernt.

 Posted by at 10:53  Tagged with:

ADO.NET Entity Framework – DataReader schon offen

 Entwicklung  Kommentare deaktiviert für ADO.NET Entity Framework – DataReader schon offen
Mrz 312009
 

Ich experimentiere bei einem Projekt ein wenig mit ADO.NET Entity Framework rum. Einfach mal um zu sehen was damit geht und was nicht.

Auf meinem Client lief dieses Codestück hier ohne Probleme.

using (ArchiverEntities ae = new ArchiverEntities())
{
	int days = int.Parse(ConfigurationManager.AppSettings["BACKDAY"]);
	try
	{
		DateTime startDate = DateTime.Now.AddDays(days);
		startDate = new DateTime(startDate.Year, 
                startDate.Month, startDate.Day, 23, 59, 59);
		var interviews = ae.Interviews.Where(i => (i.StartDate <= startDate));
		foreach (var inter in interviews)
		{
			if (!inter.InterviewerReference.IsLoaded)
			{ inter.InterviewerReference.Load(); }

			if (!inter.SupervisorReference.IsLoaded)
			{ inter.SupervisorReference.Load();}

			if (!inter.LabSupervisorReference.IsLoaded)
			{ inter.LabSupervisorReference.Load(); }
...

Auf dem Liveserver dann immer der selbe Fehler. Daten wurden selektiert, aber in Zeile 13 machte es bum.

There is already an open DataReader associated with this Command which must be closed first …

Wie so läuft das bei mir aber auf dem Server nicht? Nach einem Hinweis im Internet war mir dann so einiges klar.

Ich löschte im Connectionstring ein vermeintlich unnötiger Eintrag.

MultipleActiveResultSets=True

Das muss unbedingt drin sein. Danach funktionierte die Abfrage auch auf dem Server. 🙂

 Posted by at 9:50  Tagged with:
Mrz 172009
 

ASP.NET ist einfach noch nicht so meine Welt. Das muss ich immer mal wieder merken.

Gerade vorhin. Ich habe ein GridView an das ich ein Objekt binde. Das Objekt hat einige Eigenschaften vom Typ DateTime. Diese Eigenschaften können mit DateTime.MinValue vorbelegt sein. Das sieht dann aber ziemlich blöde aus im GridView.

image

Ich dachte es gibt sicher eine Eigenschaft wo man das beim GridView einstellen kann. Na ja, es gibt die nicht. Lösen kann man es wie folgt.

Aus dem Feld ein ItemTemplate machen und mit einem if prüfen ob es ein DateTime.MinValue ist. Sieht dann so aus.

<ItemTemplate>
    <asp:Label ID="Label1" 
    runat="server" 
    Text='<%# ((DateTime)Eval("DateFrom")) == DateTime.MinValue ? "--.--.----" : Eval("DateFrom",  "{0:d}") %>'>
    </asp:Label>
</ItemTemplate>

Ergebnis:

image

Tausend dank geht an Thomas.

Dictionary mit einem BinaryFormatter serialisieren

 Entwicklung  Kommentare deaktiviert für Dictionary mit einem BinaryFormatter serialisieren
Feb 192009
 

Eigentlich dachte ich, dass das kein Problem darstellen sollte. Folgende Klasse sollte mit einem BinaryFormatter serialisiert und deserialisieren werden.

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

[Serializable]
public class Content
{
	private string m_ID = "";
	public string ID
	{
		get { return m_ID; }
		set { m_ID = value; }
	}

	private string m_ContentText = "";
	public string ContentText
	{
		get { return m_ContentText; }
		set { m_ContentText = value; }
	}

	public Content(string id)
	{
		this.ID = id;
	}
}

[Serializable]
public class Contents : Dictionary<string, Content>
{
	public Contents() { }
}

Zum serialisieren und deserialisieren nutze ich folgende zwei Methoden.

private static string SerializeContent(Contents data)
	{
		byte[] bytes;
		using(MemoryStream ms = new MemoryStream())
		{
			BinaryFormatter bf = new BinaryFormatter();
			bf.Serialize(ms, data);
			bytes = ms.GetBuffer();
			return Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None);
		}
	}

	private static Contents DeserializeContent(string data)
	{
		byte[] bytes = Convert.FromBase64String(data);
		using(MemoryStream ms = new MemoryStream(bytes, 0, bytes.Length))
		{
			BinaryFormatter sf = new BinaryFormatter();
			object o = sf.Deserialize(ms);
			return (Contents)o;
		}
	}

Das Objekt Contents konnte ohne Probleme serialisiert werden. Wenn man dann aber das selbe Objekt wieder deserialisieren wollte kam es zu folgender Fehlermeldung:

Der für die Deserialisierung eines Objekts mit dem Typ \"Contents\" erforderliche Konstruktor wurde nicht gefunden.

Wie so konnte man jetzt ein Objekt serialisieren aber nicht mehr deserialisieren? Gute Frage. Lösung habe ich hier gefunden.

Ich musste also die Klasse Contents wie folgt anpassen.

[Serializable]
public class Contents : Dictionary<string, Content>
{
	public Contents() 
		: base() { }

	public Contents(SerializationInfo info, StreamingContext context)
		: base(info, context) { }
}

Und schon kann man ein Dictionary<TKey, TValue> deserialisieren.

 Posted by at 19:01  Tagged with:

C# – Custom TabControl

 Entwicklung, Software  Kommentare deaktiviert für C# – Custom TabControl
Dez 222008
 

OK, der Titel ist Bluff. 🙂

Um was geht es. Bislang sah im WordPlus die Task Pane in etwa so aus. Je nachdem welche Snap-In’s man gewählt hatte.

Alte WordPlus TaskPane

Die Daten, die in der Task Pane angezeigt werden, werden aus den Docproperty’s des Dokumentes generiert. Wenn jetzt aber ein Makro im Hintergrund ein Docproperty ändert schlägt sich das nicht in der Task Pane nieder. Das führt dazu, dass in der Task Pane etwas anderes angezeigt wird als in den Docproperty’s ist.

Das sollte mit einem TabControl gelöst werden. Bedingung, es muss gut aussehen. Aber man sage das einem Entwickler. Ich nehme das 0815 TabControl. Aber das führt zu einem Aufschrei bei den Verantwortlichen. Das sieht ja gar nicht schick aus. Also muss man sowas selber bauen.

Aussehen sollte es in etwa so.

So sollte es in etwa aussehen

Aber nur in etwa. Einfach nicht so langweilig wie das normale TabControl.

fertiges TabControl

Der rote Pfeil markiert jetzt mein Problem. Wie soll ich diese Linien sinnvoll Zeichnen ohne das ich irgend welche OnPaint oder OnPreRender überschreiben muss. Dazu fehlt mir schlicht die Zeit und das Zeitbudget. 🙂

Aber für solche Probleme gibt es ja versierte Kollegen. Man nehme einfach ein Label, stelle BorderStyle auf FixesSingle und setze die Höhe auf 1. Dan gibt es eine Linie. Man platziere sie richtig, stelle noch den Anchor richtig ein, und schon sieht es cool aus. Vor allem schneller als mit OnPaint oder so. Dieses Zweiertab besteht aus 11 Labels. 🙂

TabControl im Designer

Jetzt kann man einfach noch das richtige Label ausschalten und schon hat man die “Lücke”.

Wer eine bessere Idee hat darf sich melden. Das ging schnell und einfach. Aber … Na ja.

Ob die Lösung ins Finale WordPlus kommt ist noch nicht klar. Wird sich noch zeigen.

Office XP – deploy Add-in – Wie registriert man die Extensibility.dll

 Entwicklung, Software  Kommentare deaktiviert für Office XP – deploy Add-in – Wie registriert man die Extensibility.dll
Nov 282008
 

Wer suched der findet. Bei mir hat es jetzt sehr, sehr lange gedauert.

Ich habe ein Office XP Add-in das auf diversen Systemen mit verschiedenen Konfigurationen installiert werden muss. Ein Add-in für Office XP/2002 muss die Schnittstelle Extensibility.IDTExtensibility2 implementieren. Diese ist in der Extensibilty.dll definiert.

Auf einem XP mit SP2 und Office XP/2002 ohne SP ist die DLL aber nicht registriert, dass heisst nicht im GAC.

Auf dem Entwicklungsclient habe ich die DLL natürlich. Also, ich nehme die dll, kopiere sie auf das Zielsystem und versuche das Teil mit RegAsm.exe zu registrieren. Das führt zu einer Fehlermeldung in dieser form.

RegAsm : error RA0000 : Could not load file or assembly ‚Extensibility, Version= 7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a‘ or one of

Das Teil will einfach nicht in den GAC.

Wie bin ich nun vorgegangen um das Add-in auf einem frisch aufgesetzten System an den Start zu bringen.

  1. Man mache ein Setup. Das Setup mache ich NUR weil ich nicht weiss was alles in die Registry muss.
    Die Primäre Ausgabe wird bei Register auf vsdrpCOM gestellt.
    image
  2. Bei den Abhängigkeiten genau das selbe. Wenn ich die Office XP/2002 PIA Abhängigkeiten aus dem Setup ausschliesse, und sie deshalb nicht registriert werden, läuft das Add-in nicht mehr. Aber eigentlich müssten die Office XP/2002 PIA’s ja schon bei der Installation der Office XP/2002 PIA’s registriert worden sein.
    image
  3. Wenn man schon dabei ist, dann kann man auch gleich noch drei Registry Key importieren. Für jedes Office Programm das ein Add-in hat ein Schlüssel.

    Windows Registry Editor Version 5.00

    [HKEY_CURRENT_USER\Software\Microsoft\Office\Word\Addins\namespace.namespace]
    "Description"="Bschreibung"
    "FriendlyName"="Angezeigter Name des AddIns im Office"
    "LoadBehavior"=dword:00000003

    Die rot markierten Textteile müssen dann noch passend ersetzt werden.

  4. Das Add-in hat einen sehr einfachen Code.
  5. using System;
    using System.Text;
    using System.Reflection;
    using Extensibility;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace LAG.WordPlus.OfficeAddIns.Word2002
    {
    	public class Word2002 : Object, Extensibility.IDTExtensibility2
    	{
    		#region IDTExtensibility2 Member
    
    		public void OnAddInsUpdate(ref Array custom)
    		{
    		}
    
    		public void OnBeginShutdown(ref Array custom)
    		{
    
    		}
    
    		public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
    		{
    			MessageBox.Show("OnConnection -- Hallo :-)");
    		}
    
    		public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
    		{
    			MessageBox.Show("OnDisconnection -- ByBy :-)");
    		}
    
    		public void OnStartupComplete(ref Array custom)
    		{
    			MessageBox.Show("OnStartupComplete -- TippTopp :-)");
    		}
    		#endregion
    	}
    }
  6. Dann besorgt man sich folgenden VS2005-KB908002-ENU-x86.EXE fix. Die Thematik des KB interessiert nicht wirklich. Dort drin gibt es eine ganz bestimmtes MSI Packet.
  7. Man führt die EXE aus. Im Ordner C:\Programme\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\KB908002 findet man nun die Datei extensibilityMSM.msi. Und die ist für dieses Problem Gold wert. Ich habe leider keinen anderen Weg gefunden wie man an das MSI kommt.
  8. Auf dem Zielsystem führt man nun das extensibilityMSM.msi aus und anschliessend noch das eigene Setup. 

Danach müsste ein Dialog erscheinen der etwa so aussieht.

image

Es kann aber noch 1000 andere Gründe geben.

– Sind die nötigen .NET Versionen installiert?

– Sind die Office CP/2002 PIA’s installiert?

Jedenfalls habe ich jetzt endlich einen Weg gefunden wie ich das Add-in lauffähig bringe.

VSTO – Probleme mit Zeilenumbrüchen

 Entwicklung  Kommentare deaktiviert für VSTO – Probleme mit Zeilenumbrüchen
Nov 182008
 

Es kann durchaus sein, dass in einer Anwendung (WordPlus) ein solcher Dialog auftaucht.

image

Dargestellt werden die eingegebenen Daten so:

image

Hier wird also ein Zeilenumbruch eingefügt. Das macht man in der Regel mit Environment.NewLine;.

Diese Adresse wird jetzt dem Word oder Excel übergeben. Das kann dann so aussehen.

image

Was mich jetzt aber am meisten erstaunt ist die Tatsache, dass es bei einem DOCPROPERTY richtig aussieht und bei einer DOCVARIABLE nicht. Dort gibt es für den Zeilenumbruch ganz spezielle Sonderzeichen.

image

Das selbe kenne ich noch von gewissen Feldern im Excel. Wenn das auftritt, dann macht man einfach ein Replace("\r\n", "\r"); auf den String den man nutzen will. Und schon sieht es wieder normal aus.

image

Was Microsoft hier wohl überlegt hat?

Okt 282008
 

Ich habe heute, nicht das erste Mal, gelesen, dass C# 4.0 optionale Parameter haben soll. Ich kenne das von VB6 her. Ich habe es dort nie schätzen gelernt. Von mir aus machte es mehr Probleme als es löste.

Das man bei der Office Programmierung die optionalen mit der Klasse Missing füllen muss, dass stört mich gar nicht. Für mich persönlich ist es ein Schritt zurück.

Ein Vorschlag an Anders Hejlsberg. Nimm als Gegenleistung das goto aus dem C# Syntax raus. 🙂

Die restlichen neuen Features finde ich gut.

Ach ja. Dann gab es beim Heise Newsticker zu dem Thema einen Kommentar den ich irgend wie finde.

Kein Wunder, dass .NET immer bedeutungsloser wird.

Ich glaube, da lebt jemand zu 100% in der Java Welt.

 Posted by at 12:40  Tagged with:

VS – Unterschied zwischen F5 und CTRL+F5, oder wie finde ich den Fehler

 Allerlei, Entwicklung, Software  Kommentare deaktiviert für VS – Unterschied zwischen F5 und CTRL+F5, oder wie finde ich den Fehler
Okt 102008
 

Das hat jetzt einiges an Nerven gekostet. Eigentlich sollte ich nur ein Release-Build machen und das einchecken. Aus lauter gewohnheit habe ich dann mal die EXE-Datei gestartet und musste feststellen das die Anwendung nicht wollte. Wie so nicht? Na ja, dass galt es nun rauszufinden.

Also stellte ich die Konfiguration auf Debug und drückte F5. Die Anwendung startete ohne Fehler. Ein Doppelklick auf die EXE oder ein CTRL+F5, die Anwendung wollte nicht. 🙁

Zuerst prüfte ich dann mal alle Verzeichnisse auf einen Schreibschutz. Es war nichts schreibgeschützt.

Da ich nur eine allgemeine Fehlermeldung bekam, die leider nichts sage wo was passierte, musste ich mich mühsam durchsuchen bis ich dort war wo es bum machte. Ich konnte ja nicht debuggen. Mit der MessageBox.Show(“Hallo x”) schaute ich wo es noch tat.

Nach einiger Zeit landete ich hier.

protected override DbConnection CreateConnection(string connectionString)
{
	return new SqlConnection(connectionString);
}

Jetzt konnte ich alles in ein try{} catch() Block packen und schauen was genau der Fehler war.

System.TypeInitializationException: Der Typeninitialisierer für

"System.Data.SqlClient.SqlConnection" hat eine Ausnahme verursacht.

—> System.TypeInitializationException: Der Typeninitialisierer für

"System.Data.SqlClient.SqlConnectionFactory" hat eine Ausnahme verursacht.

—> System.TypeInitializationException: Der Typeninitialisierer für

"System.Data.SqlClient.SqlPerformanceCounters" hat eine Ausnahme

verursacht.

—> System.Configuration.ConfigurationErrorsException: Das

Konfigurationssystem konnte nicht initialisiert werden.

—> System.Configuration.ConfigurationErrorsException: Unbekannter

Konfigurationsabschnitt "lag.wordplus". (P:\ProjectX\WP.config line 8)

   bei System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean

ignoreLocal)

   bei

System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors

schemaErrors)

   bei System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()

   bei System.Configuration.ClientConfigurationSystem.EnsureInit(String

configKey)

   — Ende der internen Ausnahmestapelüberwachung —

   bei System.Configuration.ConfigurationManager.PrepareConfigSystem()

   bei System.Configuration.ConfigurationManager.GetSection(String

sectionName)

   bei

System.Configuration.PrivilegedConfigurationManager.GetSection(String

sectionName)

   bei System.Windows.Forms.WindowsFormsSection.GetSection()

   — Ende der internen Ausnahmestapelüberwachung —

   bei System.Data.SqlClient.SqlConnectionFactory..cctor()

   — Ende der internen Ausnahmestapelüberwachung —

   bei System.Data.SqlClient.SqlConnection..cctor()

   — Ende der internen Ausnahmestapelüberwachung —

   bei System.Data.SqlClient.SqlConnection..ctor()

   bei System.Data.SqlClient.SqlConnection..ctor(String connectionString)

   bei LAG.WordPlus.Database.SqlDatabase.CreateConnection(String

connectionString) in P:\ProjectX\SqlDatabase.cs:Zeile 23.

Scheinbar wurde der Konfigurationsabschnitt “lag.wordplus” nicht gefunden. Den gibt es aber. Ganz sicher.

Der Unterschied zwischen CTRL+F5 und F5:

CTRL+F5: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile == ANWENDUNGSNAME.config

F5: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile == ANWENDUNGSNAME.vshost.exe.config

Das brachte mich dann auf die Idee die App.config genauer anzuschauen.

Ich fand dort folgendes.

<!– <configSections>

    <section name="lag.wordplus"

type="LAG.WordPlus.Configuration.WordPlusSection, LAG.WordPlus" />

  </configSections> –>

Na ja, was soll ich sagen. <!– –> weg und es tut.

Und wieder um eine Erfahrung reicher. 🙂