Illegale Zeichen aus einem Dateinamen oder Pfad entfernen

 Entwicklung, Hilferuf und Lösung  Kommentare deaktiviert für Illegale Zeichen aus einem Dateinamen oder Pfad entfernen
Mrz 172011
 

Ein Problem das sich bei mir immer mal wieder auftut ist das erstellen einer Datei anhand irgend welcher Daten aus irgend welchen Quellen. Das kann bei einer Applikation ein Feld aus der Datenbank sein. Der Titel eines Buches mit dem man kurz eine Temporäre Datei erstellen will um sie als Attachement einem EMail anzuhängen. Aber um die Datei speichern zu können darf sie keine Sonderzeichen drin haben.

Wenn jetzt das Buch den Titel Mein <”Sonderzeichen”> Buch  hat wird das speichern schon schief gehen. Die “ und <> sind in Dateinamen und Pfadnamen nicht erlaubt.

Sofort baut man sich eine Methode die alle Sonderzeichen entfernt. Aber was sind den nun Sonderzeichen? Jetzt fängt die Suche im Internet an, in der Hoffnung eine Liste mit den ungültigen Zeichen zu finden. Seit dem .NET 2.0 Framework kann man dieses bemühen.

Path.GetInvalidFileNameChars() und Path.GetInvalidPathChars(). Schon muss man sich die Zeichen nicht mehr zusammensuchen.

Ich habe mir kurz mal eine Konsolen-Anwendung zur Demo gemacht. Man sieht hier zwei Möglichkeiten wie man die Sonderzeichen entfernen kann. Die erste Methode, die habe ich im Main auskommentiert (Zeile 19), macht es mit einer klassischen Methode die ab der Zeile 32 implementiert. Die Zweite, die aktive (Zeile 21), macht es mit einer Erweiterungsmethode die ab der Zeile 48 implementiert ist.

#region using

using System;
using System.IO;

#endregion

namespace IllegaleZeichen
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string folder = Path.GetTempPath();
            string fileName = "Mein <\"Sonderzeichen\"> Buch" + ".txt";

            Console.WriteLine(fileName);

            //string file = Path.Combine(folder, RemoveIlegaleCharackter(fileName));

            string file = Path.Combine(folder, fileName.RemoveIlegaleCharackter());

            using(StreamWriter sw = new StreamWriter(file))
            {
                sw.Write("Tut");
            }

            Console.WriteLine(file);
            Console.Read();
        }

        private static string RemoveIlegaleCharackter(string fileName)
        {
            string illegal = fileName;
            string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

            foreach(char c in invalid)
            {
                illegal = illegal.Replace(c.ToString(), "");
            }

            return illegal;
        }
    }

    public static class ExtMeth
    {
        public static string RemoveIlegaleCharackter(this string fileName)
        {
            string illegal = fileName;
            string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

            foreach(char c in invalid)
            {
                illegal = illegal.Replace(c.ToString(), "");
            }

            return illegal;
        }
    }
}

Die Datei wird dann mit dem Namen “Mein Sonderzeichen Buch.txt” im Temp-Verzeichnis erstellt.

Aug 162010
 

Wenn bei BlogEngine.NET eine Erweiterung aus- oder eingeschaltet wird kommt eine Meldung die einem mitteilt, dass auf die Web.config nicht geändert werden kann. Das kommt daher, dass der ExtensionManager versucht, bei einer Änderung des Status einer Erweiterung, das Blog neu zu starten. Versucht wird das in dem die Web.config geringfügig verändert wird. Bei einer Änderung der Web.config wird das Web automatisch neu gestartet.

Um den Fehler zu verhindern müsste man dem Account, unter dem das Web läuft, schreibrechte auf die Web.config geben. Wenn man einen eigenen IIS hat ist das kein Problem. Bei einem Shared Hosting geht das aber nicht immer. Ich kann z.B. auf den Dateien im root-Verzeichnis keine Rechte ändern. Die Web.config ist also immer schreibgeschützt für die Webanwendung. Dann kommt immer der Fehler.

