Archiv

Archiv für die Kategorie ‘Microsoft .NET’

.NET Open Space Süd 2012 in Karlsruhe

Eigentlich leide ich zur Zeit unter notorischem Zeitmangel. Aber das .NET Open Space in Karlsruhe habe ich mir dieses Jahr nicht nehmen lassen. Nachdem ich letztes Jahr nicht teilnehmen konnte, war es für mich der zweite Besuch. Viele Gesichter kannte ich aus diesem Grund schon. Die Veranstaltung wurde wieder in den Räumen von bluehands ausgerichtet. Ich finde das nach wie vor beachtlich, alles einfach mal so umzuräumen. Danke!
Die Themen wurden von den 70 Teilnehmern wieder selbst bestimmt und wie auch schon 2010 war es kein Problem, ausreichend Themen für die Agenda zu finden. Neuer großer Schwerpunkt war ganz klar die Entwicklung für Smartphones. Kaum einer kann sich der Problematik momentan entziehen und irgendwo ist mit dem Windows 8 Tablet ja auch von Microsoft etwas im Anmarsch, was deren Windows Phone pushen könnte. Interessant war in diesem Fall die aktuelle Dominanz von Apple-Hardware, zeitweise waren auf dem Openspace 80% der Notebooks und Tablets von Apple. Auf alle Fälle durften wir diesbezüglich einen guten Einblick in die Entwicklung für Android und Apple-Smartphones mit Xamarin gewinnen.
Christina Hirth griff das Thema CleanCode(Developer) auf, was eine sehr intensive Diskussion in Gang brachte. Es herschte eine Einigkeit darüber, dass den meisten Teilnehmer vom #nossued das Thema wohl geläufig ist. Eine gute Möglichkeit, dieses in Firmen einzubringen, könnten Coding Dojos sein.

Pfadnamen für Ausgabe kürzen

15. Dezember 2011 Keine Kommentare

Im Microsoft .NET Framework ist schon lange eine Funktion zum Kürzen von Dateinamen enthalten. Aus c:\Windows\System32\MyDll.dll kann dann zum Beispiel c:\Windows\…\MyDll.dll werden. Hierzu greift man auf TextRenderer.MeasureText zurück. Allerding hat diese Funktion einen kleinen Fehler bei dem zurückgegebenen Wert, der hier beschrieben ist:

Der Workaround ist zum Glück auch gleich angegeben, und kann dann so aussehen:

string filename = string.Copy(filenameForOutput);
if (!string.IsNullOrEmpty(filename))
{   
   TextRenderer.MeasureText(filename, Font, Size,
     TextFormatFlags.PathEllipsis | TextFormatFlags.ModifyString);
   filename = filename.Substring(0, filename.IndexOf('\0'));
}

Der Source zeigt auch gleich noch eine kleine andere Ergänzung: Die Funktion modifiziert den übergebenen String. Man sollte diesen also vorher kopieren, sofern man nicht schon mit einer Kopie des Strings arbeitet.

KategorienMicrosoft .NET Tags: ,

Manuelles schreiben einer lokalen Url in Asp.NET

23. September 2011 1 Kommentar

Glaubt man der Dokumentation von HttpRequest.ApplicationPath, so ist es recht einfach, einen Link in einer eigenen Asp.NET Seite zu verwenden:

Label1.Text = Request.ApplicationPath;
Image1.ImageUrl = Request.ApplicationPath + "/images/Image1.gif";
Label2.Text = Image1.ImageUrl;

Aber damit steht man schnell vor einem Problem: Liegt die Anwendung im Root, wie es beispielsweise standardmäßig beim Visual Studio 2010 Development Server der Fall ist, so erhält man als Ergebnis nur ein /. Die korrekte Zeile wäre dann also:

Image1.ImageUrl = Request.ApplicationPath + "images/Image1.gif";

Befindet sich die Seite aber in einem virtuellen Verzeichnis, so kommt das Verzeichnis ohne abschließenden / zurück:

Image1.ImageUrl = Request.ApplicationPath + "/images/Image1.gif";

Je nach Speicherort muss man also beim händischen Zusammensetzen der URL’s also den abschließenden Slash berücksichtigen, oder eben auch nicht.

Eine gute Lösung habe ich in einem Blog-Artikel von 2004 gefunden:

VirtualPathUtility.ToAbsolute("~/images/Image1.gif")

Damit überlässt man Asp.NET die korrekte Umsetzung, welche auch tadellos und ohne Experimente funktioniert!

