Freitag, 25. Juli 2008

WTF of the Month

Ich poste das jetzt mal ohne Worte:

   1:  [...]
   2:   
   3:  boolean leaveout = false;
   4:   
   5:  [...]
   6:   
   7:  // if this is not used, don't forget to set "leaveout" to false a few lines up
   8:   
   9:  [...]
  10:   
  11:  ResultSet rs = db.executeQuery(sql);
  12:   
  13:  if (rs.next())
  14:  {
  15:      x();
  16:      y();
  17:      z(leaveout);
  18:   
  19:      while(rs.next())
  20:      {
  21:          x();
  22:          y();
  23:          z(true);
  24:      }
  25:  }

desillusionierte Grüße
SH

Mittwoch, 23. Juli 2008

HowTo: Erstellen von Snippets

Gestern habe ich über .NET-Snippets.de berichtet, wo man eine Vielzahl von nützlichen Snippets finden kann. Nun kennt sich vielleicht nicht jeder mit der Thematik Snippets aus und weiß, wie man heruntergeladene Snippets in Visual Studio verfügbar macht. Oder das Posting hat den Ehrgeiz geweckt, eigene Snippets zu erstellen, doch es fehlt das Wissen, wie das zu erledigen ist.

Deshalb kam mir der Gedanke, ein HowTo zu schreiben, wie man Snippets erstellen und in VS einbinden kann. Doch da kam mir ein heutiges Posting in den MSDN-News zuvor. Es trägt den Namen "Codierungs-Turbos in Visual Studio 2008" und behandelt einige Interessante Themen, wie man mit Hilfe von Visual Studio Programmierarbeit schneller erledigen kann. Netterweise ist unter anderem ein kompletter Abschnitt über Snippets mit an Bord. Dieser behandelt wirklich alles rund um Snippets. Ganz so, als hätte Microsoft gerochen, was mir gerade so im Kopf herum geht.

Zu erreichen ist der gesamte Beitrag hier: Codierungshilfsmittel Der Abschnitt über Snippets ist hier zu finden: Erstellen und Verwenden von IntelliSense-Codeausschnitten

Dienstag, 22. Juli 2008

Snippets: .NET-Snippets.de

Eine sehr nützliche Neuerung, die Visual Studio 2005 mit sich brachte, waren die Snippets. Dabei handelt es sich um kleine Codeschnipsel, die auf einfache Art und Weise in den Code eingefügt werden können.Beispielsweise ergibt die Eingabe von f-o-r-TAB-TAB  eine fertige for-Schleife. Die Werte, die angepasst werden müssen (in diesem Fall Indexer und Grenzwert) sind als Felder hervorgehoben und können via Tab sehr einfach angesprungen und verändert werden. Nach Abschluss der Anpassungen reicht ein Tippen auf Enter und die For-Schleife ist fertig (natürlich ohne die interne Logik... ).

Inzwischen benutze ich auch zum Einfügen unseres Firmen-Headers ein Snippet. Das spart mir pro Datei ca. 20 Zeilen "Handschreiben" und dem entsprechend auch Zeit.

Eine ausgesprochen gute Quelle für Snippets aller Art ist die Seite .NET-Snippets.de, die kurz nach Veröffentlichung von Visual Studio 2005 ans Netz ging. Das System von .NET-Snippets.de ist es, dass die angemeldeten Benutzer eigene Snippets hochladen und veröffentlichen können. Weiterhin kann jeder Nutzer jedes Snippet kommentieren und kostenlos herunterladen. Doch nicht nur aufgrund der reinen Fülle an Snippets, die zur Verfügung stehen, ist .NET-Snippets.de immer mal wieder einen Blick wert. Schon des Öfteren habe ich dort sehr schöne Lösungen für alltägliche Probleme gefunden.

Ich kann nur jedem .NET-Entwickler empfehlen, sich hier anzumelden, sich einmal durch die Snippets zu "wühlen" und auch gerne eigene Snippets zu veröffentlichen. Anreize gibt es ab und an mal durch Snippet-Contests, bei denen die bestbewerteten Snippets (respektive deren Ersteller) mit Preisen belohnt werden.

OPC: DataChange-Event

Ein OPC-Client hat die Möglichkeit, vom Server Nachrichten über Aktualisierungen von Werten zu empfangen. Das ist natürlich sehr nützlich, wenn man gerne eine bestimmte Aktion ausführen möchte, wenn ein definierter Wert erreicht wird. Kommt ja auch nicht gerade selten vor, diese Anforderung, also hat die OPC Foundation da schon ganz schön schlau definiert.