Man kann das Web aber noch anders neu starten. Mit HttpRuntime.UnloadAppDomain(); erreicht man das selbe ohne die Fehlermeldung und gefühlt schneller. Angepasst werden muss folgendes.

In der ExtensionManager.cs:

public static void ChangeStatus(string extension, bool enabled)
{
    foreach (ManagedExtension x in _extensions)
    {
        if (x.Name == extension)
        {
            x.Enabled = enabled;
            BlogEngine.Core.DataStore.ExtensionSettings xs = new BlogEngine.Core.DataStore.ExtensionSettings(x.Name);
            xs.SaveSettings(x);
            SaveToCache();

            //string ConfigPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Web.Config";
            //File.SetLastWriteTimeUtc(ConfigPath, DateTime.UtcNow);

            HttpRuntime.UnloadAppDomain();
            break;
        }
    }
}

Die Zeilen 12 und 13 werden auskommentiert. Die Zeile 15 ist neu und bewirkt ein neustarten der Webanwendung.

.NET Object to VBScript to .NET

 Entwicklung  Kommentare deaktiviert für .NET Object to VBScript to .NET
Mai 142010
 
Ausganslage

Zur Zeit arbeite ich an einem grossen Projekt wo wir eine VB6 Anwendung nach .NET (C#) migrieren. Im grossen und ganzen wird einfach die Logik migriert, ohne gross neue Funktionen hinzuzufügen.

In der alten Anwendung wurde zu Berechnungszwecken einiges an Logik in VBScripte ausgelagert. Das machte man, um die Logik anpassen zu können ohne das neue DLLs erstellt werden müssten. Das muss ich jetzt in C# nachbauen. Die VBScripte sollen noch nicht migriert werden. Schlicht vom Aufwand her.

Ich muss ein .NET Objekt an ein VBScript übergeben, das macht die Berechnungen und gibt danach das Objekt wieder an C# zurück.

Zuerst dachte ich, man könnte einfach WScript.exe mit ein paar Parametern aufrufen. Allerdings kann ich so natürlich keine Objekte übergeben. Nach einigem suchen wurde ich dann fündig wie dich ein .NET Objekt an ein VBScript übergeben kann und auch wieder zurück bekomme. Das Stichwort lautet: Microsoft Script Control 1.0

Was muss nun alles gemacht werden um das Problemchen zu lösen.

Erstellen eines COM Visible .NET Objektes

Um ein .NET Objekt in einer COM Umgebung zu bearbeiten muss das Objekt für COM Sichtbar gemacht werden. Das kann in der Projekteinstellung der Klassen gemacht werden.

COM Visible einstellen

COM Visible registrieren

Mit diesen Einstellungen wird das Objekt automatisch von Visual Studio als COM Registriert. Auf einem produktiven System müsste das natürlich das Setup übernehmen oder ein Batch. Mit regasm MyAssembly.dll wird die DLL auf der Commandline registriert oder aus einem Batch raus.

Die Klasse selber kann sehr einfach aussehen.

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

namespace ComVisibleClass
{
	public class Person
	{
		private string m_Name;
		private string m_Vorname;
		private int m_Alter;
		public string Name
		{
			get{return m_Name;}
			set{m_Name = value;}
		}
		public string Vorname
		{
			get{return m_Vorname;}
			set{m_Vorname = value;}
		}
		public int Alter
		{
			get{return m_Alter;}
			set{m_Alter = value;}
		}
	}
}
Aufrufen des VBScriptes

Für den Test habe ich ein ganz einfaches VBScript erstellt. Es wird ein .NET Objekt übergeben und über eine MsgBox gibt es ein paar Meldungen zum Objekt. Somit ist klar das die Wert auch wirklich im VBScript manipuliert werden und auch wieder zurück ins .NET kommen.

Aber um ein VBScript auszuführen muss noch eine DLL eingebunden werden.

Microsoft Scripting Control 1.0

Unter Verweis hinzufügen wechselt man in das COM Register und sucht dort das Microsoft Scripting Control 1.0. Danach kann man auf das Control im Code zugreifen. Einfach noch das using hinzufügen wie in Zeile 6 im folgenden Code zu sehen.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using ComVisibleClass;
using MSScriptControl;

namespace TestApp
{
	class Program
	{
		static void Main(string[] args)
		{
			StreamReader streamReader = new StreamReader("Script.vbs", Encoding.Default);
			string script = streamReader.ReadToEnd();
			streamReader.Dispose();

			RunScript(script);

			Console.ReadLine();
		}

		private static void RunScript(string scriptCode)
		{
			try
			{
				ScriptControlClass script = new ScriptControlClass();
				script.Language = "VBScript";
				script.Reset();
				script.AllowUI = true;
				script.UseSafeSubset = false;

				script.AddCode(scriptCode);

				Person person = new Person() { Name = "Schumacher", Vorname = "Roland", Alter = 35 };
				Console.WriteLine(person.Alter);

				script.AddObject("Person", person, true);

				Object[] objects = new object[] { };

				script.Run("CalculateStatus", ref objects);

				Console.WriteLine(person.Alter);
			}
			catch (COMException ex)
			{
				Console.WriteLine(ex.ToString());
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
		}
	}
}

In den Zeilen 14 bis 16 wird ein VBScript geladen das im selben Verzeichnis liegt wie die Testanwendung. Das Script wird noch erläutert.

In Zeile 27 bis 42 findet die Ausführung des VBScriptes statt. Zuerst wird eine neue Instanz von ScriptControlClass erstellt und ein paar Parameter gesetzt. Mit script.AddCode übergibt man dem Control den Code den es ausführen soll. Achtung. Wenn das VBScript keine Sub oder Function enthält wird das Script schon in der Zeile 38 ausgeführt. Aber sobald das VBScript eine Function oder Sub enthält passiert in dieser Zeile noch nichts.

In Zeile 35 erstelle ich das Objekt das ich an das VBScript übergeben will. Man beachte das Alter. Das ist beim initialisieren auf 35 gesetzt. Zur Kontrolle gebe ich das in Zeile 36 aus.

In Zeile 38 übergebe ich das Objekt an das VBScript. Hier wird es auch wieder interessant. Im ersten Parameter geben ich eine Bezeichnung für das Objekt an. Genau mit dieser Bezeichnung greift man dann im VBScript auf die Person zu. In Zeile 42 wird schlussendlich die Funktion CalculateStatus() aufgerufen.

In Zeile 44 gebe ich nochmals das Alter aus. Und siehe da, es ist nicht mehr 35 sondern 99. Das alter 99 wurde im VBScript gesetzt. Der Informationsaustausch fand also statt. Ich kann ein .NET Objekt in VBScript manipulieren und die Ergebnisse stehen mir in .NET zur Verfügung.

Das VBScript

Das VBScript macht nicht viel spezielles.

Public Sub CalculateStatus()

    Dim oPerson
    set oPerson = Person

    if err.number <> 0 then
        MsgBox "UPS : Error " & Err.number & " Creating Customer: " & Err.description
    else
        MsgBox "Customer Name: " & oPerson.Name & " -- Vorname: " & oPerson.Vorname & " -- Alter: " & oPerson.Alter
        
        oPerson.Alter = 99
        
        MsgBox "Customer Name: " & oPerson.Name & " -- Vorname: " & oPerson.Vorname & " -- Alter: " & oPerson.Alter
    end if
End Sub

In Zeile 1 ist die Sub CalculateStatus definiert. In Zeile 3 definiere ich das Objekt oPerson und setze es in der Zeile 4. Und hier sieht man wieder die Person. Das ist genau die Person die man mit script.AddObject("Person", person, true);  übergeben hat.

In Zeile 9 wird die original Person kurz Visualisiert. Und Zeile 11 wird das Alter auf 99 gesetzt und in Zeile 13 wird die Person nochmals Visualisiert. Danach ist das VBScript fertig.

Im .NET ist das Alter der Person nun 99.

Result

Fazit

Was mir am Anfang als unlösbar mit .NET erschien ist im nachhinein sehr einfach zu lösen. Dank dem Internet und Google findet man irgend wann schon eine Lösung.

Jetzt gilt es nur noch ein Problem zu lösen. Die VBScripte müssen im Debugger gestartet werden können. Dazu gäbe es ja das Schlüsselwort stop im VB. Allerdings greift das nicht. Hier muss ich noch nach einer Lösung suchen was aber auch machbar sein wird.

Was noch cool wäre

So etwas sollte es auch mit C# als Script geben.

kick it on dotnet-kicks.de

Apr 162010
 

Sachen die immer mal wieder nützlich sein können.

1. Kalenderwoche eines Datum ermitteln.
private int KW(DateTime datum)
{
    CultureInfo cultureInfo = CultureInfo.CurrentCulture;
    int kw = cultureInfo.Calendar.GetWeekOfYear(datum, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
    return kw;
}
2. Ein einfaches Event erstellen.
public event EventHandler<EventArgs> DeletePersonEvent;
public void DeletePerson()
{
	if(DeletePersonEvent != null)
	{
		DeletePersonEvent(this, new EventArgs());
	}
}
3. WPF Zeilenumbruch in einer Caption oder Text Tag.
<TextBox x:Name="myTextBox" Text="Linie 1 &#x0a; Linie 2" />

Zeilenumbruch mit: &#x0a;

4. DataTable über WCF

Das geht nicht. Man darf es zuerst noch in ein DataSet verpacken und dann über WCF übertragen.

public DataSet GetPersonenDataSet()
{
    DataSet ds = new DataSet();
    ds.Tables.Add(m_PersonDao.Read());

    return ds;
}

m_PersonDao.Read() gibt ein DataTable zurück.

5. WPF Datenbindung

Auch immer mal wieder etwas was ich suche, obwohl es eigentlich sehr einfach ist.

Wahrscheinlich gibt es etwa 1000 Möglichkeiten wie man es lösen kann. Ich mache es zur Zeit so.

Code-Behind vom XAML:

public RaumView(RaumModel model)
{
    InitializeComponent();

    this.m_Model = model;

    this.DataContext = m_Model;
}

In Zeile 7 binde ich eine Klasse, die meine Objekte enthält, an den DataContext.

Danach kann ich auf den Controls relativ einfach die Daten binden.

Text="{Binding Raum.Standort.Bezeichnung, Mode=OneWay}"

In diesem Text wird eigentlich m_Model.Raum.Standort.Bezeichnung angezeigt. Das Feld wird aber nicht zurück in den DataContext synchronisiert (Mode=OneWay). Wenn das der fall sein soll, dann macht man das so.

Text="{Binding Raum.Bezeichnung, Mode=TwoWay}"

Die Bezeichnung des Raumes wird jetzt in den DataContext zurück synchronisiert (Mode=”TwoWay”).

So in etwa reicht es für mich zur Zeit aus.

6. GUID im Debugger ändern

Hat man mal das Bedürfnis eine Variable zur Laufzeit zu ändern, die vom Typ GUID ist, kann man nicht einfach die neue GUID im Debugger reinschreiben.

So sieht es zur Laufzeit aus.

Original GUID

Einfach neue GUID zwischen die {} kopieren resultiert in einem Fehler.

Fehler

Lösung:

Richtig gemacht

new Guid(“<GUID>”)

So sieht es danach aus.

neue GUID

7. Irgend eine Datei öffnen

Ich will eine Datei öffnen ohne das ich das Programm kennen muss was für die Datei nötig ist. Das geht natürlich nur wenn ein Haendler für die Datei auf dem System registriert ist. Und wenn, dann ist es einfach.

Process myProcess = new Process();
myProcess.StartInfo.FileName = "<PFAD_ZUR_DATEI>";
myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.RedirectStandardOutput = false;
myProcess.Start();

Der FileName muss ein kompletter Pfad zu seiner Datei sein. z.B.: “c:\temp\MeinProgi\MeineDatei.txt

8. Eine E-Mail mit dem Standard E-Mail Programm öffnen.

Auch das kann mit der Process Klasse gemacht werden.

Process myProcess = new Process();
myProcess.StartInfo.FileName = string.Format("mailto:{0}", mailto);
myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.RedirectStandardOutput = false;
myProcess.Start();

Und schon öffnet sich eine E-Mail mit seinem Standard E-Mail Programm. Aber das geht nur wenn auf dem System ein mailto Haendler registriert ist. Aber sobald ein E-Mail Klient installiert ist ist das der Fall. Wenn kein mailto Haendler vorhanden ist kann eigenartiges Passieren. Bei uns auf einem Windows XP startete danach ein paar 100 Mal der Internet Explorer. Wie so er so oft startete ist mir nicht klar. Aber danach gab es eine Klickorgie bis alle IE’s geschlossen waren.

Der mailto Haendler ist unter HKEY_CLASSES_ROOT\mailto zu finden. Das heisst, es muss auch etwas in der Form HKEY_CLASSES_ROOT\mailto\shell\open\command mit einem Eintrag vorhanden sein.

So kann auch eine URL mit dem Standardbrowser geöffnet werden. Dann gibt man bei FileName nur die URL an. http://…

9. WCF Klassen und ihre Konstruktoren

Ich wollte bei der Initialisierung einer neuen Klasse einen Wert erzwingen. Der soll immer gesetzt werden. Dazu habe ich einfach den Konstruktor überladen, dass heisst, keinen leeren mehr angeboten.

Danach wollte der Host nicht mehr starten. Aber die Fehlermeldung gab keinen Hinweis darauf. Auch war in den Tracelogs vom WCF kein Hinweis zu finden. Per Zufall habe ich dann einen Haltepunkt geschickt gesetzt und dort den Hinweis bekommen das die Klasse Serie nicht serialisiert werden kann. Da machte es klick.

Man merke sich. Klassen die über WCF übertragen werden sollen MÜSSEN einen default Konstruktor haben ohne Überladung.

10. WPF Dantenbindung zwischen Controls Eigenschaften

Na ja, ob hier der Titel passt?! Schauen wir mal.

Ich komme immer mal wieder an die Situation das ich Controls einschalten oder ausschalten soll, und das anhand eines anderen Controls. Z.B. Wenn RadioButton IsChecked=true ist, dann bist du eingeblendet. Das geht mit WPF auch erstaunlich einfach.

IsEnabled="{Binding ElementName=optMamUnten, Path=IsChecked}"

Binding ElementName gibt an welches Control man nutzen will. Mit Path wird dann noch gesagt auf welches Property man achten will. Sobald das RadioButton mit dem Namen optMamUnten IsChecked=true ist wird das Control eingeschaltet. So einfach kann es sein.

Gibt dann einen ganz schönen und einfach umzusetzenden Effekt.

Checked 1

Checked 2

kick it on dotnet-kicks.de

Visual Studio 2008 – Neue Klasse ist nicht public

 Entwicklung  Kommentare deaktiviert für Visual Studio 2008 – Neue Klasse ist nicht public
Jan 262010
 

Irgend wie kann das gewaltig nerven. Ich bin an einem relativ grossen Projekt. Dabei muss ich, quasi Fliessbandmässig, neue Klassen erstellen.  Allerding sind die Klassen nicht public. Jedesmal das public ergänzen nervt doch gewaltig.

Lösung:

C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplatesCache\CSharp\Code\1031\Class.zip
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplatesCache\CSharp\Code\1031\Interface.zip

In diesen zwei ZIP-Dateien sind die Vorlagen drin. Einfach noch ein public hinzufügen und schon klappt es wie gewünscht.

Wie so MS neue Klassen defaultmässig private macht ist mir nicht ganz klar. Ich brauche mehr public Klassen als private.

using System;
using System.Collections.Generic;
$if$ ($targetframeworkversion$ == 3.5)using System.Linq;
$endif$using System.Text;

namespace $rootnamespace$
{
	public class $safeitemrootname$
	{
	}
}

Das public in Zeile 8 macht es möglich.

kick it on dotnet-kicks.de

Sound der mir gefällt

 Ausbildung, Entwicklung  Kommentare deaktiviert für Sound der mir gefällt
Dez 162009
 

Zu erst dachte ich an einen Fehler. Das kann doch nicht sein. Der Titel eines Stückes heisst tatsächlich “C Sharp 2005”.

C Sharp 2005

Ich praktiziere jeden Morgen eine Stunde Selbststudium. Zur Zeit lese ich das Buch Effektive Softwarearchitekturen. Dabei höre ich Musik mit dem MediaMonkey.  Ich habe mir dabei angewöhnt die Titel zu bewerten. Das hat sich bewährt wenn man den Sound danach mit dem Mobile oder iPod synchronisiert. Dann ist nur der beste Sound auf dem Teil.

Heute viel mir dann wieder ein Stück auf, bzw. in die Ohren. “C Sharp 2005” von der CD “Dream Dance 35”? C Sharp? Das muss ein Fehler beim Taggen gewesen sein.

Also ab auf Amazon und nach der CD suchen. Tatsächlich, auch dort war der Titel mit “C Sharp 2005” gelistet.

Amazon - Dream Dance 35 - C Sharp 2005

Das ist doch richtiger Sound für einen C# Entwickler. 🙂

VSTO – Word Office XP/2002 – normal.dot wird immer grösser

 Entwicklung  Kommentare deaktiviert für VSTO – Word Office XP/2002 – normal.dot wird immer grösser
Okt 152009
 

Für WordPlus 4 generiere ich ein Menü im Word. Wir mussten feststellen, dass nach dem Word geschlossen wurde die normal.dot immer um ein paar KB grösser wurde. 30,40,50,60KB … bis es Kunden gab die mit mehr als 10MB zu kämpfen hatten. Das darf aber nicht sein. Nicht wegen einem Menü.

Wenn ein Menüpunkt dem Word hinzugefügt passiert das in etwa so.

Office.CommandBar toolBar = word.CommandBars.Add("Name des Menüpunkte", 
Microsoft.Office.Core.MsoBarPosition.msoBarTop, Type.Missing, true);

Der letzte Parameter würde eigentlich angeben das der Eintrag nur temporär wäre. Aber die VSTO scheint das zu ignorieren. Deshalb wird die normal.dot immer grösser.

Im Netzt gibt es einige Lösungen für das Problem.

– Die normal.dot schreibschützen. Nein, nicht gut.

– Das Menü in ein Menu.dot auslagern und das installieren. Auch nicht gut, da bei uns das Menü dynamisch aufgebaut wird.

– Den CusomizationContext auf der WordApplication ändern. Das tönt nicht schlecht.

Den letzten Punkt habe ich dann mal umgesetzt.

private Word.Template GetCustomTemplate()
{
	object templatePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase), "Menu.dot").Replace(@"file:\", "");            

	if (!File.Exists(templatePath as string))
	{
		return null;
	}

	object install = true;
	word.AddIns.Add(templatePath.ToString(), ref install);
	return word.Templates.get_Item(ref templatePath);
}

private void CreateMenu(string ribbonXml, string iconPath)
{
	Word.Template customTemplateTemp = (Word.Template)word.CustomizationContext;
	Word.Template newTemplate = null;

	try
	{
		//TemplateContext in Sicherheit bringen
		newTemplate = GetCustomTemplate();
		if (newTemplate != null)
		{
			word.CustomizationContext = newTemplate;
		}

		//Menü erstellen
	}
	finally
	{
		if (newTemplate != null)
		{
			((Word.Template)word.CustomizationContext).Saved = true;
			word.CustomizationContext = customTemplateTemp;
		}
	}
}

Es wird also ein Menu.dot gesucht. Wenn es diese gibt wird der CustomizationContext damit ausgetauscht und das original vorher noch in Sicherheit gebracht. Das Menü wird erstellt und danach wieder der alte Context angehängt. Ist eine Empfehlung aus dem Netzt und wurde früher in VBA auch so gelöst.

Bei mir funktionierte das einwandfrei. Egel ob ich Word aus Visual Studio heraus startete oder nicht. Das Menü wurde richtig geladen und die Klickevents funktionierten.

Ich musste jetzt aber feststellen, dass es Clients gibt bei denen einfach kein Klickevent ausgelöst wird. Wenn auf ein Menüeintrag geklickt wurde passierte einfach nichts. Aber nicht auf allen Clients. Auf dem Entwicklungsclient klappte es. Egal ob ich das AddIn aus VS heraus startete oder nicht. Menü tat immer. Man finde jetzt das Problem.

Word hat zudem die dumme Angewohnheit ein altes Menü immer noch anzuzeigen, obwohl das AddIn nicht geladen wurde. Ich ging also immer davon aus, dass das AddIn geladen wird.

Nach langem suchen wurde dann klar was los ist.

Das AddIn wird geladen, dass Menü erstellt und auch angezeigt. Aber sobald der Code in der Zeile 36 vorbei kam verschwand das Menü. Also wusste ich das es mit dem CustomizationContext zusammen hängen musste.

Die Lösung ist jetzt äusserst einfach.

//Menü erstellen
((Word.Template)word.CustomizationContext).Saved = true;

Das Menü wird nun angezeigt und funktioniert auch. Kein Context wechsel mehr.

Die normal.dot wird aber nicht mehr grösser. Wenn Änderungen an Word vorgenommen werden, z.b. Toolbar Anpassungen, werden diese aber sehr wohl in der normal.dot gespeichert. Aber das automatisch generierte Menü kommt nicht mehr in die normal.dot. Ziel erreicht.

kick it on dotnet-kicks.de

Sep 232009
 

Wenn das bestimmt werden soll wird es schwierig. Ich musste einem Problem nachgehen das auftaucht wenn man mit der VSTO unter Excel in einem 64Bit OS arbeitet. Gewisse Operationen verursachen folgende Fehlermeldung:

Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD))

