WRITELINE

Geek. Coder. Gamer. Bayern Munich Fan.

Visit my blog stream http://writeline.io

  • Dariusz quatscht

    Windows Server 2008 Feature: Transactional NTFS

    • 3 Comments

    Mit Windows Server 2008 kommen Neuerungen im Bereich des Transaktionsmanagements. Das lang erwartende Transactional NTFS. Damit lassen sich Dateien transaktionsorientiert bearbeiten. Hier ein kleines Beispiel:

       1: #include "stdafx.h"
       2:  
       3: int main(array<System::String ^> ^args)
       4: {
       5:  
       6:     HANDLE transactionHandle = NULL;
       7:     HANDLE fileHandle = NULL;
       8:  
       9:     transactionHandle = ::CreateTransaction( NULL, 
      10:                             0, 
      11:                             NULL, 
      12:                             0, 
      13:                             0, 
      14:                             NULL, 
      15:                             NULL );
      16:  
      17:     fileHandle = ::CreateFileTransacted( L"c:\\test\\transactedfile2.txt",
      18:         GENERIC_READ | GENERIC_WRITE,
      19:         NULL,
      20:         NULL,
      21:         CREATE_ALWAYS,
      22:         FILE_ATTRIBUTE_NORMAL,
      23:         NULL,
      24:         transactionHandle,
      25:         NULL,
      26:         NULL );
      27:  
      28:     ::CloseHandle( fileHandle );
      29:  
      30:     // Einfach mal in das Verzeichnis schauen, die Datei ist noch 
      31:     // nicht sichtbar.
      32:  
      33:     ::CommitTransaction( transactionHandle );
      34:     
      35:     // Jetzt ist die Datei im Explorer sichtbar.
      36:     
      37:     return 0;
      38: }
    Resourcen können nun miteinander transaktionell kombiniert werden, die bisher nur wenigen Entwicklern vorenthalten waren. Wer hat schon einen eigenen Transactional Resource Manager implementiert. Ein schönes Beispiel für Transaktionen zwischen verschiedenen Resourcen hat Jason Olson in einem Screencast aufgenommen. Er spannt einen Transaktionskontext zwischen einem WCF Service und NTFS. Sehenswert.
  • Dariusz quatscht

    Windows Server 2008 Feature: Restart Manager

    • 1 Comments

    Mit Windows Server 2008 (und bereits mit Windows Vista) erscheint eine API die es Installationsroutinen einfacher macht die Anzahl möglicher Reboots zu reduzieren. Mit der Restart Manager API können Prozesse ermittelt werden die bestimmte Resourcen verwenden. Möchte man zum Beispiel eine DLL austauschen kann man erfragen welche Anwendungen momentan diese DLL nutzen und diese gegebenfalls runterfahren und neu starten.

    Nehmen wir an wir möchten eine Anwendung aktualisieren und müssen hierzu drei verschiedene Dienste runterfahren, IIS Admin, SQL Server und MSMQ. Diese Dienste haben unter Umständen Abhängigkeiten die so von System zu System variieren können. Mittels der Restart Manager API kann man diese Abhängigkeiten ermitteln und auch sehen ob diese sich mitkontrollieren lassen.

    Zuerst muss man die Restart Manager API initializieren in dem man eine Session beginnt. Das Konzept der Session erlaubt es unterschiedliche Installationsroutinen mit denselben Resourcen zu arbeiten.

       1: DWORD result = 0;
       2: DWORD sessionHandle = -1;
       3: WCHAR sessionKey[ CCH_RM_SESSION_KEY + 1 ];
       4:  
       5: result = ::RmStartSession( &sessionHandle, 0, sessionKey );
       6: if( result != ERROR_SUCCESS )
       7: {
       8:     goto rmend;
       9: }

    Als nächstes werden die Resourcen welche man prüfen möchte übergeben. Hier übergibt man der Struktur Windows Dienste, Filenamen und Prozessnamen. In diesem Beispiel nur die drei Dienstnamen.

       1: DWORD fileCount = 0;
       2: DWORD serviceCount = 3;
       3: DWORD processCount = 0;
       4:  
       5: LPCWSTR services[] = { L"IISADMIN", L"MSSQLSERVER", L"MSMQ" };
       6: LPCWSTR files[] = NULL;
       7:  
       8: UINT processInfo = 200;
       9: UINT processInfoNeeded;
      10: RM_PROCESS_INFO* affectedApplications = NULL;
      11: DWORD rebootReason = 0;
      12:  
      13: affectedApplications = new RM_PROCESS_INFO[ processInfo ];
      14:  
      15: result = ::RmRegisterResources( sessionHandle, 
      16:              fileCount, 
      17:              files, 
      18:              processCount, 
      19:              NULL, 
      20:              serviceCount, 
      21:              services );
      22: if( result != ERROR_SUCCESS )
      23: {
      24:     goto rmend;
      25: }

    Natürlich müsste man noch prüfen ob der übergebene Speicher der Struktur ausreicht und entsprechend beim Fehlercode nach mehr Speicherbedarf mehr Speicher allokieren, der Einfachheit halber hier komplett weggelassen.

    Nachdem die Resourcen registriert sind, kann man sich nun eine Liste der Abhängigkeiten geben lassen.

       1: result = ::RmGetList( sessionHandle, 
       2:              &processInfoNeeded, 
       3:              &processInfo, 
       4:              affectedApplications, 
       5:              &rebootReason );
       6: if( result != ERROR_SUCCESS )
       7: {
       8:     goto rmend;
       9: }

    affectedApplications liefert uns nun die betroffenen Anwendungen. Hier der Ausdruck auf der Console nach diesem Aufruf.

    shot1

    Zwei weitere Anwendungen vom Typ Windows Dienst (Service) werden durch die Rückgabestruktur gemeldet. Net.Msmq Listener und Message Queuing Triggers. Alle 5 Dienste sind gerade am laufen und lassen sich auch neu starten wie das Restartable: Yes anzeigt.

    Nun käme der Zeitpunkt der eigentlichen Installationsroutine. Zum einen müssten die Dienste nun gestoppt werden,

       1: result = ::RmShutdown( sessionHandle, 0, NULL );
       2: if( result != ERROR_SUCCESS )
       3: {
       4:     goto rmend;
       5: }

    danach kann man auf alle geblockten Resourcen zugreifen und diese austauschen. Jetzt folgt die Installationsroutine. Ist man damit fertig startet man die Dienste auf einen Schlag.

       1: result = ::RmRestart( sessionHandle, 0, NULL );

    Um einen problematischeren Fall zu betrachten, hier ein Beispiel mit der OleAut32.dll.

       1: LPCWSTR services[] = NULL;
       2: LPCWSTR files[] = { L"C:\\windows\\system32\\oleaut32.dll" };
       3:  
       4: UINT processInfo = 200;
       5: UINT processInfoNeeded;
       6: RM_PROCESS_INFO* affectedApplications = NULL;
       7: DWORD rebootReason = 0;
       8:  
       9: affectedApplications = new RM_PROCESS_INFO[ processInfo ];
      10:  
      11: ::RmRegisterResources( sessionHandle, 
      12:              fileCount, 
      13:              files, 
      14:              processCount, 
      15:              NULL, 
      16:              serviceCount, 
      17:              services );
      18:  
      19: ::RmGetList( sessionHandle, 
      20:              &processInfoNeeded, 
      21:              &processInfo, 
      22:              affectedApplications, 
      23:              &rebootReason );

    Hier sieht die Ergebnismenge schon deutlich anders aus.

    shot2

    Die rotmarkierten Zeilen sind Anwendungen die diese DLL aktiv angezogen haben und auch nicht über den Restartmanager gestartet werden können. Hier muss die Installationsroutine im Reboot die entsprechende Aktion bewerkstelligen um zum Beispiel die oleaut32.dll auszutauschen. Anmerkung: OleAut32.dll bitte nicht anrühren, ist nur ein Beispiel einer blockierten DLL.

    Durch das Konzept der Session können nun Installationen auch weitere Installationsroutinen starten und den Sessionkey mitübergeben. Resourcen die einen Systemreboot benötigen werden angezeigt. Man kann nun in seiner Installationsroutine erstmal die notwendigen anderen Resourcen installatieren, löschen, austauschen die keinen Maschinenreboot benötigen und hebt sich die kritischen zum Schluß auf. Mit Hilfe der API lassen sich so Installationsroutinen intelligenter verbinden und Systemreboots auf ein minimum reduzieren.

  • Dariusz quatscht

    Die Zukunft von Visual C++

    • 1 Comments

    Vor einigen Tagen hat sich unser Vice President zu der Zukunft von Visual C++ offiziell geäußert.

    • Kommende Versionen von Visual C++ werden mit Hauptaugenmerk auf native Entwicklung fokussiert entwickelt.
    • Es wird eine immer leichtere Integration in die Managed Welt mit C++/CLI geben.
    • Es wird diverse Verbesserungen im Framework Design bestehender Klassenbibliotheken mit Hinblick auf Benutzeroberflächen geben.

    Was mich wirklich erstaunt hat war die Aussage von Bill Dunlap auf dem Blog von Herb Sutter das noch ein großes MFC Update folgen wird.

    An dieser Stelle möchte ich einen Aufruf starten. Falls Sie ein größeres C++ Projekt supporten, entwickeln oder starten möchten, Sie ein ISV sind der mit Microsoft schon in Kontakt war oder in einem großen Unternehmen entwickeln, hier ein einmaliges Angebot.

    Kurz nach der TechEd 07 (Mitte November) werden voraussichtlich zwei Mitglieder der Visual C++ Produktgruppe in Deutschland sein. Wir werden einen Workshop zu Visual C++ veranstalten. Neben diesen Workshop möchten wir gerne den ein oder anderen Kunden besuchen um Vor-Ort mit Rat und Tat zur Seite zu stehen.

    Dieser Termin ist noch nicht zu 100% bestätigt, ich möchte trotzdem schonmal im Vorfeld mit Interessenten Kontakt aufnehmen.

    Kontaktieren Sie mich über mein Blog. Ich werde allerdings erst Anfang September antworten können da ich momentan im Urlaub bin.

  • Dariusz quatscht

    Pimp up your Office

    • 1 Comments

    Sie gehören zu den Leuten die sich gerne Poster ins Büro hängen? Am besten noch über Produkte und Technologien die Sie tagtäglich einsetzen? Dann könnte diese Webseite das richtige für Sie sein.

    Ein südafrikanischer Partner hat auf seiner Webseite Poster zu diversen Technologien und Prozessen veröffentlicht. Unter anderem findet man hier etwas zu Team System und PowerShell.

    Einzige Voraussetzung, man hat einen schönen A0 Plotter um das ganze zu drucken, sonst wird es je nach Bürogröße doch ein wenig schwierig das ein oder andere von der gegenüberliegenden Wand zu lesen.

  • Dariusz quatscht

    Power Tools für VSTS Database Professional

    • 1 Comments

    Mit der Rolle des Database Professionals gibt es in Visual Studio Team System endlich dediziert Tools und Prozesse um Datenbankentwickler und -administratoren in den Entwicklungszyklus mit zu integrierern. Nun ist das erste Power Tools Release für diese Rolle da. Power Tools sind einfach fehlende nützliche Helfer die das eigentliche Tooling ergänzen.

    Es sind MSBuild Tasks enthalten die Datenbankschema und -inhalte in einem Build Prozess vergleichen können. Des Weiteren wurde für die Freunde von Strongly Typed Datasets (Ich gehöre nicht zu dieser Fraktion) im Bereich Refactoring eine Erweiterung eingebaut, die das Umbennen im Datenbankschema nun auch im Dataset nachzieht sofern sich alles in einer Visual Studio Solution befindet.

    Eine vollständige Liste der Tools und deren Beschreibung findet man auf Gert Drapers Blog.

    Die Downloadseite zu den Powertools ist hier zu finden.

  • Dariusz quatscht

    VSTS geht in die nächste Runde: Rosario

    • 1 Comments

    Wir lieben Codenamen für unsere Produkte. Leider ist das oft verwirrend. Besonders in diesem Fall, da es dies so meines Wissens noch nicht gab. (Ok, Silverlight lasse ich gelten).

    Die nächste Version von Visual Studio Team System heißt Visual Studio Team System 2008. Wie gesagt, das ist die nächste Version die auf den Markt kommen wird.

    Jetzt gibt es eine weitere Version die als CTP erhältlich ist: Microsoft Visual Studio Team System code name "Rosario" oder kurz Rosario.

    Rosario ist die Version die nach Visual Studio Team System 2008 auf den Markt kommt.

    Das ist, wie gesagt, bisher so noch nicht da gewesen. Wir veröffentlichen einen Build einer Version, die nach der Version auf den Markt kommt, die noch nicht erschienen ist.

    Das VSTS Team hat sich entschlossen frühzeitig Einblick in die kommenden Versionen von Team System zu geben. Mit Rosario hat man die Möglichkeit reinzuschnuppern was kommen wird. Anhand dieser frühen CTP (Community Technology Preview) können Partner und Kunden früher Feedback in die Produktgruppe geben.

    Ein wenig detailierter hat dies Jeff Beehler auf seinem Blog beschrieben.

    Ansonsten erfährt man alles was notwendig ist auf der MSDN Seite zu Rosario, dort kann man sich auch schon ein Image herunterladen mit dem man experimentieren kann.

  • Dariusz quatscht

    Demos zum HPC Webcast für Entwickler

    • 2 Comments

    Vielen Dank noch einmal für die zahlreiche Teilnahme am High Performance Computing Webcast für Entwickler.

    Zusammenfassend hier nochmal die Punkte die vorgestellt wurden:

    Open MP

    Parellel Verarbeitung von Anwendungslogik innerhalb eines Prozesses. Abstrahiert die Threading Mechanismen des Betriebssystems und ist somit einfacher zu portieren.

    MPI (Message Passing Interface)

    Die zentrale API zum Entwickeln von Anwendungen die über Cluster Knoten hinweg kommunizieren. Eine nachrichtenbasierte API zum Austausch von Daten. Abstrahiert auch Kommunikationsmuster so das hier viel Entwicklungsarbeit abgenommen werden kann.

    Cluster API

    Alles was man mit dem Windows Compute Cluster Server 2003 konfigurieren kann lässt sich über die API aus einer eigenen Anwendung heraus steuern. Anlegen von Tasks, erstellen von Jobs, Überwachung der verschiedenen Knoten im Cluster, usw...

    Der gestrige Webcast war rein der Programmierung gewidmet. Um einen Überblick zum Windows Compute Cluster Server 2003 zu bekommen möchte ich hier auf den Webcast von meinem Kollegen Steffen Krause verweisen, der eine hervorragende Einführung gibt.

    Hier der Link zu den Demos des gestrigen Webcasts.

  • Dariusz quatscht

    Wie baue ich eine Software Factory (Teil 2)?

    • 3 Comments

    Im ersten Teil habe ich die Grundvoraussetzungen betrachtet und das Beispiel-Package des Guidance Automation Toolkit compiliert und gestartet. Das Beispiel ist ein wenig erschlagend wenn man sich die gesamten Verzeichnisse anschaut die in der Lösung miterstellt werden. Im heutigen Teil möchte ich kurz auf das Beispiel Projekt der Serie eingehen und ein weiteres Hello World Demo schreiben. 

    Das neue Projekt wird einfach "MyFactory" heißen. MyFactory wird ein Guidance Package sein die eine typisierte Directory Komponente erstellen kann. Was ist eine typisierte Directory Komponente? Nehmen wir folgenden Fall an: Im Unternehmen gibt es neben Active Directory noch zwei weitere Directories die im großen und ganzen die gleichen Attribute zur Verfügung stellen. Die Directories trennen logisch verschiedene Segmente im Unternehmen. Die Attribute sind jedoch Directory Ebene unterschiedlich benannt und müssen gesondert angesprochen werden. Des Weiteren gibt es gewisse Richtlinien wie eben mit diesen Directories gearbeitet werden darf. Es liegt auf der Hand eine standardisierte Komponente zu erstellen die für alle Entwickler die gleiche Zugriffslogik zur Verfügung stellt.

    Jedes Projekt und jede Anwendung die eines der drei Directories nutzen, müssen Zugriff auf die einzelnen Directory Attribute beantragen. Die meisten Anwendungen dürfen sensible, vertrauliche Daten nicht lesen. Zum Beispiel darf eine Anwendung der Personalstelle durchaus Gehaltsstufen des Mitarbeiters aus dem Directory lesen, während eine Intranet Anwendung die für die Weiterbildung der Mitarbeiter verantwortlich ist lediglich deren Kontaktdaten lesen darf.

    Welche Lösungsansätze gibt es?

    a) Die generische Komponente

       1: CompanyDirectory directory = new CompanyDirectory( CompanyDirectory.Directory1 );
       2: DirectoryObject user = directory.FindUserByEmail( "test@microsoft.com" );
       3: // auslesen der Kontaktdaten
       4: string name = user.Properties[ Attributes.Lastname ];
       5: string phone = user.Properties[ Attributes.Telephone ];
       6: // etc...

    Mit einer generischen Komponente kann man den Fall gut lösen. Der Nachteil der sich hier ergibt sind Laufzeitfehler falls auf Eigenschaften zugegriffen wird die es nicht gibt oder die für die Anwendung nicht freigeschalten sind. Laufzeitfehler lassen sich sehr gut mit Unit Tests eliminieren, so das dies ein möglicher Ansatz wäre.

    b) Die typisierte Komponente

       1: CompanyDirectory directory = new ComponyDirectory( CompanyDirectory.Directory1 );
       2: // es wird ein typisiertes User Objekt von der Methode erstellt
       3: DirectoryUser user = directory.FindUserByEmail( "test@microsoft.com" );
       4: string name = user.Lastname;
       5: string phone = user.Telephone;
       6: // typisierte Zugriffe

    Mit der typisierten Komponente läßt es sich leichter entwickeln. Attribute für die die Anwendung keinerlei Verwendung oder Rechte hat werden in einem solchen typisierten Objekt erst gar nicht aufrauchen. Durch die typisierung erhält man neben Intellisense Unterstützung auch noch kompilierfehler gemeldet falls etwas nicht stimmt. Es können auch hier Laufzeitfehler auftreten, allerdings mehr aus einer Anwendungsbezogenen Sicht heraus und seltener Aufgrund von ansprechen von Attributen.

    Beide Ansätze verfügen über eine Konfigurationsdatei um das Mapping von Directory Attributen auf Objekt- bzw. PropertyBag Attribute zu bewerkstelligen. Ein Beispiel:

       1: <!-- Directory 1 --/>
       2: <attribute name="phone" property="Telephone"/>
       3:  
       4: <!-- Directory 2 --/>
       5: <attribute name="phoneNumber" property="Telephone"/>

    Der Entwickler programmiert gegen die Eigenschaft Telephone während diese im Directory 1 auf phone oder im Directory 2 auf phoneNumber gemappt ist. Ändert sich das Attribut in einem der Directories einmal kann man es in der Konfiguration nachziehen ohne die Anwendung neu übersetzen zu müssen.

    Für das weitere Beispiel werde ich Methode B wählen.

    Nachdem der Projekthintergrund für die Factory beschrieben ist, wird das eigentliche erstellte Package erstmal entrümpelt. Nach der Erstellung durch das Projekt Template erhält man folgende Projektstruktur:

    shot1

    Ich lösche erstmal alles bis auf MyFactory.xml und TypeAlias.xml.

    shot2

    Das sieht übersichtlicher aus und ist einfacher als Ausgangsbasis. Der Einstiegspunkt ist die MyFactory.xml package Beschreibung. Standardmässig wird alles in diese eine Package XML Datei gepackt. Dies ist sehr unübersichtlich und erschwert das arbeiten. Gut, das man diese Datei in mehrere aufsplitten kann. Hierzu bedient man sich dem XInclude

    Hier erstmal die veränderte MyFactory.xml.

       1: <?xml version="1.0" encoding="utf-8" ?>
       2: <GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core"
       3:     Name="MyFactory" 
       4:     Caption="MyFactory"
       5:     Description="A simple factory sample"
       6:     BindingRecipe="CreateBindings"
       7:     Guid="ef0581dd-9b0d-48ae-acd9-599d5a92fc6b" 
       8:     SchemaVersion="1.0">
       9:   <Overview Url="documentation\Overview.htm"/>
      10:   <Recipes>
      11:     <xi:include href="Recipes\CreateBindings.xml" 
      12: xmlns:xi="http://www.w3.org/2001/XInclude"/>
      13:     <xi:include href="Recipes\HelloWorldSimpleRecipe.xml" 
      14: xmlns:xi="http://www.w3.org/2001/XInclude"/>
      15:   </Recipes>
      16: </GuidancePackage>

    In Zeile 11 und Zeile 13 werden weitere Dateien referenziert und somit die eigentliche Einstiegsdatei auf das wesentlichste reduziert.

    Rezepte

    Ein wichtiger Bestandteil von dem Guidance Automation Extensions ist das Rezept (Recipe). Ein Rezept enthält 1 bis mehrere Aktionen (Actions). Zum Beispiel kann ein Rezept die Aktionen Projekt Verzeichnis erstellen und Referenzen hinzufügen enthalten. Rezepte sind wirklich nur Sammlungen von Aktionen.

    Aktionen

    Eine Aktion (Action) ist die kleinste Einheit. Die abstrakte Basisklasse Action hat nur zwei Methoden zum implementieren: Execute() und Undo(). Dadurch lässt sich auch schon feststellen das Undo Operationen auf Rezept Ebene nicht vom Design unterstützt werden sondern mühselig in den Aktionen mit rein implementiert werden müssen. Diese sind somit nicht mehr als unabhängige Einheit untereinander zu betrachten sind. Für das Beispiel Projekt "MyFactory" ist das Undo von Aktionen nicht nötig.

    Ein besonderes Rezept ist das BindingRecipe (Zeile 6). Dieses wird beim Laden des Packages ausgeführt und dient als Verknüpfungsrezept. Mögliche Rezepte werden so zu UI Elementenen hinzugeordnet.

    Die CreateBindings.xml Datei bei mir sieht folgendermaßen aus:

       1: <?xml version="1.0" encoding="utf-8" ?>
       2: <Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       3:         xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       4:         Name="CreateBindings" 
       5:         xmlns="http://schemas.microsoft.com/pag/gax-core">
       6:   <Types>
       7:     <TypeAlias Name="RefCreator" 
       8: Type="Microsoft.Practices.RecipeFramework.Library.Actions.CreateUnboundReferenceAction,
       9: Microsoft.Practices.RecipeFramework.Library"/>
      10:   </Types>
      11:   <Caption>All MyFactory Bindings</Caption>
      12:   <Actions>
      13:     <Action Name="EnableHelloWorldRef" 
      14:             Type="RefCreator" 
      15:             AssetName="HelloWorldSimpleRecipe"
      16: ReferenceType=
      17: "Microsoft.Practices.RecipeFramework.Library.AssetReferences.UnboundReferences.CSharpProjectRecipeReference, 
      18: Microsoft.Practices.RecipeFramework.Library" />
      19:   </Actions>
      20: </Recipe>
      21:  

    Das Rezept "HelloWorldSimpleRecipe" (Zeile 15) wird an alle C# Projekt Ordner als Kontextmenü gebunden. Um ein Rezept zu binden gibt es zwei Möglichkeiten. Bound=true oder Bound=false. Wird Bound auf true gesetzt so ist das Rezept meist Bestandteil eines Projekttemplates oder eines speziellen Items das als Template zur Verfügung gestellt wird. Das Rezept ist dann auch nur an dieser Art von Template verfügbar. Ist Bound auf false gesetzt so wird das Rezept mittels eines Refernztypen aufgelöst. Ein solcher Referenztyp ist auf Zeile 17 deklariert. Das Rezept HelloWorldSimpleRecipe wird an das Kontextmenü eines C# Projektes gebunden.

    Ein Blick auf das HelloWorldSimpleRecipe zeigt das genau eine Aktion enthalten ist.

       1: <?xml version="1.0" encoding="utf-8" ?>
       2: <Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       3:         xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       4:         Name="HelloWorldSimpleRecipe" 
       5:         xmlns="http://schemas.microsoft.com/pag/gax-core"
       6:         Bound="false">
       7:   <DocumentationLinks>
       8:     <Link Caption="Explain me what Hello World is" 
       9:           Url="documentation\helloworld.htm" 
      10:           Kind="Documentation"/>
      11:   </DocumentationLinks>
      12:   <xi:include href="Common\TypeAlias.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
      13:   <Caption>Demo Hello World...</Caption>
      14:   <HostData>
      15:     <CommandBar Name="Project"/>
      16:   </HostData>
      17:   <Actions>
      18:     <Action Name="HelloWorldAction" 
      19:       Type="Microsoft.Germany.Sample.Actions.HelloWorldAction, MyFactory">
      20:     </Action>
      21:   </Actions>
      22: </Recipe>

    Die Aktion HelloWorldAction wird ausgeführt sobald der Kontextmenü Eintrag ausgewählt wurde. Die Implementierung ist ebenso einfach.

       1: using System;
       2: using System.Windows.Forms;
       3: using Microsoft.Practices.RecipeFramework;
       4:  
       5: namespace Microsoft.Germany.Sample.Actions
       6: {
       7:     public class HelloWorldAction : Action
       8:     {
       9:         public override void Execute()
      10:         {
      11:             MessageBox.Show( "Hello, world.", 
      12:                 "Another complicated Hello World demo", 
      13:                 MessageBoxButtons.OK );
      14:         }
      15:  
      16:         public override void Undo()
      17:         {
      18:             throw new Exception( "The method or operation is not implemented." );
      19:         }
      20:     }
      21: }

    Die Undo Methode habe ich nicht implementiert. Das Projekt sieht nun sehr übersichtlich aus, hier nochmal der Auszug aus dem Solutionexplorer:

    shot9 

    Die TypeAlias.xml ist im Verzeichnis Common hinterlegt (Nicht zu sehen im Screenshot).

    Nach dem kompilieren wird das Package noch registriert. Bei der Registrierung treten in der Regel die meisten Fehler auf wenn man anfängt mit dem Guidance Automation Toolkit zu arbeiten. Wichtig ist einfach nur zu beachten das die eigentliche Fehlermeldung in einem gesonderten Output Fenster angezeigt wird.

    shot5

    In einer neuen Visual Studio 2005 Instanz teste ich das Package. Hierzu muß beachtet werden das es nicht unter den Projekttemplates auftaucht. Da es keinerlei Projekt- oder Solutiontemplates beinhaltet wird kann man es auch nicht als Basis für ein Projekt nehmen. Das Package wird über den Guidance Package Manager in einer kleinen Console Application hinzugefügt.

    shot6  

     Danach wählt man "Enable/Disable Packages..." aus.

    shot7

     Nun selektiere ich das MyFactory Package.

    shot8

    Im Guidance Navigator sieht man nun die Einführende Beschreibung des Packages und im Kontextmenü taucht auch dementsprechend das Rezept auf.

    shot10 shot11

    Das wars für diesen Teil. Das MyFactory Projekt stellt nun das Grundgerüst dar um die Factory auszubauen und die entsprechende Funktionalität zu implementieren.

    Im nächsten Teil werden näher auf das Binden von Rezepten eingehen und auch schauen welche Einschränkungen das momentane Release des Guidance Automation Toolkit hat.

  • Dariusz quatscht

    Screencast: Merging C++ and C# code into one assembly

    • 1 Comments

    I just produced a little screencast on the previous german post. This is just a test of the soapbox infrastructure to realize if the quality is good enough. Any feedback is welcome.

    By the way, I know that my english has lot of space for improvement.

  • Dariusz quatscht

    Mischen von C# und C++ Code in einem Assembly

    • 2 Comments

    Möchte man in einer nativen C++ Anwendung Funktionen aus der Managed Welt benutzen, bietet es sich an die Funktionalität per C++/CLI zu kapseln. Die Wrapper-Funktionen werden dann in einer DLL als Exportiert gekennzeichnet und können von C++ Clients per Linker hinzugezogen werden.

    In diesem Rahmen macht es Sinn die Abhängigkeiten zu reduzieren. Es ist wünschenswert die entsprechende C# Bibliothek in das gleiche C++/CLI Assembly zu packen.

    Dies wird aus Visual Studio 2005 nicht direkt unterstützt, sondern muss durch die Kommandozeile erledigt werden. Anhand eines kleinen Beispiels zeige ich im folgenden wie sich ein solches Assembly erstellen lässt.

    Nehmen wir an wir hätten folgende Klasse in C#

       1: using System;
       2: namespace Merging
       3: {
       4:     public class SomeClass
       5:     {
       6:         public string HelloWorld()
       7:         {
       8:             return "Hello, world.";
       9:         }
      10:     }
      11: }

    Das Kompilat wird nun nicht in Visual Studio 2005 direkt erstellt sondern über die Kommandozeile. Der Grund hierfür ist das Visual Studio 2005 in den möglichen Outputs nur folgende Werte zulässt

    shot1

    Es fehlt leider der Ausgabetyp Modul. Module lassen sich nämlich zu einem Assembly zusammen linken. Kompiliert man von der Kommandozeile so lässt sich dort auch ein Modul als Erstellungsziel spezifizieren.

    csc /t:module class1.cs
    Microsoft (R) Visual C# 2008 Compiler Beta 2 version 3.05.20706.1
    for Microsoft (R) .NET Framework version 3.5
    Copyright (C) Microsoft Corporation. All rights reserved.

    Anmerkung: Ich benutze zwar den 2008 Beta 2 compiler, funktioniert aber auch einwandfrei mit dem 2005 compiler.

    Als Ergebnis erhält man nun die Datei class1.netmodule. Jetzt kommt das Bindeglied: C++/CLI. Die Funktion HelloWorld() wird nun in einer C++ DLL durch eine Funktion NativeHelloWorld() zur Verfügung gestellt. Nach dem Erstellen eines Win32 DLL Projektes wird zuerst der Compiler Switch /CLR aktviert. Die Header Datei enthält folgende Definitionen:

       1: #define NATIVELIB_API __declspec(dllexport)
       2:  
       3: NATIVELIB_API void NativeHelloWorld();

    Die Implementierung der Funktion in meinem Beispiel sieht nun folgendermaßen aus

       1: NATIVELIB_API void NativeHelloWorld()
       2: {
       3:     Merging::SomeClass ^cl = gcnew Merging::SomeClass();
       4:     System::IntPtr pointer = 
       5: System::Runtime::InteropServices::Marshal::StringToHGlobalAuto( cl->HelloWorld() );
       6:     const wchar_t *result = static_cast<const wchar_t*>(pointer.ToPointer());
       7:  
       8:     wprintf_s( L"%s\n", result );
       9: }

    Zwei wichtige Dinge gilt es zu tätigen damit dieser Code kompiliert. Zum einen muss die Deklaration von Merging::SomeClass dem Compiler bekannt gemacht werden. Hierzu wird das Modul per #using statement eingebunden.

    #using "c:\Users\dparys\Documents\Proj\Merging\class1.netmodule"
    

    Des Weiteren muss dem Linker gesagt werden das er dieses Modul als Library betrachten soll.

    shot2

    Jetzt lässt sich die C++ DLL kompilieren und enthält nun den C# Code und den C++/CLI Wrapper Code in einem Assembly wie hier in ILDASM zu sehen.

    shot3

    Per DumpBin Kommandozeilen Tool lässt sich auch der Export anzeigen.

    c:\>users\dparys\proj\debug\dumpbin /exports NativeLib.dll
    
    Microsoft (R) COFF/PE Dumper Version 9.00.20706.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file NativeLib.dll
    
    File Type: DLL
    
      Section contains the following exports for NativeLib.dll
    
        00000000 characteristics
        46B17095 time date stamp Thu Aug 02 07:50:13 2007
            0.00 version
               1 ordinal base
               1 number of functions
               1 number of names
    
        ordinal hint RVA      name
    
              1    0 00001050 ?NativeHelloWorld@@YAXXZ = 
    ?NativeHelloWorld@@YAXXZ (void __cdecl NativeHelloWorld(void))
    
      Summary
    
            1000 .data
            6000 .rdata
            1000 .reloc
            1000 .rsrc
            3000 .text
    

    Somit kann die Funktion nun in einem nativen C++ Client genutzt werden, wie der folgende Code zeigt.

       1: #include "stdafx.h"
       2:  
       3: #define NATIVELIB_API __declspec(dllimport)
       4: NATIVELIB_API void NativeHelloWorld();
       5:  
       6:  
       7: int _tmain(int argc, _TCHAR* argv[])
       8: {
       9:     NativeHelloWorld();
      10: }
      11:  

    Bleibt noch Anzumerken das sich der ganze Prozess in einem Build-Skript am besten integrieren lässt. Mein Beispiel ist trivial und einfach. Es soll nur demonstrieren das es möglich ist. Bei größeren Bibliotheken sind alleine die Abhängigkeiten von Referenzen und die Parameter der einzelnen Tools eine Herausforderung für sich.

Page 1 of 2 (13 items) 12