Nicht ganz so schlau ist jedoch die Umsetzung innerhalb der OpcDAAuto.dll gelungen. Ich habe heute etwa einen halben Tag gekämpft, bis ich tatsächlich in der Lage war, den DataChange-Event, der vom Server bei Veränderungen von zuvor dafür angemeldeten Werten gesendet wird, zu empfangen. Ich habe Tutorials gewälzt, das Internet durchforstet und experimentiert - alles ohne Erfolg. Alle Voraussetzungen, die ich finden konnte, schienen mir erfüllt. Nach ewigem Herumprobieren gab es nur noch einen einzigen Unterschied zwischen den Tutorials - die, heruntergeladen und kompiliert im Übrigen einwandfrei funktionierten - und meiner Applikation. Dieser fand sich beim Anlegen von OPC-Items, also jenen Werten, die ich überwachen wollte. Mein Code - im Übrigen eigentlich absolut korrekt - sah folgendermaßen aus:

   1:  OPCGroupClass opcGroupTriggerItems;
   2:   
   3:  // Gruppe anlegen
   4:  opcGroupTriggerItems = ( OPCGroupClass ) opcServer.OPCGroups.Add( "TriggerItems" );
   5:   
   6:  // Items hinzufügen
   7:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem1", 0 ) );
   8:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem2", 1 ) );
   9:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem3", 2 ) );
  10:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem4", 3 ) );
  11:   
  12:  // Event Handler Methode registrieren
  13:  // WICHTIG: Die Event Handler Methode wird erst nach dem hinzufügen der Items zur Gruppe registriert,
  14:  //          da sonst beim Hinzufügen für jedes Item ein Event ausgelöst wird.
  15:  opcGroupTriggerItems.DataChange += new DIOPCGroupEvent_DataChangeEventHandler( opcGroupTriggerItems_DataChange );

Hier wird zuerst eine Gruppe auf dem OPC-Server angelegt, zu der dann anschließend die Items hinzugefügt werden, die die Werte darstellen, die überwacht werden sollen. Um zu vermeiden, dass für alle Items ein initialer Event ausgelöst wird, der über die Änderung von "nicht vorhanden" nach "vorhanden" und die damit verbundene Wertänderung benachrichtigt, wird die Event-Handler-Methode erst nach dem Anlegen der nötigen Items registriert. So wurden die Items korrekt angelegt, sie konnten ausgelesen werden, aber die Benachrichtigung über Wertänderungen funktionierte, wie bereits erwähnt, überhaupt nicht.

Der Code, der mir von den Tutorials aufgezeigt wurde und der jetzt, nachdem ich ihn so auch in meiner Applikation habe, zu dem gewünschten Ergebnis führt, sieht so aus:

   1:  OPCGroupClass opcGroupTriggerItems;
   2:  string[] itemIds = new string[5];
   3:  int[] clientHandles = new int[5];
   4:  int[] serverHandles;
   5:  int[] errors;
   6:   
   7:  // Gruppe anlegen
   8:  opcGroupTriggerItems = ( OPCGroupClass ) opcServer.OPCGroups.Add( "TriggerItems" );
   9:   
  10:  // Items erstellen
  11:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem1", 1 );
  12:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem2", 2 );
  13:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem3", 3 );
  14:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem4", 4 );
  15:   
  16:  // ClientHandles erstellen
  17:  clientHandles.SetValue( 1, 1 );
  18:  clientHandles.SetValue( 2, 2 );
  19:  clientHandles.SetValue( 3, 3 );
  20:  clientHandles.SetValue( 4, 4 );
  21:   
  22:  // Items der Gruppe hinzufügen
  23:  opcGroupTriggerItems.OPCItems.AddItems( 4, ref itemIds, ref clientHandles, out serverHandles, out errors, null, null );
  24:   
  25:  // Event Handler Methode registrieren
  26:  // WICHTIG: Die Event Handler Methode wird erst nach dem hinzufügen der Items zur Gruppe registriert,
  27:  //          da sonst beim Hinzufügen für jedes Item ein Event ausgelöst wird.
  28:  opcGroupTriggerItems.DataChange += new DIOPCGroupEvent_DataChangeEventHandler( opcGroupTriggerItems_DataChange );

