c++ wcf web-services soap wsdl

Crear servicio WCF para clientes C++ no administrados



web-services soap (5)

¿Puede publicar un servicio web REST y usar la biblioteca COM MSXML? Ya debe estar instalado, tiene un analizador XML y una biblioteca HTTP.

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

Necesito clientes Windows C ++ no administrados para hablar con un servicio WCF. Los clientes C ++ podrían estar ejecutándose en Win2000 y versiones posteriores. Tengo un control sobre ambos servicios WCF y sobre qué API C ++ se está utilizando. Dado que se trata de una aplicación patentada, es preferible usar material de Microsoft siempre que sea posible, definitivamente no API con licencia de GNU. Aquellos de ustedes que lo tienen funcionando, ¿pueden compartir un proceso paso a paso sobre cómo hacerlo funcionar?

Investigué las siguientes opciones hasta ahora:

  • WWSAPI: no es bueno, no funcionará en clientes Win 2000.
  • Servidor ATL, usado siguiendo la guía como referencia. Seguí los pasos descritos (eliminé referencias de políticas y apisoné WSDL); sin embargo, el sDDL no puede utilizar el WSDL resultante.

¿Alguna idea más? Por favor, responda solo si realmente lo está haciendo usted mismo.

Edit1 : me disculpo por cualquier persona que pueda haber confundido: lo que estaba buscando era una forma de llamar al servicio WCF desde el cliente donde no está instalado .NET Framework, por lo que usar la biblioteca de ayuda basada en .NET no es una opción, debe ser puro C ++ no administrado


Crearía una clase administrada de C # para hacer el trabajo de WCF y exponer la clase como un objeto COM para los clientes de C ++.


La idea básica es escribir el código WCF para sus clientes en C # (es más fácil de esta manera) y usar un dll bridge Cll para cerrar la brecha entre su código C ++ no administrado y el código WCF administrado escrito en C #.

Aquí está el proceso paso a paso usando Visual Studio 2008 junto con .NET 3.5 SP1.

  1. Lo primero que debe hacer es crear el Servicio WCF y un medio para alojarlo. Si ya tiene esto, salte al paso 7 a continuación. De lo contrario, crea un Servicio de Windows NT siguiendo los pasos desde here . Use los nombres predeterminados que ofrece VS2008 para el proyecto y cualquier clase que se agregue al proyecto. Este servicio de Windows NT alojará el servicio WCF.

    • Agregue un servicio WCF llamado HelloService al proyecto. Para hacer esto, haga clic con el botón derecho en el proyecto en la ventana del Explorador de soluciones y seleccione la opción de menú Agregar | Nuevo elemento ... En el cuadro de diálogo Agregar nuevo elemento, seleccione la plantilla de servicio C # WCF y haga clic en el botón Agregar. Esto agrega HelloService al proyecto en forma de un archivo de interfaz (IHelloService.cs), un archivo de clase (HelloService.cs) y un archivo de configuración de servicio predeterminado (app.config).

    • Defina el HelloService así:

``