Abhilfe schafft der Artikel KB320369. Der Fehler wird offensichtlich auf einen Fehler in der VSTO zurück geführt.

Ich wollte den Hack aber nicht auch unter 32Bit OS anwenden. Aber dafür muss ich irgend wie bestimmen ob der Code unter einem 64Bit oder 32Bit OS ausgeführt wird. Das .NET Framework scheint da keine Methode anzubieten um das einfach zu lösen. Im Internet sind  einige kuriose Lösungen für das Problem zu finden.

Ich habe jetzt folgende umgesetzt die unter Vista 64Bit und Windows 7 32Bit funktioniert.

//Nicht vergessen
using System.Runtime.InteropServices;


[DllImport("kernel32.dll")]
private static extern uint GetSystemWow64Directory(StringBuilder lpBuffer, uint uSize);
internal static byte GetOSBitness()
{
    StringBuilder lpBuffer = new StringBuilder(260);
    uint uSize = GetSystemWow64Directory(lpBuffer, 260);
    if (uSize > 0) return 1; //64-bit
    return 0; //32-bit
}

Gefunden –> HOWTO: Detect Process Bitness

Diese Lösung funktioniert aber nur ab Windows XP (MSDN).

kick it on dotnet-kicks.de

Sep 232009
 

Man lernt nie aus. So sollte es auch sein.

