Thomas Wölfers Baustatik-Blog

Thomas Wölfers Baustatik-Blog

Symptom: Visual Studio 2005 stürzt schlagartig ab


Sowas kann man ganz leicht produzieren, nur wenn es dann passiert, kann man sich natürlich nicht mehr dran erinnern. Ich zumindest nicht.

Folgende Situation: Man hat eine Solution mit 2 Windows.Forms Projekten. Im einem Projekt baut man ein UserControl, im anderen ist eine Form, die das UserControl verwendet. Man wechselt in das Projekt mit dem User Control, weil man zum Beispiel ein zusätzliches Label, das das Control enthält, herausführen will. Dazu baut man folgenden Code ein:

private Label label;
public Label Label
{
   get { return this.Label; }
}

Das übersetzt sich ganz prima. Jetzt geht man zurück in das andere Projekt, und öffnet die Form, die das UserControl verwendet, um dort den Text des Labels zu setzen. Resultat: Visual Studio verabschiedet sich direkt und vollständig aus dem Speicher.

Grund: Der Property-Getter fürs Label wird vom Forms-Designer aufgerufen - und verursacht dann einen Stack-Überlauf, mit dem Visual Studio nicht klarkommt. Es müsste halt "return this.label" heissen...


Die Sache mit dem TypeDescriptionProvider


Seit .Net 2.0 gibt es mehr als eine Methode, einen CustomTypeDescriptor an eine Klasse zu hängen: Vorher gab es nur die Möglichkeite ICustomTypeDescriptor zu implementieren - seitdem gibt es auch eine weitere Methode, und die basiert auf dem TypeDescriptionProvider.

Die Dokumentation dazu ist allerdings, gelinde gesagt, nicht besonders aussagekräftig. Was man zunächst wissen muss ist die Tatsache, das man einen TypeDescriptionProvider über zwei Methoden mit einem Typ verknüpfen kann.

  1. Per Attribut an der Klassen und
  2. Dynamisch zur Laufzeit

Für den 2. Fall gibt es bei Partho eine ganz gute Beschreibung. Will man die Sache aber als Attribut benutzen, wird es unübersichtlich. (Ich habe jedenfalls heute relativ lange damit gekämpft.). Folgendes ist zu tun:

1. Die Klasse wird per TypeDescriptionProvider-Attribut mit dem Provider verknüpft.

[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MyClass
{

2. Jetzt braucht man den Provider. Um den zu bauen ist es wichtig zu wissen, das die Provider als Stack vorliegen - man kann also mehrer Schichten von Providern übereinander lagern. Beim erzeugen des Providers muss der "Parent" Provider angegeben werden - sonst endet man hinterher ganz ohne Metadaten. Ist man in der ersten Schicht, dann ist der "Parent" Provider der ursprüngliche des behandelten Typs. Die Basisklasse des eigenen Providers ist einfach TypeDescriptionProvider. Die Klasse muss mindestens eine Methode überladen, und zwar die, die den eigentlichen CustomTypeDescriptor liefert:

public sealed class MyTypeDescriptionProvider : TypeDescriptionProvider
{
   public MyTypeDescriptionProvider()
   :
base(TypeDescriptor.GetProvider(typeof(MyClass)))
   {}

   public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
   {
      return new MyTypeDescriptor(base.GetTypeDescriptor(objectType, instance));
   }
}

3. Jetzt braucht es nur noch den Provider selbst. Wenn der zum Beispiel GetProperties() überladen soll, dann ist es dafür nur wichtig, die "ursprünglichen" Properties nicht per TypeDescriptor.GetProperties() zu erfragen (sonst gibts logischerweise einen Stack-Überlauf (logischerweise: Ist wirklich logisch, hat mich aber trotzdem mindestens eine Stunde gekostet :-)), sondern die passende Methode der Basisklasse. Ach ja: Auch der eigene CustomTypeDescriptor hat ein "Parent". Das ganze sieht dann so aus:

    public sealed class MyTypeDescriptor : CustomTypeDescriptor
    {
        public MyTypeDescriptor(ICustomTypeDescriptor parent)
            : base(parent)
        {
        }

        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection result = base.GetProperties(attributes);
            // hier wäre eine gute stelle, an den props rumzufummeln
            return result;
        }

    }


MSDN Reader verfügbar


Vom MSDN Magazin gibt es nun den MSDN Reader. Dabei handelt es sich um eine WPF Anwendung (mit Quellcode), mit der man die Online-Variante des Magazins "hübscher" lesen können soll. Und in der Tat lesen sich die Artikel besser als per Browser - allerdings fand ich die Navigation ein wenig schlechter: Das mag Gewöhnungssache sein - ich werde das auch jeden Fall noch eine Zeitlang ausprobieren. "Auf Anhieb" war ich allerdings ein wenig verloren - irgendwie habe ich den Eindruck, das die Sache mit dem Reader nicht übersichtlicher wird.


LookupAccountName API - woher man den Namen bekommt


Es gibt einige "bekannte" (well-Known) SIDs - so zum Beispiel die der Gruppe "Jeder" (Jedermann, Everyone, World). Nun ist es so, das man für verschiedene APIs, so zum Beispiel LookupAccountName den zum SID passenden String benötigt. Also eben "jeder". Dummerweise sind diese Strings aber solche, die lokalisiert werden: Bei unterschiedlichen Sprachversionen von Windows, muss man da auch den zur Sprache passenden String übergeben. Mit anderen Worten: "everyone" funktioniert eben nicht "einfach so".

Nun ist es deutlich schwieriger an diesen String zu gelangen, als man meinen möchte. Im Prinzip gibt es zwar die API LookupAccountSid die trotz Ihres Namens nicht die SID zu einem Account, sondern den Account-Name (und die Domäne) zu einer SID liefert. Die ist aber ein bisschen schwer zu bestücken. Wer aber auch einmal das Problem hat, den übersetzten Namen für einen "well known" SID zu brauchen, der kann mit folgendem Beispielcode vielleicht etwas schneller zu Ziel kommen: Hier wird (ohne jegliche Fehlerbehandlung) der Name der "Everyone" Gruppe ermittelt. Viel Vergnügen damit. :-)

