Créer un service WCF pour les clients C ++ non gérés

Je dois faire en sorte que les clients Windows C ++ non gérés communiquent avec un service WCF. Les clients C ++ peuvent être exécutés sur Win2000 et versions ultérieures. J’ai un contrôle sur le service WCF et sur les API C ++ utilisées. Comme il s’agit d’une application propriétaire, il est préférable d’utiliser des éléments Microsoft dans la mesure du possible, certainement pas les API sous licence GNU. Ceux d’entre vous qui le font fonctionner, pouvez-vous partager un processus pas à pas pour le faire fonctionner?

J’ai étudié les options suivantes jusqu’à présent:

  • WWSAPI – pas bon, ne fonctionnera pas sur les clients Win 2000.
  • ATL Server, utilisé comme guide suivant . J’ai suivi les étapes décrites (suppression des refs de règles et aplatissement de WSDL), mais le WSDL résultant n’est toujours pas utilisable par sproxy

Plus d’idées? S’il vous plaît répondez seulement si vous le faites réellement travailler vous-même.

Edit1 : Je m’excuse pour ceux que je pourrais avoir confondus: ce que je cherchais était un moyen d’appeler le service WCF à partir de client (s) où aucun framework .NET n’est installé, donc l’utilisation de la bibliothèque d’assistance .NET n’est pas une option, il doit être pur C ++ non géré

L’idée de base est d’écrire le code WCF pour vos clients en C # (c’est plus simple) et d’utiliser un dll bridge C ++ pour combler le fossé entre votre code C ++ non géré et le code WCF géré écrit en C #.

Voici le processus étape par étape utilisant Visual Studio 2008 avec .NET 3.5 SP1.

  1. La première chose à faire est de créer le service WCF et un moyen de l’héberger. Si vous avez déjà cela, passez à l’étape 7 ci-dessous. Sinon, créez un service Windows NT en suivant les étapes ci- dessous . Utilisez les noms par défaut proposés par VS2008 pour le projet et toutes les classes ajoutées au projet. Ce service Windows NT hébergera le service WCF.

    • Ajoutez un service WCF nommé HelloService au projet. Pour ce faire, cliquez avec le bouton droit sur le projet dans la fenêtre Explorateur de solutions et sélectionnez l’élément de menu Ajouter | Nouvel élément … Dans la boîte de dialog Ajouter un nouvel élément, sélectionnez le modèle de service C # WCF et cliquez sur le bouton Ajouter. Cela ajoute le HelloService au projet sous la forme d’un fichier d’interface (IHelloService.cs), d’un fichier de classe (HelloService.cs) et d’un fichier de configuration de service par défaut (app.config).

    • Définissez le HelloService comme ceci:

[ServiceContract] public interface IHelloService { [OperationContract] ssortingng SayHello(ssortingng name); } public class HelloService : IHelloService { public ssortingng SayHello(ssortingng name) { return Ssortingng.Format("Hello, {0}!", name); } } 
  • Modifiez la classe Service1 créée à l’étape 1 ci-dessus pour qu’elle ressemble à ceci:

     using System.ServiceModel; using System.ServiceProcess; public partial class Service1 : ServiceBase { private ServiceHost _host; public Service1() { InitializeComponent(); } protected override void OnStart( ssortingng [] args ) { _host = new ServiceHost( typeof( HelloService ) ); _host.Open(); } protected override void OnStop() { try { if ( _host.State != CommunicationState.Closed ) { _host.Close(); } } catch { } } } 
  • Construire le projet

  • Ouvrez l’invite de commandes Visual Studio 2008. Accédez au répertoire de sortie du projet. Tapez la commande suivante: `installutil WindowsService1.exe ‘Ceci installe le service Windows NT sur votre ordinateur local. Ouvrez le panneau de configuration Services et démarrez le service Service1. Il est important de le faire pour que l’étape 9 ci-dessous fonctionne.

    1. Ouvrez une autre instance de Visual Studio 2008 et créez une application MFC qui est à peu près aussi éloignée de WCF. Par exemple, j’ai simplement créé une application de dialog MFC et ajouté un Say Hello! bouton à elle. Cliquez avec le bouton droit sur le projet dans l’Explorateur de solutions et sélectionnez l’option de menu Propriétés. Sous les parameters généraux, remplacez le répertoire de sortie par .. \ bin \ Debug. Sous les parameters généraux C / C ++, ajoutez .. \ HelloServiceClientBridge aux répertoires d’inclusion supplémentaires. Sous les parameters généraux de l’éditeur de liens, ajoutez .. \ Debug aux répertoires de bibliothèque supplémentaires. Cliquez sur le bouton OK.
  • Dans le menu Fichier, sélectionnez l’élément de menu Ajouter | Nouveau projet … Sélectionnez le modèle de bibliothèque de classes C #. Changez le nom en HelloServiceClient et cliquez sur le bouton OK. Cliquez avec le bouton droit sur le projet dans l’Explorateur de solutions et sélectionnez l’option de menu Propriétés. Dans l’onglet Générer, modifiez le chemin de sortie en .. \ bin \ Debug afin que le fichier assembly et app.config se trouve dans le même répertoire que l’application MFC. Cette bibliothèque contiendra la référence de service, à savoir la classe proxy WCF, au service Hello WCF hébergé dans le service Windows NT.

  • Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le dossier Références du projet HelloServiceClient et sélectionnez l’option de menu Ajouter une référence de service. Dans le champ Adresse, tapez l’adresse de Hello Service. Cela devrait être égal à l’adresse de base dans le fichier app.config créé à l’étape 2 ci-dessus. Cliquez sur le bouton Go. Le service Bonjour doit apparaître dans la liste des services. Cliquez sur le bouton OK pour générer automatiquement la ou les classes proxy pour le service Hello. REMARQUE: il semble que je rencontre toujours des problèmes de compilation avec le fichier Reference.cs généré par ce processus. Je ne sais pas si je le fais mal ou s’il y a un bogue, mais le moyen le plus simple de résoudre ce problème est de modifier directement le fichier Reference.cs. Le problème est généralement un problème d’espace de noms et peut être résolu avec un minimum d’efforts. Sachez simplement que c’est une possibilité. Pour cet exemple, j’ai changé le HelloServiceClient.ServiceReference1 en simplement HelloService (avec tous les autres changements requirejs).

  • Pour permettre à l’application MFC d’interagir avec le service WCF, nous devons créer une DLL “pont” C ++ gérée. Dans le menu Fichier, sélectionnez l’élément de menu Ajouter | Nouveau projet … Sélectionnez le modèle de projet C ++ Win32. Changez le nom en HelloServiceClientBridge et cliquez sur le bouton OK. Pour les parameters d’application, modifiez le type d’application en DLL et cochez la case Projet vide. Cliquez sur le bouton Terminer.

  • La première chose à faire est de modifier les propriétés du projet. Cliquez avec le bouton droit sur le projet dans l’Explorateur de solutions et sélectionnez l’option de menu Propriétés. Sous les parameters généraux, remplacez le répertoire de sortie par .. \ bin \ Debug et remplacez l’option Common Language Runtime Support par Common Language Runtime Support (/ clr). Sous les parameters Framework et References, ajoutez une référence aux assemblys .NET System, System.ServiceModel et mscorlib. Cliquez sur le bouton OK.

  • Ajoutez les fichiers suivants au projet HelloServiceClientBridge – HelloServiceClientBridge.h, IHelloServiceClientBridge.h et HelloServiceClientBridge.cpp.

  • Modifiez le IHelloServiceClientBridge.h pour qu’il ressemble à ceci:

     #ifndef __IHelloServiceClientBridge_h__ #define __IHelloServiceClientBridge_h__ #include  #ifdef HELLOSERVICECLIENTBRIDGE_EXPORTS #define DLLAPI __declspec(dllexport) #else #define DLLAPI __declspec(dllimport) #pragma comment (lib, "HelloServiceClientBridge.lib") // if importing, link also #endif class DLLAPI IHelloServiceClientBridge { public: static std::ssortingng SayHello(char const *name); }; #endif // __IHelloServiceClientBridge_h__ 
  • Modifiez le HelloServiceClientBridge.h pour qu’il ressemble à ceci:

     #ifndef __HelloServiceClientBridge_h__ #define __HelloServiceClientBridge_h__ #include  #include "IHelloServiceClientBridge.h" #ifdef _DEBUG #using<..\HelloServiceClient\bin\Debug\HelloServiceClient.dll> #else #using<..\HelloServiceClient\bin\Release\HelloServiceClient.dll> #endif class DLLAPI HelloServiceClientBridge : IHelloServiceClientBridge { }; #endif // __HelloServiceClientBridge_h__ 
  • La syntaxe du fichier .cpp utilise C ++ géré, ce qui nécessite un certain temps d’adaptation. Modifiez le HelloServiceClientBridge.cpp pour qu’il ressemble à ceci:

     #include "HelloServiceClientBridge.h" using namespace System; using namespace System::Runtime::InteropServices; using namespace System::ServiceModel; using namespace System::ServiceModel::Channels; std::ssortingng IHelloServiceClientBridge::SayHello(char const *name) { std::ssortingng rv; gcroot binding = gcnew WSHttpBinding(); gcroot address = gcnew EndpointAddress(gcnew Ssortingng("http://localhost:8731/Design_Time_Addresses/WindowsService1/HelloService/")); gcroot client = gcnew HelloService::HelloServiceClient(binding, address); try { // call to WCF Hello Service Ssortingng^ message = client->SayHello(gcnew Ssortingng(name)); client->Close(); // marshal from managed ssortingng back to unmanaged ssortingng IntPtr ptr = Marshal::SsortingngToHGlobalAnsi(message); rv = std::ssortingng(reinterpret_cast(static_cast(ptr))); Marshal::FreeHGlobal(ptr); } catch (Exception ^) { client->Abort(); } return rv; } 
  • La seule chose à faire est de mettre à jour l’application MFC pour appeler l’appel de service WCH SayHello (). Sur le formulaire MFC, double-cliquez sur Say Hello! bouton pour générer le gestionnaire d’événements ButtonClicked. Faites en sorte que le gestionnaire d’événements ressemble à ceci:

     #include "IHelloServiceClientBridge.h" #include  void CMFCApplicationDlg::OnBnClickedButton1() { try { std::ssortingng message = IHelloServiceClientBridge::SayHello("Your Name Here"); AfxMessageBox(CSsortingng(message.c_str())); } catch (...) { } } 
  • Exécutez l’application et cliquez sur Say Hello! bouton. Cela provoquera l’application appeler la méthode SayHello () du service Hello WCF hébergé dans le service Windows NT (qui devrait toujours être en cours d’exécution). La valeur de retour est ensuite affichée dans une boîte de message.

J’espère que vous pouvez extrapoler à partir de cet exemple simple pour répondre à vos besoins. Si cela ne fonctionne pas, s’il vous plaît faites le moi savoir afin que je puisse corriger le message.

Pour ceux qui sont intéressés, j’ai trouvé une solution semi-fonctionnelle ATL Server. Voici le code hôte, notez qu’il utilise BasicHttpBinding, c’est le seul qui fonctionne avec ATL Server:

  var svc = new Service1(); Uri uri = new Uri("http://localhost:8200/Service1"); ServiceHost host = new ServiceHost(typeof(Service1), uri); var binding = new BasicHttpBinding(); ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IService1), binding, uri); endpoint.Behaviors.Add(new InlineXsdInWsdlBehavior()); host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); var mex = host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); host.Open(); Console.ReadLine(); 

code pour InlineXsdInWsdlBehavior peut être trouvé ici . Une modification importante doit être apscope à InlineXsdInWsdlBehavior pour qu’il fonctionne correctement avec le sproxy lorsque des types complexes sont impliqués. Il est dû au bogue du sproxy, qui ne couvre pas correctement les alias d’espace de nommage, de sorte que wsdl ne peut pas contenir des alias d’espaces de noms ou des sproxy répétés. Voici les fonctions à modifier:

  public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context) { int tnsCount = 0; XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas; foreach (WsdlDescription wsdl in exporter.GeneratedWsdlDocuments) { // // Recursively find all schemas imported by this wsdl // and then add them. In the process, remove any //  // List importsList = new List(); foreach (XmlSchema schema in wsdl.Types.Schemas) { AddImportedSchemas(schema, schemaSet, importsList, ref tnsCount); } wsdl.Types.Schemas.Clear(); foreach (XmlSchema schema in importsList) { RemoveXsdImports(schema); wsdl.Types.Schemas.Add(schema); } } } private void AddImportedSchemas(XmlSchema schema, XmlSchemaSet schemaSet, List importsList, ref int tnsCount) { foreach (XmlSchemaImport import in schema.Includes) { ICollection realSchemas = schemaSet.Schemas(import.Namespace); foreach (XmlSchema ixsd in realSchemas) { if (!importsList.Contains(ixsd)) { var new_namespaces = new XmlSerializerNamespaces(); foreach (var ns in ixsd.Namespaces.ToArray()) { var new_pfx = (ns.Name == "tns") ? ssortingng.Format("tns{0}", tnsCount++) : ns.Name; new_namespaces.Add(new_pfx, ns.Namespace); } ixsd.Namespaces = new_namespaces; importsList.Add(ixsd); AddImportedSchemas(ixsd, schemaSet, importsList, ref tnsCount); } } } } 

L’étape suivante consiste à générer un en-tête C ++:

 sproxy.exe /wsdl http://localhost:8200/Service1?wsdl 

puis le programme C ++ ressemble à ceci:

 using namespace Service1; CoInitializeEx( NULL, COINIT_MULTITHREADED ); { CService1T cli; cli.SetUrl( _T("http://localhost:8200/Service1") ); HRESULT hr = cli.HelloWorld(); //todo: analyze hr } CoUninitialize(); return 0; 

Le code C ++ résultant gère assez bien les types complexes, sauf qu’il ne peut pas affecter NULL aux objects.

Je créerais une classe gérée C # pour effectuer le travail WCF et exposer la classe en tant qu’object COM aux clients C ++.

Vous pouvez implémenter un client SOAP assez facilement en utilisant le kit de maintenance obsolète MS Soap . Malheureusement, il ne semble pas y avoir de solution de rechange en dehors du passage à .NET.

Pouvez-vous publier un service Web REST et utiliser la bibliothèque COM MSXML – doit déjà être installé, possède un parsingur XML et une bibliothèque HTTP.

http://msdn.microsoft.com/en-us/library/ms763742.aspx