Auch hier wird zunächst eine Gruppe angelegt. Es werden hier jedoch die Items nicht, wie zuvor, einzeln der Gruppe hinzugefügt. Statt dessen werden ein String-Array mit den Werten und ein Integer-Array mit den Client-Handles, unter denen die Items eindeutig zu identifizieren sind, angelegt. Hier ist die Besonderheit zu beachten, dass der erste Wert des Arrays jeweils nicht beachtet wird, das Array wird immer nur von 1 bis max durchlaufen. Das hängt, so weit ich informiert bin, damit zusammen, dass die von mir verwendete OpcDAAuto.dll in VB geschrieben ist. Jedenfalls werden anschließend die Items und ihre Client-Handles gesamt an eine Methode übergeben, die sie der Gruppe hinzufügen. Anschließend wieder die Event-Handler-Methode registriert. Test anwerfen - funzt.  Sehr erstaunlich und für mich nicht wirklich nachvollziehbar, aber im Endeffekt ist es jetzt auch egal, es läuft ja nun :)

log4net & 64Bit - Update

Zumindestens einen Teil des Problems aus dem Post http://justacodeblog.blogspot.com/2008/07/log4net-ibasecomponent-64bit-windows.html habe ich inzwischen aufgeklärt.

Unter Windows 2003 64Bit gibt es drei Gac Verzeichnisse: GAC_32 (für 32Bit kompilierte Assemblies), GAC_64 (für 64Bit kompilierte Assemblies) und GAC_MSIL (Assemblies die sowohl als auch ausgeführt werden können).

Mit Hilfe von Filemon habe ich dann herausgefunden das meine Anwendung log4net im GAC_32 verzweifelt sucht. Eigentlich sollte log4net mit Hilfe des Gacutils im GAC_32 Verzeichnis landen, tut es aber nicht, es landete bei mir im GAC_MSIL Verzeichnis.

Ich habe es dann "von Hand" dort hin kopiert und nun funktioniert es, jedoch habe ich keine Ahnung ob man bei Gacutil einen Switch setzen kann der die automatische Entscheidung überstimmt und das Deployment in einem bestimmten Verzeichniss erzwingen kann...

Montag, 21. Juli 2008

BizTalk 2006 Subscriptions

Manchmal sieht man den Wald vor lauter Bäumen nicht...
Um sich alle Subscriptions in BizTalk anzeigen zu lassen, gehe man in der Administration auf die Group -> New Query -> Search For - Equals - Subscriptions -> Run Query

voilà

(doh)

Custom Pipeline Components - Update

Seit meinem ersten Eintrag zum selber schreiben von Custom Pipeline Components (http://justacodeblog.blogspot.com/2008/05/custom-pipeline-components-wizard.html) hab ich inzwischen soviel Erfahrungen zu dem Thema gesammelt, das ich hierzu ein Update verfassen muss.

Ein paar Anmerkungen zum arbeiten mit Martijn Hoogendoorn's Component Wizard und Pipeline Komponenten generell:

- Der Beste Ansatz um die erstellten Komponenten stressfrei zu deployen und in der Toolbox des Pipeline Projektes zu verwenden ist es die fertige DLL Komponente: 1.) in den GAC zu deployen 2.) in das Pipeline Component Verzeichnis von Biztalk zu deployen (z.B.: C:\Program Files\Microsoft BizTalk Server 2006\Pipeline Components) Beides kann man gut im Post Built Event erledigen z.B. wie folgt (für 64Bit Windows mit angepasst, darum die relativen Pfade) "$(DevEnvDir)..\..\SDK\v2.0\Bin\gacutil.exe" /i "$(TargetPath)" xcopy "$(TargetPath)" "$(DevEnvDir)..\..\..\Microsoft BizTalk Server 2006\Pipeline Components" /R /Y

- Log4Net und 64Bit Windows können Probleme in Verbindung mit der Biztalk Toolbox Erkennung bereiten. Dabei kann entweder die korrekte Namenserkennung der Komponente, oder gar die komplete Komponenten Erkennung fehlschlagen. Das Problem vermute ich in den Windows Sicherheitseinstellungen (?). Ansonsten hilft als "Workaround" nur das komplette Entfernen von Log4Net aus der Komponente

- Beim Umbennen einer Pipeline Komponente (Namespace und/oder Klassenname) muss man zwei weitere Punkte beachten für die korrekte Anzeige in der Toolbox später. 1.) Unterhalb der Kompenenten Klassendeklaration wird der Name registriert:

   1:  private System.Resources.ResourceManager resourceManager = new System.Resources.ResourceManager(
   2:      "<Namespace>.<Pipeline Komponenten Name>",
   3:      Assembly.GetExecutingAssembly());

