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.

PSSecurityException, obwohl in Powershell die Ausführung von Skripten erlaubt wurde

13. April 2011 2 Kommentare

Beim Ausführen einer Powershell-Datei erhielt ich im Protokoll neulich folgende bekannte Fehlermeldung, obwohl Set-ExecutionPolicy auf RemoteSigned gesetzt war:

Die Datei "...test.ps1" kann nicht geladen werden, da die Ausführung
 von Skripts auf diesem System deaktiviert ist. Weitere
 Informationen erhalten Sie mit "get-help about_signing".
Bei Zeile:1 Zeichen:2
+ . <<<<  '...test.ps1'
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

Die Lösung lag letztendlich darin, dass das entsprechende Skript auf einem 64Bit-System als 32Bit-Task aufgerufen wurde. Es war ausreichend, Set-ExecutionPolicy unter Powershell (x86) ebenfalls aufzurufen und entsprechend zu setzen.

Fehlercode 8004402F beim Installation von Sp1 auf Windows 7

23. März 2011 5 Kommentare

Seit einigen Tagen versuche ich das Servicepack 1 (KB976932) von Windows 7 zu installieren. Immer ohne Erfolg. Der Vorgang startet ganz normal, aber beim Neustart des Rechners läuft die Konfiguration zügig auf 99% hoch, bevor ein Fehler auftritt und alles wieder rückgängig gemacht wird.

Da das ganze ca. eine Stunde dauert, ist jeder Test schwierig und einfach aufwändig. Im Ereignisprotokoll oder WindowsUpdate.log waren leider keine sinnvollen Hinweise zu finden, lediglich kurz nach dem Hochfahren wird der Fehlercode 8004402F angezeigt. Ein ähnliches Problem brachte dann auch noch die Installation vom Internet-Explorer 9.

Glücklicherweise habe ich heute einen recht alten Blog-Beitrag für Windows Vista gefunden, der dann auch half. Mein %TEMP% Ordner war auf Laufwerk D umgestellt. Dieses Laufwerk ist aber ein verschlüsseltes TrueCrypt-Laufwerk, was beim Neustart nicht zur Verfügung steht. Ich habe diesen Ordner wieder auf eine unverschlüsselte Festplatte umgelenkt, und die Installation war erfolgreich.

Schade, dass hier nirgends ein entsprechender Fehler ausgegeben wird. Beim Starten der Installation hätte mir der Hinweis auf einen nicht verfügbaren Temp-Ordner viele Stunden der Suche erspart.

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: ,

Windows 7, Delphi und das Datumsformat

18. November 2010 Keine Kommentare

Vor kurzem hatten wir auf einem neuen Windows 7 Rechner das Problem, dass die Regionaleinstellungen in der Anwendung nach EN-en aussahen, obwohl DE-de aktiviert war. Andere Windows 7 Rechner zeigten die Probleme nicht. Nach einiger Suche habe ich in einem Forum zwei mögliche Workaround ausmachen können:

1) Man schaltet die Ländereinstellungen auf irgendwas anderes, und dann wieder zurück auf die Wunschsprache.

2) Man fügt in sein Delphi Projekt folgende Unit ein und trägt sie in irgendeiner uses ein.

unit Win7;
 
interface
 
uses
 SysUtils, Windows;
 
implementation
 
initialization
 SetThreadLocale(LOCALE_USER_DEFAULT);
 GetFormatSettings;
end.

Speziell nach Sichtung dieser zwei Zeilen bleibt aber die Frage zurück, ob das ein Problem von Windows 7 oder ein Problem von alten Win32 Anwendungen ist, die sich vielleicht unbewußt nicht an die Vorgaben von Microsoft halten.

Robocopy und seine Rückgabewerte

3. November 2010 Keine Kommentare

Im Rahmen einer Datensicherung mit Acronis sollte das Sicherungsarchiv auf Wechselmedien gespiegelt werden. Dazu bietet sich das Kommandozeilentool Robocopy an, das seit Jahren zum Lieferumfang von Windows gehört und extrem mächtig ist. Grundsätzlich hat es funktioniert, aber der Abschlusstask mit Robocopy wurde von Acronis immer als fehlerhaft bewertet. Nach einer Weile war die Ursache klar: Kommandozeilenbefehle liefern als Rückgabewert üblicherweise

= 0: kein Fehler
> 0: Fehler

Robocopy belegt aber die Codes 0-7 mit Erfolgsmeldungen. Um diesem Verhalten gerecht zu werden, Acronis im Erfolgsfalle aber Null zurückzugeben, wurde eine kleine Batchdatei geschrieben, welche die ErrorLevel 0-7 in 0 umwandelt und in allen anderen Fällen die 8 zurückgibt (der reale Rückgabewert ist bei Fehlern egal, da Robocopy eine Logdatei schreiben kann).

robocopy "Quellpfad" "Zielpfad" "*.TIB" /PURGE
if NOT errorlevel 8 (EXIT /B 0)
EXIT /B 8

Interessant ist hier besonders die Abfrage

if NOT errorlevel 8

, welche quasi für

if errorlevel < 7

steht. Wer nicht so oft Batch-Dateien schreibt, wird von diesem Verhalten oft überrascht.

KategorienAllgemein 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.

Probleme bei der Verwendung von System.Double

30. August 2010 Keine Kommentare

Bezüglich der Verarbeitung von Komma-Werten habe ich heute wieder einge Erkenntnisse gewonnen, die teilweise auch aus der verbesserten Dokumentation vom Microsoft.NET Framework 4 her rührten. Dort ist unter anderem zum Typ Double folgendes zu lesen:

Bei Verwendung einer Gleitkommazahl könnte ein Wert möglicherweise nicht wiederhergestellt werden.

Das Problem verdeutlicht folgendes gekürztes Beispiel aus der MSDN:

double fromLiteral = -4.42330604244772E-305;
double fromParse = Double.Parse("-4.42330604244772E-305");
 
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
 
// On 32-bit versions of the .NET Framework, the output is:
//    Double value from literal:        -4.42330604244772E-305
//    Double value from Parse method:   -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
//    Double value from literal:      -4.4233060424477198E-305
//    Double value from Parse method:   -4.42330604244772E-305

Das die hinteren Stellen einer Double-Zahl Ungenauigkeiten unterliegen, war mir schon länger klar. Das der selbe Code auf verschiedenen Systemen aber andere Genauigkeiten erzielen kann, war eine neue Erkenntnis, die bis dato nur als ungeäußerte und nicht beweisbare Vermutung im Raum stand.

Um das Problem zu umgehen, habe ich zwei Extensions für mich geschrieben:

public static bool NearZero(this double value)
{
    return Math.Abs(value) < 1e-12;
}
public static bool NearValue(this double value, double compareValue)
{
    if (value.NearZero() && compareValue.NearZero()) return true;
 
    return Math.Abs(value - compareValue) < 
        Math.Min(Math.Abs(value), Math.Abs(compareValue)) * 1e-12;
}

Mit der ersten prüfe ich auf 12 Stellen hinter dem Komma, ob die Zahl fast Null ist. Mit der zweiten Methode kann ich simple 2 Werte auf annähernde Gleichheit überprüfen.