Crea servizio WCF per client C ++ non gestiti

Devo ottenere client Windows C ++ non gestiti per comunicare con un servizio WCF. I client C ++ potrebbero essere in esecuzione su Win2000 e versioni successive. Ho un controllo su entrambi i servizi WCF e quale API C ++ viene utilizzata. Dato che si tratta di un’applicazione proprietaria, è preferibile utilizzare le risorse di Microsoft laddove ansible, sicuramente non le API con licenza GNU. Quelli di voi che lo hanno fatto, potete condividere un processo passo-passo su come farlo funzionare?

Ho studiato le seguenti opzioni finora:

  • WWSAPI: non valido, non funzionerà con i client Win 2000.
  • Server ATL, utilizzato come guida di riferimento. Ho seguito i passaggi delineati (rimuovi i criteri di policy e applico WSDL), tuttavia il WSDL risultante non è ancora utilizzabile da sproxy

Altre idee? Per favore rispondi solo se in realtà lo stai facendo.

Edit1 : Mi scuso per chiunque possa aver confuso: quello che stavo cercando era un modo per chiamare il servizio WCF dai client in cui non è installato .NET framework, quindi l’uso della libreria helper basata su .NET non è un’opzione, deve essere puro C ++ non gestito

L’idea di base è scrivere il codice WCF per i tuoi clienti in C # (è più semplice in questo modo) e usare una DLL bridge C ++ per colmare il divario tra il tuo codice C ++ non gestito e il codice WCF gestito scritto in C #.

Ecco la procedura dettagliata che utilizza Visual Studio 2008 insieme a .NET 3.5 SP1.

  1. La prima cosa da fare è creare il servizio WCF e un mezzo per ospitarlo. Se hai già questo, vai al passaggio 7 di seguito. In caso contrario, creare un servizio di Windows NT seguendo i passaggi da qui . Utilizzare i nomi predefiniti offerti da VS2008 per il progetto e le classi aggiunte al progetto. Questo servizio Windows NT ospiterà il servizio WCF.

    • Aggiungere un servizio WCF denominato HelloService al progetto. Per fare ciò, fare clic con il tasto destro del mouse sul progetto nella finestra Esplora soluzioni e selezionare la voce di menu Aggiungi | Nuova voce …. Nella finestra di dialogo Aggiungi nuovo elemento, selezionare il modello di servizio WCF C # e fare clic sul pulsante Aggiungi. Ciò aggiunge HelloService al progetto sotto forma di un file di interfaccia (IHelloService.cs), un file di class (HelloService.cs) e un file di configurazione del servizio predefinito (app.config).

    • Definisci HelloService in questo modo:

[ServiceContract] public interface IHelloService { [OperationContract] string SayHello(string name); } public class HelloService : IHelloService { public string SayHello(string name) { return String.Format("Hello, {0}!", name); } } 
  • Modificare la class Service1 creata nel passaggio 1 in alto per apparire come segue:

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

  • Aprire il prompt dei comandi di Visual Studio 2008. Passare alla directory di output per il progetto. Digitare quanto segue: `installutil WindowsService1.exe ‘Questo installa il servizio di Windows NT sul computer locale. Aprire il pannello di controllo Servizi e avviare il servizio Service1. È importante eseguire questa operazione affinché il passaggio 9 sottostante funzioni.

    1. Aprire un’altra istanza di Visual Studio 2008 e creare un’applicazione MFC, che è il più lontano ansible da WCF. Ad esempio, ho semplicemente creato un’applicazione di dialogo MFC e aggiunto Say Hello! pulsante ad esso. Fare clic con il tasto destro del mouse sul Solution Explorer e selezionare l’opzione del menu Proprietà. Sotto le impostazioni generali, modificare la directory di output su .. \ bin \ debug. Sotto le impostazioni generali di C / C ++, aggiungere .. \ HelloServiceClientBridge alle Directory di inclusione aggiuntive. Sotto le impostazioni generali di Linker, aggiungere .. \ Debug alle directory di libreria aggiuntive. Fai clic sul pulsante OK.
  • Dal menu File, selezionare la voce di menu Aggiungi | Nuovo progetto …. Seleziona il modello di libreria di classi C #. Cambia il nome in HelloServiceClient e fai clic sul pulsante OK. Fare clic con il tasto destro del mouse sul Solution Explorer e selezionare l’opzione del menu Proprietà. Nella scheda Crea, modificare il percorso di output in .. \ bin \ Debug in modo che il file assembly e app.config si trovi nella stessa directory dell’applicazione MFC. Questa libreria conterrà il riferimento del servizio, ovvero la class del proxy WCF, al servizio Hello WCF ospitato nel servizio Windows NT.

  • In Esplora soluzioni, fare clic con il pulsante destro del mouse sulla cartella Riferimenti per il progetto HelloServiceClient e selezionare l’opzione di menu Aggiungi riferimento al servizio …. Nel campo Indirizzo, digitare l’indirizzo di Hello Service. Questo dovrebbe essere uguale all’indirizzo di base nel file app.config creato nel passaggio 2 sopra. Fai clic sul pulsante Vai. Il servizio Hello dovrebbe apparire nell’elenco dei servizi. Fare clic sul pulsante OK per generare automaticamente le classi proxy per il servizio Hello. Nota: mi sembra di incorrere sempre in problemi di compilazione con il file Reference.cs generato da questo processo. Non so se sto sbagliando o se c’è un bug, ma il modo più semplice per risolvere questo problema è modificare direttamente il file Reference.cs. Il problema è in genere un problema di namespace e può essere risolto con il minimo sforzo. Basta essere consapevoli che questa è una possibilità. Per questo esempio, ho cambiato HelloServiceClient.ServiceReference1 in HelloService semplicemente (insieme a qualsiasi altra modifica richiesta).

  • Per consentire all’applicazione MFC di interagire con il servizio WCF, è necessario creare una DLL “bridge” gestita in C ++. Dal menu File, selezionare la voce di menu Aggiungi | Nuovo progetto …. Selezionare il modello di progetto Win32 C ++. Cambia il nome in HelloServiceClientBridge e fai clic sul pulsante OK. Per Impostazioni applicazione, modificare il Tipo di applicazione in DLL e selezionare la casella di controllo Progetto vuoto. Fai clic sul pulsante Fine.

  • La prima cosa da fare è modificare le proprietà del progetto. Fare clic con il tasto destro del mouse sul Solution Explorer e selezionare l’opzione del menu Proprietà. In Impostazioni generali, modificare la directory di output in .. \ bin \ Debug e modificare l’opzione di supporto Common Language Runtime al supporto Common Language Runtime (/ clr). Sotto le impostazioni Framework e References, aggiungere un riferimento a .NET System, System.ServiceModel e mscorlib assembly. Fai clic sul pulsante OK.

  • Aggiungere i seguenti file al progetto HelloServiceClientBridge: HelloServiceClientBridge.h, IHelloServiceClientBridge.h e HelloServiceClientBridge.cpp.

  • Modifica IHelloServiceClientBridge.h come segue:

     #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::string SayHello(char const *name); }; #endif // __IHelloServiceClientBridge_h__ 
  • Modifica HelloServiceClientBridge.h come segue:

     #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 syntax per il file .cpp utilizza il C ++ gestito, che richiede un po ‘di tempo per abituarsi. Modifica HelloServiceClientBridge.cpp in modo che assomigli a questo:

     #include "HelloServiceClientBridge.h" using namespace System; using namespace System::Runtime::InteropServices; using namespace System::ServiceModel; using namespace System::ServiceModel::Channels; std::string IHelloServiceClientBridge::SayHello(char const *name) { std::string rv; gcroot binding = gcnew WSHttpBinding(); gcroot address = gcnew EndpointAddress(gcnew String("http://localhost:8731/Design_Time_Addresses/WindowsService1/HelloService/")); gcroot client = gcnew HelloService::HelloServiceClient(binding, address); try { // call to WCF Hello Service String^ message = client->SayHello(gcnew String(name)); client->Close(); // marshal from managed string back to unmanaged string IntPtr ptr = Marshal::StringToHGlobalAnsi(message); rv = std::string(reinterpret_cast(static_cast(ptr))); Marshal::FreeHGlobal(ptr); } catch (Exception ^) { client->Abort(); } return rv; } 
  • L’unica cosa che rimane da fare è aggiornare l’applicazione MFC per richiamare la chiamata di servizio WCF SayHello (). Nel modulo MFC, fai doppio clic su Say Hello! pulsante per generare il gestore di eventi ButtonClicked. Fai in modo che il gestore di eventi assomigli a questo:

     #include "IHelloServiceClientBridge.h" #include  void CMFCApplicationDlg::OnBnClickedButton1() { try { std::string message = IHelloServiceClientBridge::SayHello("Your Name Here"); AfxMessageBox(CString(message.c_str())); } catch (...) { } } 
  • Esegui l’applicazione e fai clic su Say Hello! pulsante. Ciò farà sì che l’applicazione invochi il metodo SayHello () del servizio Hello WCF ospitato nel servizio di Windows NT (che comunque dovrebbe essere ancora in esecuzione). Il valore di ritorno viene quindi visualizzato in una finestra di messaggio.

Si spera che tu possa estrapolare da questo semplice esempio per soddisfare le tue esigenze. Se questo non funziona, per favore fatemelo sapere così posso sistemare il post.

Per coloro che sono interessati, ho trovato una soluzione ATL Server semi-funzionante. Di seguito è riportato il codice host, si noti che sta utilizzando BasicHttpBinding, è l’unico che funziona con il server ATL:

  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(); 

il codice per InlineXsdInWsdlBehavior potrebbe essere trovato qui . È necessario apportare una modifica importante a InlineXsdInWsdlBehavior affinché funzioni correttamente con sproxy quando sono coinvolti tipi complessi. È causato dal bug in sproxy, che non applica correttamente gli alias dello spazio dei nomi, quindi wsdl non può avere alias di spazi dei nomi che si ripetono o lo sproxy andrà a schifo. Ecco le funzioni che devono cambiare:

  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") ? string.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); } } } } 

Il passo successivo è generare l’intestazione C ++:

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

e quindi il programma C ++ si presenta così:

 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; 

Il codice C ++ risultante gestisce i tipi complessi piuttosto decentemente, tranne che non può assegnare NULL agli oggetti.

Vorrei creare una class gestita C # per eseguire il lavoro WCF ed esporre la class come un object COM ai client C ++.

È ansible implementare un client SOAP in qualche modo utilizzando facilmente MS Soap Toolkit deprecato. Sfortunatamente, non sembra esserci un sostituto per questo al di fuori del passaggio a .NET.

È ansible pubblicare un servizio Web REST e utilizzare la libreria COM MSXML, dovrebbe essere già installato, ha un parser XML e una libreria HTTP.

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