Kategorie: Tech

Der am häufigsten gelesene Artikel dieses Blogs ist “Belegte Ports anzeigen” unter Windows. Da ich nun seit einigen Jahren Mac-Nutzer bin fand ich es an der Zeit, auch eine Variante für dieses Betriebssystem zu schreiben. Wer also immer schon mal wissen wollte, welche Prozesse auf dem Mac welches Ports belegten, findet dies mit folgendem Kommando am Terminal heraus:

sudo lsof -i -P | grep -i LISTEN

Ohne das vorangestellte “sudo”, das eine Eingabe des Administrator-Kennworts erfordert, liefert der Befehl nur Prozesse des aktuell angemeldeten Nutzers. Ergebnis ist in jedem Fall eine Liste der folgenden Art:

Belegte Ports anzeigen - Screenshot

Beim Fernsteuern von Office-Anwendungen ist generell Vorsicht angesagt. Das gilt aber insbesondere, wenn die Fernsteuerung aus .Net heraus erfolgt. Wieviel Vor- und Umsicht tatsächlich notwendig ist, mußten wir in der letzten Phase vor Auslieferung der ersten Version einer WinForms-Anwendung kürzlich schmerzlich erfahren. Um diesen Schmerz anderen Entwicklern in Zukunft zu ersparen, hier ein paar “Lessons Learned”:

COM/COM Interop – Kurze Einführung

COM ist lange, lange Jahre Microsofts Objektmodell gewesen und was die Menge an existierendem COM-Code anbelangt auch sehr erfolgreich. Das später dazugekommene DCOM zur verteilten Komponentenentwicklung (als Gegenstück zu CORBA) war aus mancherlei Gründen weniger erfolgreich und Microsoft bastelte bald an einer neuen Variante. Diese neue Variante entwickelte sich intern schnell weiter und es wurde daraus das, was wir heute unter der “.Net-Plattform” kennen. Es ist also kein Wunder, daß Microsoft einige Anstrengung unternommen hat, die alte COM-Welt nicht einfach zu vergessen und damit die unzähligen existierenden Komponenten unbrauchbar zu machen. Stattdessen ist tief in .Net die Möglichkeit integriert, COM-Objekten (beinahe) so zu verwenden wie native .Net-Komponenten. Dieser Mechanismus wird “COM Interop” genannt.
Es gibt jedoch einen entscheidenden Unterschied zwischen beiden Modellen: .Net-Code ist sogenannter “managed Code”, wird von einer Runtime, der sogenannten CLR (Common Language Runtime) verwaltet, was in etwa der Java VM entspricht. Zu den Aufgaben dieser Runtime gehört unter anderem die Speicherverwaltung, auch Garbage Collection (GC) genannt. In .Net (wie in Java) hat man als Entwickler wenig Kontrolle darüber, wie, wann und ob überhaupt der GC nicht mehr benötigte Objekte freigibt und damit auch von diesen benutzte Ressourcen. Ganz anders bei COM. Der überwiegende Teil der COM-Komponenten wurde sicher in C/C++ erstellt, also in unmanaged Code. Hier ist der Entwickler selbst für Speicher-Allokation und -Freigabe zuständig. Treffen diese beiden Welten aufeinander stellt sich die Frage, wer denn nun die Verwaltung der COM-Objekte übernimmt. Nach wie vor kann man das als Entwickler von .Net-Code der CLR bzw. dem GC überlassen. Aber da sind wir auch schon bei dem nächsten Problem:

Wenn man in .Net eine Referenz auf eine COM-Bibliothek anlegt, wird ein sogenannter RCW (Runtime Callable Wrapper) erstellt, eine Art Proxy für das COM-Objekt. Hier findet das Marshalling der Daten statt. Pro COM-Objekt gibt es immer nur einen RCW, unabhängig von der Anzahl der Instanzen. Hierbei spielt das sogenannte “Appartment Model” eine Rolle. Unter COM gibt es verschiedener solcher Modelle, im wesentlichen die beiden MTA und STA, Multi Threaded Appartment und Single Threaded Appartment. Das Appartment besagt, ob eine COM-Komponente von verschiedenen Threads (MTA) oder nur von einem einzigen (STA) angesprochen werden kann. Um diese beiden Varianten in .Net abzubilden, hat dort jeder Thread ebenfalls eines der beiden Appartments, default ist “MTA”. Durch ein Attribut an der Main()-Methode wird das Appartment aber eigentlich immer auf STA gestellt, um Probleme mit eben diesen STA-COM-Objekten zu vermeiden.