Update:
Manchmal sieht man einfach den Wald vor lauter Bäumen nicht. Die kürzere Lösung ist Url.Content, welche ein vergleichbares Ergebnis liefert und auch in den Templates von Asp.NET MVC3 verwendet wird.

KategorienAllgemein, Microsoft .NET Tags: ,

Vererbung von Properties entfernt die Attribute

Für eine Erweiterung von meinem Quellcode hatte ich geplant, ein paar Properties mit Attributen zu versehen. Dabei sollten die Properties virtual sein. Die Abfrage der Attribute sollte dann mittels GetCustomAttributes geschehen. Leider hat das gar nicht funktioniert, was auch schon andere .NET-Programmierer feststellen mussten. Darum habe ich die Problemstellung extrahiert:

Zum Prüfen der Attribute wurde folgende Funktion verwendet:

static void CheckAttribute(MemberInfo memberInfo)
{
    var attributes = memberInfo.GetCustomAttributes(true);
    if (attributes.Length == 0) return;
 
    foreach (object attribute in attributes)
        Console.WriteLine("The type of the attribute in {1} is {0}.", 
            attribute, memberInfo.Name);
}

Weiterhin wurden zwei Klassen geschrieben, wo einmal eine abgeleitete Methode und eine abgeleitete Propertie mit Attributen versehen wurde:

public abstract class BaseClass
{
    [TestAttribute]
    public virtual String TestProperty { get; set; }
 
    [TestAttribute]
    public virtual void TestMethod() { return; }
}
public class DerivedClass : BaseClass
{
    public override string TestProperty { get; set; }
    public override void TestMethod() { base.TestMethod(); }
}
 
public class TestAttribute : Attribute
{
}

Zu guter Letzt wurde das ganze überprüft:

Console.WriteLine("BaseClass:");
CheckAttribute(typeof(BaseClass).GetProperty("TestProperty"));
CheckAttribute(typeof(BaseClass).GetMethod("TestMethod"));
 
Console.WriteLine("DerivedClass:");
CheckAttribute(typeof(DerivedClass).GetProperty("TestProperty"));
CheckAttribute(typeof(DerivedClass).GetMethod("TestMethod"));

Das Ergebnis fiel überraschend aus:

BaseClass:
The type of the attribute in TestProperty is TestAttrib.TestAttribute.
The type of the attribute in TestMethod is TestAttrib.TestAttribute.
DerivedClass:
The type of the attribute in TestMethod is TestAttrib.TestAttribute.

Wähernd die Methode TestMethod in beiden Klassen als mit dem Attribut ausgezeichnet, erkannt wurde, war dies bei der Eigenschaft TestProperty nicht der Fall. Um die Ursache zu finden, war ein Blick in die IL notwendig. Hier die Umsetzung der BaseClass, welche das Attribute in beiden Fällen korrekt definiert.

.method public hidebysig newslot virtual instance void TestMethod() cil managed
{
    .custom instance void TestAttrib.TestAttribute::.ctor()
}
 
.property instance string TestProperty
{
    .get instance string TestAttrib.BaseClass::get_TestProperty()
    .set instance void TestAttrib.BaseClass::set_TestProperty(string)
    .custom instance void TestAttrib.TestAttribute::.ctor()
}

Die Ableitung war aber überaschend umgesetzt. Während die Methode offensichtlich überschrieben wurde, wurde die Propertie quasi neu angelegt, aber ohne dem zugewiesenen Attribut.

.method public hidebysig virtual instance void TestMethod() cil managed
{
}
 
.property instance string TestProperty
{
    .get instance string TestAttrib.DerivedClass::get_TestProperty()
    .set instance void TestAttrib.DerivedClass::set_TestProperty(string)
}

Damit war das Ergebnis der Untersuchung: GetCustomAttributes hat definitionsgemäß auch die Ableitungen untersucht. Jedoch verlieren sich die Attribute beim Überschreiben von Properties. Eine Erklärung dafür habe ich bis jetzt nicht gefunden, ich konnte den Effekt aber in .NET 2.0 – 4.0 nachstellen.

dotnet-snippets.de wird 5 Jahre alt

24. April 2011 Keine Kommentare

Die Webseite dotnet-snippets.de wird 5 Jahre alt. Herzlichen Glückwunsch!

Wer die Seite noch nicht kennt, aber mit Microsoft .NET entwickelt, sollte einfach mal vorbeischauen. Es gibt viele kurze hilfreiche Codeschnipsel zu den meisten Problemchen, die man so im täglichen Leben hat.