2.) Im Ressource File selbst: COMPONENTNAME = <Namespace>.<Pipeline Komponenten Name>

- Für einen rundimentären Test ist das pipeline.exe Tool selbst eine schnelle und schöne Sache. Jedoch empfiehlt es sich unbedingt auch eine BizTalk Test Orchestration zusammenzuklicken! Der Umstand ist simpel. Während die pipeline.exe unsere Komponente direkt aus dem lokalen Filesystem aufruft, wird in BizTalk ein COM Aufruf ausgeführt! Dies verändert den Kontext in dem sich der Stream befindet, den man gerade verarbeitet! Simples Beispiel: solange man über die pipeline.exe testet, kann der Stream nach FileStream gecastet werden, um z.B. den File Namen auszulesen. Führt man den selben Aufruf in BizTalk aus, sind die FileStream Informationen gar nicht mehr vorhanden. Ein Cast schlägt fehl!

- Für das Testen mit der pipeline.exe empfiehlt es sich eine eigene Solution Konfiguration anzulegen (z.B. Debug Pipeline). In den Projekteigenschaften konfiguriert man dann unter Debug:

  • Start external program: <pfad zum Pipeline Test Tool>\Pipeline.exe
  • Command line arguments z.B.: -pt <namespace>.<biztalk pipeline orchestration> -an <custom pipeline component name>-d <input flatfile> -v
  • und ggf. noch die Working directory anpassen wenn nötig

(nicht vergessen bei einer neuen Solution Konfiguration unter der Projekteigenschaft Build - Advanced Button die Debug Info mit erzeugen zu lassen (full))

- Bei der Implementierung ist darauf zu achten das BizTalk die Pipeline bei hoher Last automatisch parallel ausführt!! D.h. VORSICHT beim Umgang mit z.B. statischen Elementen, wer nicht darauf verzichten kann, muß Threadsicher programmieren!

Donnerstag, 17. Juli 2008

HowTo: Erstellen eines Windows Service unter .NET

Da ich gerade zum ersten mal in die Verlegenheit komme, einen Windows Service unter .NET zu entwickeln, habe ich mich mal ein wenig im WWW umgeschaut, um ein brauchbares Tutorial zu finden. Fündig wurde ich hierbei bei CodeGuru.

Im Tutorial wird nicht nur sehr schön erklärt, wie ein Windows Service aufgebaut ist, sondern es wird ebenfalls darauf eingegangen, wie er zu installieren ist und wie man den laufenden Service dann debuggen kann. Gerade für Anfänger auf dem Gebiet sicherlich sehr nützliche Informationen. Das Tutorial ist hier zu finden: http://www.codeguru.com/columns/dotnet/article.php/c6027/

Noch ein Zusatz von meiner Seite: Bei der Installation des Windows Service via InstallUtil.exe über die Visual Studio Eingabeaufforderung ist unbedingt die Version des Frameworks zu beachten, mit der der Service erstellt wurde. Wird nämlich die Eingabeaufforderung einer anderen Framework-Version verwendet als jene, mit der der Service erstellt wurde, erhält man beim Aufruf von InstallUtil.exe nur einen Fehler, der einem mitteilt, dass der Service nicht installiert werden konnte.

Alternativ kann man die normale Windows-Eingabeaufforderung verwenden und InstallUtil.exe aus dem Ordner "C:\WINDOWS\Microsoft.NET\Framework\FRAMEWORKVERSION" heraus starten.

Biztalk SAP Adapter Error 8007007e

Vielleicht "schon wieder" ein 64Bit Enviroment Issue... (?) der Auslöser ist mir nicht ganz klar, aber bei meinem aktuellen Projekt tritt nach der Konfiguration im Biztalk Server 2006 von SAP Ports alle paar Minuten im Eventlog den folgenden Fehler:

Nach etwas Rumforschen und Abgleichen der SAP Adapter Installationsverzeichnisse ergab sich das sowohl die ATL71.dll als auch die MSVCR71.dll fehlten. Offensichtlich hat der Adapter Installer Probleme diese Komponenten unter Windows 2003R2/x64 zu installieren. Ich musste diese von Hand nachträglich reinkopieren nach "C:\Program Files (x86)\Microsoft BizTalk Adapter v2.0 for mySAP Business Suite" Der Fehler stoppte sofort, ohne irgendetwas neu zustarten, von alleine.

Mittwoch, 16. Juli 2008

Verzögerung beim .NET BlogBook