class EveryoneGroup
{
public:
 EveryoneGroup()
 {
  PSID pEveryoneSID = NULL;
  SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;

  if(AllocateAndInitializeSid(&SIDAuthWorld, 1,
       SECURITY_WORLD_RID,
       0, 0, 0, 0, 0, 0, 0,
       &pEveryoneSID))
  {
   SID_NAME_USE snu;
   char *name = 0;
   char *domain = 0;
   unsigned long namesize = 0;
   unsigned long domainsize = 0;

   LookupAccountSid(0, pEveryoneSID, name, &namesize, domain, &domainsize, &snu);
 
   int ret = GetLastError();
   if (ret == ERROR_INSUFFICIENT_BUFFER)
   {
    name = (char *) HeapAlloc(GetProcessHeap(), 0, namesize);
    if( name)
    {
     domain = (char *) HeapAlloc(GetProcessHeap(), 0, domainsize);
     if( domain)
     {
      if ( LookupAccountSid(0, pEveryoneSID, name, &namesize, domain, &domainsize, &snu))
      {
       groupName = name;
          HeapFree( GetProcessHeap(), 0, domain);
          HeapFree( GetProcessHeap(), 0, name);
          FreeSid( pEveryoneSID);
      }
     }
    }
   }
  }
 }
  
 CString Name()
    {
  return groupName;
 }
  
private:
 CString groupName;

};


Praktikum, Nebenjob oder Diplomarbeit bei D.I.E.


Habe ich schon länger nicht mehr drauf hingewiesen: Wir suchen für die Münchner Niederlassung einen Schüler/Studenten der Spass am programmieren hat und ein Praktikum machen möchte (wir sind eine offizielle Praktikumsstelle der FH, es gibt aber auch keine Probleme mit der TU) oder einen Nebenjob sucht. Wer seine Diplomarbeit bei D.I.E. machen möchte - auch kein Problem, wäre nicht die erste. :-)

Im Fall von Interesse: Am besten direkt bei mir (tw@die.de) melden.


Blockierte HTML-Seiten


Passiert mir häufiger: Ich möchte mir den Quellcode einer HTML-Seite ansehen, klicke mit der rechten Maustaste rein, und bekomme als Reaktion nur ein Popup mit dem Hinweis, die Seite sei "gesichert" (oder etwas in der Art). Das ist natürlich blödsinn, denn die Seite ist ja schon auf meinem Rechner - da gibt es nichts mehr zu sichern. Es wird nur etwas schwieriger dranzukommen. Unabhängig von den diversen anderen Möglichkeiten hat Dana auch noch eine schöne zu bieten.

Bei der nächsten blockierten Seite kann man einfach folgendes JavaScript in die URL-Zeile eingeben. Praktisch.

javascript:window.open( '', '', '' ).document.write( '<textarea cols=80 rows=40>' + document.body.parentNode.innerHTML + '</textarea>' );


Beispielmakro: Knotenkoordinaten aus einer Datei auslesen


Auf dem Anwenderseminar in München war ich von einem Kunden darauf angesprochen worden, wie aufwendig es wäre, ein Makro zu bauen, mit dem man Knoten auf Basis von Koordinaten aus einer Datei erzeugen kann. Leider habe ich vergessen, wer das gefragt hat. Das Makro kann ich aber natürlich trotzdem kurz zeigen. Im Makro wird davon ausgegangen, das sich die Datei mit den Knotenkoordinaten im Pfad "c:\temp\knotenkoordinaten.dat" befindet. Das kann man natürlich ändern.

Die Datei enthält die Koordinaten so, das pro Zeile ein X, Y und Z angegeben wird. Die Werte werden dabei mit einem Leerzeichen von einander getrennt.

Das Makro liesst die Datei Zeile für Zeile ein. Jede Zeile wird dann in ihre X,Y und Z-Bestandteile zerlegt. Damit werden dann die Knoten erzeugt und ins Dokument gestellt.

using System;
using System.IO;
using DIE.Applications.Faltwerk.Objects.Nodes;

using DIE.Framework.ApplicationModell.Commands;

namespace MeineMakros
{
    public class KnotenAusFile : UserCommandBase
    {
        public KnotenAusFile()
            : base(@"KnotenAusFile", @"Erzeugt Knoten aus Koordinanten in einer Datei")
        {
        }

        public override void Execute(IExecuteCommandContext context)
        {
            StreamReader reader = new StreamReader("c:\\temp\\knotendaten.dat");

            int name = 1;

            while (!reader.EndOfStream)
            {
                string line = reader.ReadLine();
                string[] xyz = line.Split(' ');

                double x = double.Parse(xyz[0]);
                double y = double.Parse(xyz[1]);
                double z = double.Parse(xyz[2]);

                Node node = new Node();
                node.X = x;
                node.Y = y;
                node.Z = z;

                node.ObjectName = name.ToString();
                name++;

                context.TargetDocument.AddObject(node);
            }
        }
    }
}




Baustatik Demoversion ausprobieren »