code it

Martins Tech Blog

TraceListener in eine TextBox

In letzter Zeit hab ich ein wenig mit TraceListeners rumprobiert. Die sind sehr praktisch, wenn man schnell ein Log erzeugen will und nicht die Konfigurationsmöglichkeiten von log4net oder ähnlichen Frameworks benötigt.

Als konkreten Anwendungsfall hab ich einen Webpart, den ich loggen wollte, aber auf dem Zielsystem sind nicht ausreichende Berechtigungen, um ins Dateisystem zu schreiben.

Leider bietet mir der TextWriterTraceListener nicht die Möglichkeit, das Ergebnis statt in ein TextFile in eine TextBox zu schreiben. Aus diesem Grund habe ich ich mich dazu entschieden, einen eigenen Listener zu schreiben.

using System;
using System.Diagnostics;
using System.Web.UI.WebControls;

namespace UniqueSoftware.Logging
{
  internal class TextBoxTraceListener : TraceListener
  {
      private TextBox _textbox;

      public override void Write(string message)
      {
          _textbox.Text += message;
      }

      public override void Write(object o)
      {
          _textbox.Text += o.ToString();
      }

      public override void WriteLine(string message)
      {
          _textbox.Text += message + Environment.NewLine;
      }

      public TextBoxTraceListener(TextBox textBox)
      {
          if (textBox == null)
          {
              throw new ArgumentNullException("textBox");
          }
          this._textbox = textBox;
      }
  }
}

Da ich mich nicht in Windows.Forms befinde, muss praktischerweise das Befüllen der TextBox auch nicht mit einem Delegate durchgeführt werden. Das ist auch schon alles. Der TraceListener kann nun genau wie jeder andere Listener verwendet werden.

Die Konfiguration nehm ich programmatisch vor. Damit hat der Anwender eine grafische Benutzeroberfläche zur Anpassung des Loglevels und muss nicht in der web.config Anpassungen vornehmen.

Zunächst werden die Member-Variablen deklariert.

private TextBox logMessages;
private SourceLevels logLevel = SourceLevels.All;
private TraceSource log = null;

Im Konstruktor wird das Log dann initialisiert.

public MyWebPart()
{  
  logMessages = new TextBox();
  log = new TraceSource("MyWebPart");
  log.Switch.Level = logLevel;
  log.Listeners.Add(new Logging.TextBoxTraceListener(logMessages));
}

Nun hab ich noch 3 Methoden, die das Logging durchführen.

#region logging
private void TraceError(string message)
{
  if (log != null)
  {
      message = string.Concat(DateTime.Now.ToString("HH:mm:ss.ff"), " : ", message);
      log.TraceEvent(TraceEventType.Error, 0, message);
  }
}

private void TraceInformation(string message)
{
  if (log != null)
  {
      message = string.Concat(DateTime.Now.ToString("HH:mm:ss.ff"), " : ", message);
      log.TraceEvent(TraceEventType.Information, 0, message);
  }
}

private void TraceWarning(string message)
{
  if (log != null)
  {
      message = string.Concat(DateTime.Now.ToString("HH:mm:ss.ff"), " : ", message);
      log.TraceEvent(TraceEventType.Warning, 0, message);
  }
}
#endregion

Diese Methoden werden je nach Anwendungsfall aufgerufen und ob das Logging durchgeführt wird oder nicht, übernimmt wie gewohnt die TraceSource je nach eingestelltem LogLevel, der nun nur noch per Eigenschaft einstellbar gemacht werden muss.

Virtuelle Platten verkleinern

Bei der Arbeit mit Virtual Server mit dynamisch vergrößerbaren Platten fällt einem früher oder später auf, dass Microsoft die Bezeichnung "dynamisch vergrößerbar" sehr ernst genommen hat, denn die vhd-Dateien werden nach und nach immer größer und auch eine Defragmentierung auf dem Gast nach dem Löschen von Dateien kann daran nichts ändern.

Aber Hilfe ist in Sicht. Eine Verkleinerung ist möglich. Dazu geht man wie folgt vor... Ein Tipp noch: Da das Verfahren einige Minuten bis Stunden dauern kann und während dieser Zeit einiges an Systemressourcen benötigt, führt man es am besten während eines Zeitfensters durch, in dem man weder Gast noch Host anderweitig benötigt.

1.) Löschen nicht benötigter Dateien auf dem Gast

Damit das Verkleinern den größtmöglichen Effekt hat, sollte man auf dem Gast zunächst alle nicht mehr benötigten Dateien löschen. Beispiele dafür sind der Papierkorb, die Temp-Ordner oder Temporary Internet Files.

2.) Defragmentieren des Gasts

Nun defragmentiert man die Platte im Gast noch einmal, damit möglichst viel freier Platz zusammenhängt.

3.) Mappen der precompact.iso

In der Virtual Server Verwaltungswebsite muss man nun die Abbilddatei Precompact.iso mappen, damit man Zugriff auf den Pre-Compactor erhält.

4.) Ausführen des Pre-Compactors

Im Gast kann nun der Pre-Compactor gestartet werden. Damit wird die vhd-Datei für die Komprimierung vorbereitet.

5.) Herunterfahren des Gasts

6.) Ausführen der Komprimierung

In der Virtual Server Verwaltungswebsite wählt man nun unter Virtuelle Festplatten/Überprüfen die zu komprimierende vhd-Datei aus. Und hier findet man nun auch die Aktion "Virtuelle Festplatte komprimieren".

Soweit zur Theorie.... Jetzt muss ich nur noch jemanden finden, der das ausprobieren will, da ich keine mehreren Stunden Zeit hab.