Wie ich gerade eben via Norbert Eders Blog erfahren habe, verzögert sich das Erscheinen der nunmehr siebten Ausgabe des .NET BlogBook. Der eigentliche Erscheinungstermin wäre der 15. Juli gewesen.

Wie im entsprechenden Post nachzulesen ist, hängt die Verspätung damit zusammen, dass zur Zeit ein "großer Umbau" vorgenommen wird. Dieser scheint so groß zu sein, dass die nächste Ausgabe wohl erst am 15. Oktober erscheinen wird.

Da bin ich doch jetzt wirklich mal gespannt, wie die Änderungen am BlogBook aussehen werden.

Dienstag, 15. Juli 2008

Webcast: Neuerungen im SP1 für .NET3.5 & VS2008

Am heutigen Dienstag ist ein Webcast erschienen, der sich mit den Neuerungen und Änderungen  in .NET 3.5 und Visual Studio 2008 beschäftigt, die durch das Service Pack 1 Einzug halten werden. Als Vorbereitung auf den Release ist dies mit Sicherheit eine sehr interessante Sache und für jeden Entwickler, der mit der Technologie bzw. dem Tool arbeitet, ein Muss, um vorbereitet zu sein.

Über die Qualität kann ich bisher leider noch nichts sagen, da ich mir den Webcast noch nicht angeschaut habe. Da er aber von Dariusz Parys abgehalten wird, kann er eigentlich nur sehr gut und informativ sein. Unbedingt anschauen!

Zu beziehen ist der Webcast (mit gültigem Live-Account) hier: https://www.microsoft.com/germany/msdn/webcasts/library.aspx?id=1032382740

Skype-AddIn für Visual Studio 2005 / 2008

Ein weiterer Schritt in Richtung "Ich geh nie wieder aus meinem VS raus" ist gemacht: Der thailändische Entwickler Keng Pongsathon hat ein AddIn für Visual Studio geschrieben, welches Skype in unser aller Lieblingsspielzeug einbindet. Dieses ist auf Codeplex verfügbar: http://www.codeplex.com/SkypeStudio/

Über Sinn und Unsinn dieses AddIns kann man jetzt diskutieren. Meine Reaktion war eher in Richtung "Wozu", die eines anderen Teilnehmers an diesem Blog ein schlichtes "Goil". Da sollte sich wohl jeder selbst seine Meinung bilden.

Gefunden habe ich das AddIn im Übrigen über den deutschen MSDN Blog.

Donnerstag, 3. Juli 2008

64Bit / 32Bit und universelles Postbuildevent mit gacutil

Nur als kleine Notiz für mich selbst, da die Pfade für die Programmordner unterschiedlich sind unter 64 und 32 Bit. Hab ich mir aus den Makros die Pfade zusammengeklickert und da ich allein schon mit dem setzen von " und \ herumeiere leg ich das hier mal lieber ab ;)

"$(DevEnvDir)..\..\SDK\v2.0\Bin\gacutil.exe" /u "$(TargetPath)"
"$(DevEnvDir)..\..\SDK\v2.0\Bin\gacutil.exe" /i "$(TargetPath)"

log4net, IBaseComponent & 64Bit Windows Server

log4net 1.2.10, IBaseComponent & 64Bit Windows Server funktionieren irgendwie nicht zusammen. Das witzige unter 32Bit funktioniert es!

Für eine Biztalk Pipeline habe ich zwei Komponenten entwickelt die aus einem Flatfile Format eine XML Datei erzeugt. Beim einbinden der Komponten war im Browser von VisualStudio bei einer der korrekte Name aus der IBaseComponent zu sehen, bei der anderen der Assembly Name.
Dumm denn auf meiner 32Bit Entwicklungsmaschine war dem nicht so, aber auf dem 64Bit Zielsystem. Nach einer etwas längeren Remotedebuggingsession stellte sich dann heraus das bei der Komponente mit dem "falschen Namen", nicht einmal der Konstruktor angesprungen wird, geschweige denn die Informationen aus IBaseComponent, wenn man diese in VisualStudio "an"browst.

Erst nach der vollständigen Entfernung der log4net Referenzierung (darauf muss man erstmal kommen), wurde der korrekte Name aus dem Interface angezeigt. Befand sich log4net gar im GAC war die Pipeline Komponente überhaupt nicht mehr auffindbar, beim manuellen Browsen gabs dann nur noch den lapidaren Hinweis das wohl etwas mit der Sicherheitseinstellung der Komponente nicht stimmen würde... sehr merkwürdig....

Eine Sicherheitseinstellung in Windows Server? Ein Problem mit log4net? :-/