Pitfalls

Solange man direkt aus dem Haupt-Thread mit COM-Objekten arbeitet und das nicht exzessiv, hat man eigentlich keine Probleme. Die eigentliche Arbeit wird einem von der CLR bzw. dem RCW abgenommen. Fängt man jedoch an, etwas komplexere Aufgaben zu erledigen und dies auch durch Einsatz von Multi-Threading wird es schnell unangenehm. Bei den ersten etwas umfangreicheren Tests einer unserer WinForms-Anwendungen zeigte sich, daß es zu sehr unschönen Effekten kommen kann. In bestimmten Fällen werden dort zwei Hintergrund-Threads erzeugt, die jeweils über COM Outlook fernsteuern. Leider stürzte die Anwendung dabei auf einigen Rechnern regelmäßig nach einigen hundert Aktionen ab. Manchmal mit einer “MemoryAccessViolation” und manchmal einfach ohne irgendeine Nachricht, da war die Anwendung einfach mal eben weg. Wenn wir das Glück hatten, zumindest eine Fehlermeldung zu bekommen, deutete der Stacktrace auf ein Problem in der Anzeigekomponente, genauer gesagt im TreeView hin. Tagelange Suche nach Problemen dort zeigte aber keinerlei Auffälligkeiten in diesem Teil des Codes.
Da das Problem auf zumindest einem Rechner mehr oder weniger deterministisch auftrat deutete das Bauchgefühl relativ schnell auf COM-Interop hin. Nach tagelangen Recherchen ergaben folgende Maßnahmen eine Lösung:

  • Sicherstellung, daß immer nur ein Thread mit den Outlook COM-Objekten arbeitet (mittels “lock()”-Statements)
  • Sicherstellung, daß der ausführende Thread das Appartment-Modell des COM-Objektes verwendet (in diesem Fall STA). Daraus folgt auch, daß man leider keine ThreadPools verwenden kann, da diese mit MTA-Threads arbeiten und man das Appartment nachträglich nicht ändern kann.
  • Sicherstellung, daß jedes im Code erzeugte COM-Objekt (durch “new”) durch einen Aufruf von “Marshal.ReleaseComObject()” freigegeben wird, bevor der GC aktiv werden muß. Sicherstellen kann man das durch “try/finally”-Blöcke.

Ich hoffe, diese Informationen werden irgendwann einmal jemandem helfen, die Fehler, die wir gemacht haben, zu vermeiden. Vielleicht helfen sie ja mir selbst beim nächsten Projekt… 🙂

Hin und wieder ist es notwendig, in einem Ordner Dateien gleichen Namens zu speichern. Da das unter Windows (und den meisten anderen Betriebssystemen auch) nicht geht, muß man sich was einfallen lassen. Gängige Praxis vieler Anwendungen ist es dabei, dem Dateinamen ein Zeichen mit einer Zahl anzuhängen, wenn es den Originalnamen schon gab. Z.B. wird die Datei “Neues Dokument.txt” zu “Neues Dokument~1.txt”, dann “Neues Dokument~2.txt” usw.

Der folgende Code-Snippet erzeugt für einen gegebenen Dateinamen genau solche einen eindeutigen Namen mit der kleinstmöglichen Zahl.

public static String getUniqueFileName(String tempFileName, String extension, String extensionSeparator, String suffixSeparator)
{
  String tempFileNameNew; 
  if (extension == null || extension.length() == 0)
  {
    tempFileNameNew = tempFileName;
  } 
  else
  {
    tempFileNameNew = tempFileName + extensionSeparator + extension;
  } 

  File file = new File(tempFileNameNew);
  int suffix = 1; 

  while (file.isFile())
  {
    if (extension == null || extension.length() > 0)
    {
      tempFileNameNew = tempFileName + suffixSeparator + String.valueOf(suffix);
    }
    else
    {
      tempFileNameNew = tempFileName + suffixSeparator + String.valueOf(suffix) + extensionSeparator + extension;
    } 

    file = new File(tempFileNameNew);
    suffix++;
  }

  return tempFileNameNew;
}

Möglicher Aufruf wäre also:

String newFilename = getUniqueFileName("Neues Textdokument", "txt", ".", "~");

Immer mal wieder ist es notwendig zu wissen, welche Ports auf einem Rechner abgehört werden und damit belegt sind. Um das herauszufinden, kann man sich unter Windows (für Mac-Nutzer hier) einfacher DOS-Boardmittel bedienen. Der folgende Befehl auf der Kommandozeile ausgeführt liefert eine Liste aller abgehörten Ports:

