Neues Lost-Video

Hilfe, wie spannend kann man eine Fernsehserie machen? Gerade habe ich dieses Video bei Terminally Incoherent gefunden und ich wurde mal wieder daran erinnert, dass es noch 6 MONATE dauert, bis Staffel 4 endlich anläuft. Das ist schon fast Folter 😉

Die neue Staffel soll übrigens Flashbacks und Flashforwards enthalten, wie sie auch schon in der letzten Folge von Staffel 3 zu sehen waren. Ich bin so gespannt, wie die Macher die Story auflösen, das gibt’s gar nicht…

Lost ist übrigens nach CSI: Miami die weltweit beliebteste Serie. Wobei ich nicht verstehe, was an CSI: Miami außer den lustigen Saufspielen besser sein soll…

DCOM-/RPC-Zugriff auf Windows Server 2003 schlägt fehl: ‘Zugriff verweigert’

Eine Anwendung, die bei uns bislang auf einem Windows 2000 Server lief und Verbindungen von Clients per DCOM annimmt, erlaubte auf einem Windows 2003 Server plötzlich keine Anmeldungen mehr. “Zugriff verweigert” erschien als Fehlermeldung, sobald sich ein Benutzer mit normalen Rechten (also kein Administrator) anmelden wollte.

Die Lösung dieses Problems war eine Einstellung, die es anscheinend erst auf Windows 2003 Server gibt (zumindest habe ich sie auf unserem alten Windows 2000 Server nicht finden können). Den entscheidenen Tipp fand ich in der Microsoft Knowledge Base:

  1. dcomcnfg starten
  2. Komponentendienste auswählen und in der Symbolleiste auf Arbeitsplatz konfigurieren klicken
  3. Im Dialogfeld Arbeitsplatz auf COM-SicherheitStart- und AktivierungsberechtigungenLimits bearbeiten
  4. Die gewünschten Gruppen/Benutzer der Liste hinzufügen und ihnen die Rechte Remotestart und Remoteaktivierung gewähren

server2003-dcom-rpc-zugriff.jpg

C#: Klassenübergreifendes Logging

Heute habe ich mir ein paar Gedanken zum Thema Logging mit C# gemacht. Für das umfangreiche Projekt, an dem ich zur Zeit arbeite, musste ich eine einfache und flexible Möglichkeit finden, verschiedene Meldungen (Hinweis, Erfolg, Fehler) zu loggen, ohne die zahlreichen Klassen von einer bestimmten Implementierung abhängig zu machen. Meine Lösung hierzu sieht wie folgt aus (das Diagramm habe ich mit ArgoUML erstellt):

UML-Diagramm meines C#-Loggers