Ich habe immer mal wieder die Aufgabe eine Logdatei zu erstellen. Dabei sollte sich ein sinnvoller Name der Datei ergeben. Etwa “20090923_080355.txt”. Solche Dateinamen können nun mit den wildesten Methoden gebaut werden.

Hier ein Beispiel aus meinen Anfangszeiten (OK. Das ist noch gar nicht so lange her).

DateTime now = DateTime.Now;
string fileName = now.Year.ToString("0000") 
	+ now.Month.ToString("00") + now.Day.ToString("00") + "_" 
	+ now.Hour.ToString("00") + now.Second.ToString("00") 
	+ now.Millisecond.ToString("000") + ".txt";

Sieht doch ziemlich praktikabel aus. Auf jeden Fall funktioniert es. Aber geht es auch einfacher?

Es geht einiges einfacher.

DateTime jetzt = DateTime.Now;
string fileName = jetzt.ToString("yyyyMMdd_HHmmss_FFF") + ".txt";

Sieht doch schon viel eleganter aus.

Zur Info. “FFF” steht für die Millisekunden. In der MSDN gibt es noch einiges mehr zu entdecken.

Das gibt doch schöne Logdateinamen. Können Logdateinamen schön sein? 😉

Liste mit Logdateinamen

kick it on dotnet-kicks.de