[ServiceContract] public interface IHelloService { [OperationContract] string SayHello(string name); } public class HelloService : IHelloService { public string SayHello(string name) { return String.Format("Hello, {0}!", name); } }

  • Modifique la clase Service1 creada en el Paso 1 anterior para que se vea así:

    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 { } } }

  • Construye el proyecto.

  • Abra el símbolo del sistema de Visual Studio 2008. Navegue al directorio de salida para el proyecto. Escriba lo siguiente: `installutil WindowsService1.exe ''Esto instala el servicio de Windows NT en su máquina local. Abra el panel de control de Servicios e inicie el servicio de Servicio1. Es importante hacer esto para que el Paso 9 a continuación funcione.

    1. Abra otra instancia de Visual Studio 2008 y cree una aplicación MFC, que está lo más lejos posible de WCF. Como ejemplo, simplemente creé una aplicación de diálogo MFC y agregué un ¡Hola! botón para ello. Haga clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccione la opción del menú Propiedades. En la configuración general, cambie el directorio de salida a ... / bin / Debug. En la configuración general de C / C ++, agregue .. / HelloServiceClientBridge a los directorios de inclusión adicionales. En la configuración general de Linker, agregue ... / Debug a los directorios adicionales de la biblioteca. Haga clic en el botón Aceptar.
  • En el menú Archivo, seleccione el elemento de menú Agregar | Nuevo proyecto ... Seleccione la plantilla de biblioteca de clase C #. Cambie el nombre a HelloServiceClient y haga clic en el botón Aceptar. Haga clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccione la opción del menú Propiedades. En la pestaña Generar, cambie la ruta de salida a .. / bin / Debug para que el ensamblaje y el archivo app.config estén en el mismo directorio que la aplicación MFC. Esta biblioteca contendrá la referencia de servicio, es decir, la clase de proxy WCF, al servicio Hello de WCF alojado en el servicio de Windows NT.

  • En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Referencias para el proyecto HelloServiceClient y seleccione la opción de menú Agregar referencia de servicio ... En el campo Dirección, escriba la dirección de Hello Service. Esto debería ser igual a la dirección base en el archivo app.config creado en el Paso 2 anterior. Haga clic en el botón Ir. El Hello Service debería aparecer en la lista de Servicios. Haga clic en el botón Aceptar para generar automáticamente la (s) clase (s) de proxy para el Servicio Hello. NOTA: parece que siempre me encuentro con problemas de compilación con el archivo Reference.cs generado por este proceso. No sé si lo estoy haciendo mal o si hay un error, pero la forma más fácil de arreglar esto es modificar el archivo Reference.cs directamente. El problema suele ser un problema de espacio de nombres y se puede solucionar con un mínimo esfuerzo. Solo ten en cuenta que esta es una posibilidad. Para este ejemplo, he cambiado el HelloServiceClient.ServiceReference1 a simplemente HelloService (junto con cualquier otro cambio requerido).

  • Para permitir que la aplicación MFC interactúe con el servicio WCF, necesitamos construir una DLL administrada de "puente" de C ++. En el menú Archivo, seleccione el elemento de menú Agregar | Nuevo proyecto ... Seleccione la plantilla del proyecto C ++ Win32. Cambie el nombre a HelloServiceClientBridge y haga clic en el botón Aceptar. Para la Configuración de la aplicación, cambie el Tipo de aplicación a DLL y marque la casilla de verificación Vaciar proyecto. Haga clic en el botón Finalizar.

  • Lo primero que debe hacer es modificar las propiedades del proyecto. Haga clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccione la opción del menú Propiedades. En la configuración general, cambie el Directorio de salida a ... / bin / Debug y cambie la opción Compatibilidad de Common Language Runtime con Common Language Runtime Support (/ clr). En la configuración de Marco y referencias, agregue una referencia a los ensamblados .NET System, System.ServiceModel y mscorlib. Haga clic en el botón Aceptar.

  • Agregue los siguientes archivos al proyecto HelloServiceClientBridge: HelloServiceClientBridge.h, IHelloServiceClientBridge.h y HelloServiceClientBridge.cpp.

  • Modifique el IHelloServiceClientBridge.h para que se vea así:

    #ifndef __IHelloServiceClientBridge_h__ #define __IHelloServiceClientBridge_h__ #include <string> #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__

  • Modifique HelloServiceClientBridge.h para que se vea así:

    #ifndef __HelloServiceClientBridge_h__ #define __HelloServiceClientBridge_h__ #include <vcclr.h> #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 sintaxis para el archivo .cpp usa C ++ administrado, lo que requiere cierto tiempo para acostumbrarse. Modifique el HelloServiceClientBridge.cpp para que se vea así:

    #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^> binding = gcnew WSHttpBinding(); gcroot<EndpointAddress^> address = gcnew EndpointAddress(gcnew String("http://localhost:8731/Design_Time_Addresses/WindowsService1/HelloService/")); gcroot<HelloService::HelloServiceClient^> 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<char *>(static_cast<void *>(ptr))); Marshal::FreeHGlobal(ptr); } catch (Exception ^) { client->Abort(); } return rv; }

  • Lo único que queda por hacer es actualizar la aplicación MFC para invocar la llamada de servicio WCF SayHello (). En el formulario MFC, haga doble clic en ¡Di Hola! para generar el manejador de eventos ButtonClicked. Haga que el controlador de eventos se vea así:

    #include "IHelloServiceClientBridge.h" #include <string> void CMFCApplicationDlg::OnBnClickedButton1() { try { std::string message = IHelloServiceClientBridge::SayHello("Your Name Here"); AfxMessageBox(CString(message.c_str())); } catch (...) { } }

  • Ejecute la aplicación y haga clic en ¡Di Hola! botón. Esto causará que la aplicación invoque el método SayHello () del WCF Hello Service alojado en el servicio de Windows NT (que aún debe estar ejecutándose, dicho sea de paso). El valor de retorno se muestra en un cuadro de mensaje.

Esperemos que pueda extrapolar de este simple ejemplo para adaptarse a sus necesidades. Si esto no funciona, házmelo saber para que pueda corregir la publicación.


Para aquellos que estén interesados, encontré una solución de servidor ATL semi-funcional. A continuación se muestra el código de host, observe que está utilizando BasicHttpBinding, es el único que funciona con 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();

el código para InlineXsdInWsdlBehavior se puede encontrar here . Es necesario realizar un cambio importante en InlineXsdInWsdlBehavior para que funcione correctamente con sproxy cuando se trata de tipos complejos. Es causado por el error en sproxy, que no abarca correctamente los alias del espacio de nombres, por lo que wsdl no puede tener alias de espacio de nombres repetidos o sproxy se va a destruir. Aquí están las funciones que necesita cambiar:

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 // <xsd:imports/> // List<XmlSchema> importsList = new List<XmlSchema>(); 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<XmlSchema> 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); } } } }

El siguiente paso es generar encabezado C ++:

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

y luego el programa C ++ se ve así:

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

El código resultante de C ++ maneja los tipos complejos bastante decentemente, excepto que no puede asignar NULL a los objetos.


Puede implementar un cliente SOAP de una manera sencilla usando el desactualizado MS Soap Toolkit . Desafortunadamente, no parece haber un reemplazo para esto fuera de mover a .NET.