KategorienAllgemein, Microsoft .NET Tags:

Alte Daten mittels Powershell löschen

13. April 2011 1 Kommentar

In einem Backup-Ordner stand die Aufgabe an, alte Dateien automatisch löschen zu lassen. Dazu wurde folgendes Skript eingesetzt:

Get-ChildItem -Path . |
  Where-Object -FilterScript {($_.LastWriteTime -lt [DateTime]::Now.AddDays(-7))} |
  Remove-Item -force

Zuerst werden alle relevanten Dateien ermittelt. Danach wird auf diese Liste ein Filter gesetzt, der nur Dateien ermittelt, die älter als 7 Tage sind. Hierzu wird eine Rückgriff auf DateTime aus dem Net-Framework genutzt. Es bietet mit Now und AddDays die gewünschte Funktionalität, von jetzt 7 Tage nach hinten zu gehen. Als letzten Schritt muss die ermittelte Liste lediglich an Remove-Item weitergereicht werden.

Erweiterung von INotifyPropertyChanged (PostSharp)

17. März 2011 4 Kommentare

Mit PostSharp bekommt man eine mächtige Möglichkeit, Code mittels Attributen aufzupeppen. So wird man auch in die Lage versetzt, wiederkehrenden Code zu vermeiden. Üblicherweise sieht nämlich beispielsweise eine Implementation von System.ComponentModel.INotifyPropertyChanged wie folgt aus:

public class Test: System.ComponentModel.INotifyPropertyChanged
{
    private string _myProp;
    public string MyProp
    {
        get { return _myProp; }
        set
        {
            if (_myProp == value) return;
            _myProp = value;
            OnPropertyChanged("MyProp");
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    public void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Verwendet man jedoch PostSharp als Referenz in seinem Projekt und die Klasse NotifyPropertyChangedAttribute aus

{ProgramFiles}\PostSharp 2.0\Samples\.NET Framework 3.5\CSharp\NotifyPropertyChanged

kann man seinen Code merklich verkürzen:

[NotifyPropertyChanged]
public class TestBase
{
    public string MyProp { get; set; }
}

Problem
Leider ergibt letztere Implementation das Problem, dass ich die Kontrolle über das Auslösen von Events vollständig an das Attribut NotifyPropertyChanged abgebe. Da der Code für das Event nachträglich in das Compilat injiziert wird, ergibt sich keine Möglichkeit, das Event PropertyChanged manuell auszulösen, da .NET das Auslösen von Events nur innerhalb der implementierenden Klasse erlaubt. Versuche, Zugriff auf das Event durch Vorimplementierung des Interfaces zu erhalten, helfen leider auch nicht, da der durchs Attribut erzeugte Code in einer Property der Klasse zusätzlich angelegt wird. Dies kann man leicht mit dem Reflektor prüfen:

public string MyProp
{
    [CompilerGenerated]
    get
    {
        return this.<get_MyProp>();
    }
    [CompilerGenerated]
    set
    {
        LocationInterceptionArgsImpl<string> CS$0$1 = new LocationInterceptionArgsImpl<string>(this, Arguments.Empty);
        CS$0$1.Location = <>z__Aspects.l3;
        CS$0$1.TypedBinding = <MyProp>c__Binding.singleton;
        CS$0$1.TypedValue = value;
        this.<>z__aspect0.OnPropertySet(CS$0$1);
    }
}

Das Setzen der per Attribut erweiterten Property wird beispielsweise komplett auf z__apect0 umgeleitet. Und an genau dieses z__apect0 ist ja zur Entwurfszeit noch nicht verfügbar (das ist ja der Sinn des Attributes).

Umgehen konnte ich das Problem mit einer kleiner Erweiterung des NotifyPropertyChangedAttribute:

[ImportMember("ManualPropertyChanged", IsRequired = false,
		Order = ImportMemberOrder.AfterIntroductions)]
public Event<PropertyChangedEventHandler> ManualPropertyChanged;
 
public override void RuntimeInitializeInstance()
{
	base.RuntimeInitializeInstance();
	if (ManualPropertyChanged != null)
		ManualPropertyChanged.Add((sender, args) => 
		{ if (PropertyChanged != null) PropertyChanged(Instance, args); });
}

Bietet meine Klasse, welche das NotifyPropertyChangedAttribute ausweist, folgendes Ereignis an:

public event PropertyChangedEventHandler ManualPropertyChanged;

so abonniert das Attribut dieses Ereignis und sorgt beim Auslösen von ManualPropertyChanged automatisch für eine Weiterleitung an das Ereignis PropertyChanged.
kick it on dotnet-kicks.de

KategorienMicrosoft .NET Tags: ,

Probleme beim Erstellen einer Solution

10. Oktober 2010 Keine Kommentare

Auf meinem Notebook wurde ich beim Erstellen einer Solution völlig von folgender Fehlermeldung überrascht:

The OutputPath property is not set for this project

Nach einer langen Fehlersuche bin ich dank dieses Beitrags dem Fehler auf die Spur gekommen. Rechner von HP haben (zumindest in meinem Falle) die Umgebungsvariable “Platform = HPD”. Diese muss gelöscht werden, damit Visual Studio die Solutions sauber erstellt. Ich hoffe, dass die HP-Programme danach trotzdem ohne Fehler laufen.

Probleme bei der Verwendung des VisualBasic.PowerPacks

8. September 2010 Keine Kommentare

In einer Visual Studio 2008 Anwendung habe ich die Shapes aus dem VisualBasic PowerPack verwendet, das im Lieferumfang von Visual Studio 2008 SP1 bereits enthalten ist. Nun hat sich ein Anwender aber mit folgender Fehlermeldung gemeldet, obwohl die korrekte Version des PowerPacks im Programm-Verzeichnis mit ausgeliefert wurde:

System.TypeLoadException:
 Could not load type 'Microsoft.VisualBasic.PowerPacks.ShapeContainer'
 from assembly 'Microsoft.VisualBasic.PowerPacks.Vs,
 Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Nach einiger Suche war die Ursache klar. Visual Studio 2008 (ohne SP1) kennt diese DLL ebenfalls, nur leider noch ohne Shapes. Unglücklicherweise haben beide DLLs (mit und ohne SP1) die Version 9.0.0.0. Somit wird, sofern im GAC die DLL gefunden wird, automatisch immer die DLL aus dem GAC geladen. Die einzige Lösung ist darum, die DLL im GAC zu löschen oder zu aktualisieren:

Das geht, Admin-Rechte vorrausgesetzt, erstaunlich einfach per Drag & Drop.

Genauigkeit von System.Double im Zusammenspiel mit DirectX

3. September 2010 Keine Kommentare

Beim Prüfen von Quelltext sind mir wiederholt Ungenauigkeiten mit double Zahlen aufgefallen, die schon in der 8. Stelle auftraten. Nachdem ich Ausgeschlossen hatte, dass irgendwo Single-Konvertierungen stattfinden, habe ich das Problem genauer unter die Lupe genommen. Konkret ist mir aufgefallen, dass die double Rechnung 4.0 * 25.4 eigentlich 101.6 ergeben müsste, in der Zielvariable stand aber 101.59999847412109. Extern nachstellbar war das Problem anfangs nicht.

Reproduzieren ließ sich das Verhalten erst mit folgender Funktion:

private void TestDouble()
{
    var pres = new Microsoft.DirectX.Direct3D.PresentParameters
         {
             Windowed = true,
             SwapEffect = Microsoft.DirectX.Direct3D.SwapEffect.Discard,
             AutoDepthStencilFormat = Microsoft.DirectX.Direct3D.DepthFormat.D16
         };
 
    new Microsoft.DirectX.Direct3D.Device(
        0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,
        Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing, pres);
 
    double value = 4;
    double value2 = value * 25.4;
    Debug.Assert(101.6 == value2);
}

Das Erzeugen der 3D-Welt mittel Managed DirectX setzt standartmäßig die Genauigkeit von Double-Zahlen auf einfache Genauigkeit (entspricht Single). Nachdem diese Ursache gefunden war, fand ich hier:

http://blogs.msdn.com/b/tmiller/archive/2004/06/01/145596.aspx

schnell eine Problemlösung. Wenn man mit leichten Geschwindigkeitseinschränkungen leben kann, ist nur eine kleine Änderung beim Erzeugen vom Device notwendig:

    new Microsoft.DirectX.Direct3D.Device(
        0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,
        Microsoft.DirectX.Direct3D.CreateFlags.SoftwareVertexProcessing | 
        Microsoft.DirectX.Direct3D.CreateFlags.FpuPreserve, pres);

Der zusätzliche Parameter Microsoft.DirectX.Direct3D.CreateFlags.FpuPreserve sorgt dafür, dass DirectX die Genauigkeit der Double-Zahlen so lässt, wie das Standardmäßig der Fall ist.