Erklärung

  • Mein Entwurf basiert auf dem Singleton Logger über den an beliebiger Stelle in meinen verschiedenen Klassen einfach mittels Logger.Log(LogEintragTyp.Fehler, "Fehler!") Meldungen geloggt werden können. Hierzu ist lediglich der Verweis auf meine Assembly Logging nötig, da Log() eine statische Methode ist und Logger nicht instantiiert werden muss.
  • Der konkrete Logger, der die Ausgabe der Meldugen übernimmt, wird mittels Logger.SetLogger() gesetzt (geschieht dies nicht, wird halt nichts ausgegeben).
  • Das Interface ILogger muss von Klassen implementiert werden, die als Logger fungieren sollen. Meine Beispielimplementierung ConsoleLogger ist unten abgebildet.
  • Diese Implementierung weist eine große Flexibilität aus, weil ich zur Laufzeit den ausgebenden Logger wechseln kann (z.B. von Konsole zu Logdatei) und dank des Structs LogEintrag einfach weitere Attribute zu den Logmeldungen hinzugefügt werden können, ohne die vorhandenen Quelltexte großartig anpassen zu müssen. In meinen verschiedenen Klassen muss ich lediglich auf meine Assembly verweisen (mittels using Logging; und kann dann mit nur einer Zeile (ohne lästige Prüfung auf null etc.) einen Logeintrag erstellen.

Und so sieht das Ergebnis aus:

Beispiel-Ausgabe meines C#-Loggers

Quelltexte

Der Logger

/// <summary> /// Ein global verfügbarer Logger. /// </summary> public sealed class Logger { #region Statische Member private static readonly Logger _instanz = new Logger(); /// <summary> /// Loggt die übergebene Meldung. /// </summary> /// <param name="typ">Typ der Meldung.</param> /// <param name="meldung">Text der Meldung.</param> public static void Log(LogEintragTyp typ, string meldung) { _instanz.NeueMeldung(typ, meldung); } /// <summary> /// Setzt den Logger, der zur Ausgabe der Meldungen verwendet werden soll. /// </summary> /// <param name="logger"></param> public static void SetLogger(ILogger logger) { _instanz._logger = logger; } #endregion #region Instanzmember private ILogger _logger = null; private List<LogEintrag> _eintraege = new List<LogEintrag>(); private void NeueMeldung(LogEintragTyp typ, string meldung) { LogEintrag le = new LogEintrag(typ, meldung); this._eintraege.Add(le); if (this._logger != null) this._logger.NeueMeldung(le); } #endregion }

Logger-Implementierung

public interface ILogger { /// <summary> /// Wird aufgerufen, sobald eine neue Meldung geloggt wird. /// </summary> /// <param name="meldung">Die neue Meldung.</param> void NeueMeldung(LogEintrag meldung); /// <summary> /// Gibt die Liste aller geloggten Meldungen zurück. /// </summary> /// <returns>Die Liste aller geloggten Meldungen.</returns> List<LogEintrag> GetMeldungen(); }

Zur farbigen Ausgabe auf der Konsole habe ich die hier beschriebene Klasse verwendet: Putting colour/color to work on the console.
/// <summary> /// Ein Logger, der die Meldungen lediglich (farbig) auf der Konsole ausgibt. /// </summary> public class ConsoleLogger : ILogger { #region ConsoleColor /// <summary> /// Static class for console colour manipulation. /// (http://www.codeproject.com/csharp/console_apps__colour_text.asp) /// </summary> private class ConsoleColor { // source code of ConsoleColour from http://www.codeproject.com/csharp/console_apps__colour_text.asp } #endregion #region ILogger Member /// <summary> /// Gibt eine neue Meldung auf der Konsole aus. /// </summary> /// <param name="meldung">Die neue Meldung.</param> public void NeueMeldung(LogEintrag meldung) { switch (meldung.Typ) { case LogEintragTyp.Erfolg: ConsoleColor.SetForeGroundColour(ConsoleColor.ForeGroundColour.Green); break; case LogEintragTyp.Fehler: ConsoleColor.SetForeGroundColour(ConsoleColor.ForeGroundColour.Red); break; default: break; } Console.WriteLine(meldung.Zeitpunkt.ToString("HH:mm:ss.ffffff") + " " + meldung.Typ.ToString() + ": " + meldung.Text); ConsoleColor.SetForeGroundColour(); } /// <summary> /// Nicht implementiert. /// </summary> /// <returns>-</returns> public List<LogEintrag> GetMeldungen() { throw new Exception("The method or operation is not implemented."); } #endregion }

Log-Einträge

/// <summary> /// Mögliche Typen eines Log-Eintrags. /// </summary> public enum LogEintragTyp { /// <summary> /// Erfolgsmeldung. /// </summary> Erfolg, /// <summary> /// Fehlermeldung. /// </summary> Fehler, /// <summary> /// Hinweis. /// </summary> Hinweis } /// <summary> /// Eine Meldung für das Log. /// </summary> public struct LogEintrag { /// <summary> /// Der Typ der aktuellen Meldung. /// </summary> public LogEintragTyp Typ; /// <summary> /// Der Text der aktuellen Meldung. /// </summary> public string Text; /// <summary> /// Zeitpunkt zu dem die Meldung erstellt wurde. /// </summary> public DateTime Zeitpunkt; /// <summary> /// Konstruktor. /// </summary> /// <param name="typ">Der Typ der neuen Meldung.</param> /// <param name="text">Der Text der neuen Meldung.</param> public LogEintrag(LogEintragTyp typ, string text) { this.Typ = typ; this.Text = text; this.Zeitpunkt = DateTime.Now; } /// <summary> /// Gibt Typ und Text der aktuellen Meldung aus. /// </summary> /// <returns>Typ und Text der aktuellen Meldung.</returns> public override string ToString() { return this.Typ.ToString() + ": " + this.Text; } }

Zukunft Mittelstand (Düsseldorf, 28.08.)

Der nächste Unternehmerabend aus der Reihe Zukunft Mittelstand von kalaydo.de findet am 28. August 2007 in Düsseldorf statt. Unter dem Thema Von Informationen zu Wissen und Wettbewerbsvorsprung referiere ich erneut über unser Firmenwiki.

Die Veranstaltung beginnt um 17.30 Uhr im Filmmuseum in der Schulstraße 4, 40213 Düsseldorf.

Programm

16.45 Uhr  Optional: Führung durch das Filmmuseum
17.30 Uhr  Come-Together
18.00 Uhr  Begrüßung und Moderation
           (Joachim Vranken, Geschäftsführer, kalaydo.de)
18.15 Uhr  Wissen teilen oder Wissen verstecken? Wissensmanagement als strategischer Erfolgsfaktor
           (Dr. rer. nat. Frank Hees, Bereichsleiter Kommunikations- und Organisationsentwicklung, RWTH Aachen & OSTO® Systemberatung)
18.45 Uhr  Wissensmanagement 2.0, Wikis und Co. im Unternehmensalltag
           (Stefan Macke, Verantwortlicher Intranet & Wiki, Alte Oldenburger Krankenversicherung)
19.15 Uhr  Best Practices: Konkrete Maßnahmen zur Erfahrungssicherung
           (Martin Fischer, Abteilung Wissensmanagement, Fraunhofer-Gesellschaft)
19.45 Uhr  Diskussion mit den Referenten
20.15 Uhr  Wir laden Sie ein zu einem regionalen Buffet!

XML-Export aus Excel: Namespace-Präfix ‘ns1’ durch eigenes Präfix ersetzen

Beim Exportieren von Daten aus Excel in eine XML-Datei werden für die enthaltenen XML-Namespaces standardmäßig Präfixe der Form ns1, ns2 ... verwendet (siehe Export von XML-Daten). In einer XML-Datei benötigte ich nun aber ein eigenes Präfix, da Natural leider nicht ohne Weiteres mit XML-Namespaces umgehen kann und die Präfixe hart codiert werden müssen, um die Inhalte der Elemente auslesen zu können.

Nach einiger Suche habe ich leider keine passende Lösung für dieses Problem gefunden. Ich exportiere die XML-Daten nun einfach manuell! Anstatt ActiveWorkbook.XmlMaps.Export() zu verwenden, womit direkt eine XML-Datei erstellt werden könnte, nutze ich nun ActiveWorkbook.XmlMaps.ExportXML(). Diese Methode liefert einen XML-String zurück, den ich im Nachhinein wie gewünscht anpasse:
dim xmlstring as String dim exportError as XlXmlExportResult dim dateiname as String exportError = ActiveWorkbook.XmlMaps("XML_Zuordnung").ExportXML(xmlstring) ' Validierung fehlgeschlagen If exportError = xlXmlExportValidationFailed Then Exit Sub End If ' Namespace-Prefix ersetzen: ns1 -&gt; tab xmlstring = Replace(xmlstring, "ns1", "tab") ' Datei schreiben Open dateiname For Output Access Write As 1 Print #1, xmlstring Close #1

Es wäre schön, wenn es eine bessere Lösung gäbe, aber ich habe nichts Vergleichbares finden können. Weiß da irgendjemand mehr?

C#: Verwendete XML-Namespaces aus XML-Dateien auslesen

Lange habe ich gesucht und doch keine Antwort auf die (meiner Meinung nach einfache) Frage gefunden, wie man mit C# alle in einer XML-Datei verwendeten XML-Namespaces auslesen kann. Damit meine ich eine simple Liste mit Präfixen und den diesen zugeordneten Namespaces, wie sie im Wurzelelement der XML-Datei definiert werden.

Es gibt zwar den XmlNamespaceManager, aber dem müssen die Namespaces einer Datei manuell hinzugefügt werden, die Präfixe und URIs müssen also bekannt sein. Er liest sie nicht automatisch aus Dateien aus (abgesehen von drei Standard-Namespaces, die immer vorhanden sind).

Da ich aber gerne aus vorhandenen Dateien die verwendeten Namespaces inkl. den ihnen zugewiesenen Präfixen auslesen möchte, habe ich die folgende Methode geschrieben, die mir eine Hashtable der Namespace-URIs mit ihren Präfixen als Keys liefert.

public static Hashtable GetXmlNamespaces(XmlDocument xmldoc) { Hashtable nse = new Hashtable(); foreach (XmlAttribute att in xmldoc.DocumentElement.Attributes) { if (att.Name.StartsWith("xmlns")) { // Default-Namespace? if (att.Name.IndexOf(":") == -1) { nse[string.Empty] = att.Value; } else { string nsname = att.Name.Substring(att.Name.IndexOf(":") + 1); nse[nsname] = att.Value; } } } return nse; }

Beispiel: nse["sm"] => “http://blog.stefan-macke.de

Weiß noch jemand eine einfachere Lösung? 🙂

Rendern von TYPO3-Links in eigener Extension (RTE: <link>)

Bei der Ausgabe von Links, die im TYPO3-Backend mit dem RTE eigegeben wurden, werden die dort erzeugten &lt;a&gt;-Tags umgewandelt in das TYPO3-interne Format &lt;link /&gt; und in der Datenbank gespeichert. Die Ausgabe im Frontend führt dann zu Fehlern, da die &lt;link /&gt;-Tags nicht interpretiert werden können. Um die Tags vor der Ausgabe in korrekte Links umzuwandeln, habe ich folgende Lösung gefunden: Die Methode TS_links_rte der Klasse t3lib_parsehtml_proc übernimmt genau dieses Rendern von Links. Sie kann wie folgt in die eigene Extension eingebunden werden:

require_once(PATH_t3lib.'class.t3lib_parsehtml_proc.php'); $parseObj = t3lib_div::makeInstance('t3lib_parsehtml_proc'); $content = $parseObj->TS_links_rte($content);