Thumbnails erzeugen

Wer kennt sie nicht, die kleinen Vorschaubildchen, die man sich ansehen kann, um zu entscheiden, ob man das "richtige" Bild auch ansehen möchte? Heute stand ich vor der Aufgabe, genau diese Bildchen auch zu erstellen.

Nichts einfacher als das, dachte ich mir, denn bei anderen Projekten war ich auch schon über den Namespace System.Drawing gestolpert, der in dieser Beziehung sehr mächtig ist. Sehr überrascht stellte ich fest, dass hier die Entwickler des Framework wohl mitgedacht haben, denn Objekte des Typs Image haben von Haus aus eine Methode GetThumbnailImage, die genau das leisten soll.

Ich schaute mir also die MSDN-Seite zum Thema an und war doch etwas überrascht....

a) Die Methode hat den Parameter callback, der zwar nicht verwendet wird, aber trotzdem erzeugt und übergeben werden muss "In GDI+ ... wird der Delegat nicht verwendet. Sie müssen trotzdem einen Delegaten erstellen und einen Verweis auf diesen in diesem Parameter übergeben."

b) Die Methode hat den Parameter callbackData, der immer mit IntPtr.Zero übergeben werden muss.

"Interessante Methode!" sag ich dazu nur.

Noch überraschter war ich, folgendes Statement zu finden: "GetThumbnailImage funktioniert gut, wenn die angeforderte Miniaturansicht eine Größe von ca. 120 x 120 hat. Eine Anforderung einer großen Miniaturansicht (z. B. 300 x 300) eines Image-Objekts mit einer eingebetteten Miniaturansicht kann zu einem deutlichen Qualitätsverlust bei der Miniaturansicht führen. Es kann ggf. ratsam sein, das Hauptbild anstelle der eingebetteten Miniaturansicht zu skalieren, indem Sie DrawImage aufrufen."

Da ich nicht mit Sicherheit sagen kann, wie groß die Bilder irgendwann mal sein müssen und ich doch eine etwas generischere Methode brauche, entschließe ich mich also, das Bild wie genannt zu skalieren:

private Image GetThumbNailImage(Image BigImage, int ThumbNailWidth, int ThumbNailHeight)
{
// check params
if (BigImage == null)
{
  throw new ArgumentNullException("BigImage");
}

if (ThumbNailHeight <= 0)
{
  throw new ArgumentException("ThumbNailHeight");
}

if (ThumbNailWidth <= 0)
{
  throw new ArgumentException("ThumbNailWidth");
}

// create a new image with new size
Image thumbnail = new Bitmap(ThumbNailWidth, ThumbNailHeight);
// get a handle to the drawing interface for the new image 
Graphics thumbgraphics = Graphics.FromImage(thumbnail);
// draw the big image on the smaller image
thumbgraphics.DrawImage(BigImage, 0, 0, ThumbNailWidth, ThumbNailHeight);

return thumbnail;
}

Soweit, so gut... Nun hat man aber nicht immer die neuen Größen bei der Hand, sondern möchte auch mal nur die Breite oder die Höhe angeben und das Programm soll selbst den jeweiligen anderen Parameter errechnen und das Seitenverhältnis beibehalten. Also schreib ich mir noch schnell 2 Methoden, die diese Funktionalität bieten.

private Image GetThumbNailImageFixedWidth(Image BigImage, int ThumbNailWidth)
{
// check params
if (BigImage == null)
{
   throw new ArgumentNullException("BigImage");
}
if (ThumbNailWidth <= 0)
{
  throw new ArgumentException("ThumbNailWidth");
}

double ratio = (double) BigImage.Width / (double) BigImage.Height;

int newwidth = ThumbNailWidth;
int newheight = (int)(newwidth / ratio);

return GetThumbNailImage(BigImage, newwidth, newheight);
}
private Image GetThumbNailImageFixedHeight(Image BigImage, int ThumbNailHeight)
{
// check params
if (BigImage == null)
{
  throw new ArgumentNullException("BigImage");
}
if (ThumbNailHeight <= 0)
{
  throw new ArgumentException("ThumbNailHeight");
}

double ratio = (double)BigImage.Width / (double)BigImage.Height;

int newheight = ThumbNailHeight;
int newwidth = (int)(newheight * ratio);

return GetThumbNailImage(BigImage, newwidth, newheight);
}

Das war's auch schon. Für den Test nutz ich dann mal die Beispielbilder....

// Originalbild
Image bigimage = Image
.FromFile("C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert Landscape.jpg"); // neues Bild mit vorgegebener Höhe Image smallimagebyfixedheight = GetThumbNailImageFixedHeight(bigimage, 200); // neues Bild mit vorgegebener Breite Image smallimagebyfixedwidth = GetThumbNailImageFixedWidth(bigimage, 200); // Thumbnails speichern smallimagebyfixedheight.Save("C:\\testimageh.jpg", ImageFormat.Jpeg); smallimagebyfixedwidth.Save("C:\\testimagew.jpg", ImageFormat.Jpeg);

That's it !

Anzahl von Vorkommen eines Textes in einem anderen Text ermitteln

Heute war ich auf der Suche nach einer Funktion, die die Anzahl der Vorkommen eines Textes in einem anderen Text ermittelt. Leider boten mir weder System.Text noch System.String eine Funktion, mit der das mit wenig Aufwand möglich war.

Die doch recht einfache Lösung des Problems fand ich mit den Regulären Ausdrücken (System.Text.RegularExpressions)

using System.Text.RegularExpressions;

private int CountStrings(string str, string regexStr)
{
 Regex regex = new Regex(regexStr);
 return regex.Matches(str).Count;
}