netstat -ano | find /i “Abhören”

In meinem Fall resultiert daraus die folgende Ausgabe:

C:\Programme\Support Tools>netstat -ano | find /i "Abhören"
TCP    0.0.0.0:21          0.0.0.0:0  ABHÖREN         1408
TCP    0.0.0.0:25          0.0.0.0:
0 ABHÖREN         1408
TCP    0.0.0.0:80          0.0.0.0:
0 ABHÖREN         1408
TCP    0.0.0.0:135         0.0.0.0:
0 ABHÖREN         1048
TCP    0.0.0.0:443         0.0.0.0:
0 ABHÖREN         1408
TCP    0.0.0.0:445         0.0.0.0:
0 ABHÖREN         4
TCP    0.0.0.0:987         0.0.0.0:
0 ABHÖREN         11344
TCP    0.0.0.0:1048        0.0.0.0:
0 ABHÖREN         1408
TCP    0.0.0.0:2869        0.0.0.0:
0 ABHÖREN         1924
TCP    0.0.0.0:3389        0.0.0.0:
0 ABHÖREN         992
TCP    0.0.0.0:5051        0.0.0.0:
0 ABHÖREN         1832
TCP    0.0.0.0:19226       0.0.0.0:
0 ABHÖREN         3952
TCP    0.0.0.0:23761       0.0.0.0:
0 ABHÖREN         664
TCP    127.0.0.1:1082      0.0.0.0:
0 ABHÖREN         3308
TCP    127.0.0.1:1118      0.0.0.0:
0 ABHÖREN         2948
TCP    127.0.0.1:4664      0.0.0.0:
0 ABHÖREN         4020
TCP    127.0.0.1:6083      0.0.0.0:
0 ABHÖREN         3592
TCP    127.0.0.1:31595     0.0.0.0:
0 ABHÖREN         304
TCP    192.168.136.1:139   0.0.0.0:
0 ABHÖREN         4
TCP    192.168.154.1:139   0.0.0.0:
0 ABHÖREN         4
TCP    192.168.213.22:139  0.0.0.0:
0 ABHÖREN         4

Unter englischem Betriebssystem muß man “abhören” durch “listening” ersetzen. Die letzte Spalte der Ausgabe gibt dabei (Windows XP oder höher vorausgesetzt) die ID des Prozesses an, der den jeweiligen Port blockiert. Um nun herauszufinden, welche Anwendung dahintersteckt, öffnet man den Taskmanager (taskmgr) und läßt sich auf dem Tab “Prozesse” die Prozess-ID mit ausgeben. Dazu wählt man im Menü “Ansicht” den Punkt “Spalten auswählen” und markiert “PID”. So läßt sich im obigen Beispiel herausfinden, daß hinter der PID 1408 der Prozess “inetinfo.exe” steckt, also der Micosoft IIS. Wer hätte das gedacht… 😉

UPDATE: Für Mac-Nutzer gibt es ebenfalls eine Lösung: “Belegte Ports anzeigen – Mac Version

Aus einem privaten Projekt heraus entsprang die Anforderung, die Entfernung zwischen zwei (deutschen) Postleitzahlen zu berechnen. Dank der freien Geo-Informationen des OpenGeoDb-Projektes (http://opengeodb.hoppe-media.com/index.php) und einer einfachen Formel (http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c5115/) war die Umsetzung relativ einfach. Die Einfachheit der Methode (z.B. wird bei der Berechnung angenommen, bei der Erde handele es sich um eine perfekte Kugel) führt verständlicherweise dann auch nur zu Näherungswerten, die für viele Anwendungen allerdings ausreichen sollten.

Eine mögliche Anwendung ist die Umkreissuche innerhalb einer Datenbank, z.B. Filialen eines Unternehmens im Umkreis von 15 km von meinem Wohnort. Der im angehängten Beispielprojekt umgesetzte Algorithmus ist allerdings sehr(, sehr) einfach und geht jeweils die komplette Liste aller Postleitzahlen durch, was auf einer Website mit vielen Anfragen dieser Art zu Performance-Problemen führen kann.

Das angehängte Projekt ist in C# unter .Net 2.0 realisiert und dient nur Demonstrationszwecken, es gibt keine Dokumentation, keine Fehlerbehandlung und (natürlich) keine Gewährleistung. Der Code wird “as-is” angeboten und kann in eigenen Projekten verwendet werden.

Geo.zip (259,24 KB)