Crear servicio WCF para clientes C++ no administrados

12 minutos de lectura

Crear servicio WCF para clientes C no administrados
galés

Necesito que los clientes de Windows C++ no administrados hablen con un servicio WCF. Los clientes de C++ podrían ejecutarse en Win2000 y versiones posteriores. Tengo un control sobre el servicio WCF y qué API de C++ se está utilizando. Dado que es para una aplicación propietaria, es preferible usar material de Microsoft siempre que sea posible, definitivamente no API con licencia GNU. Aquellos de ustedes que lo tienen funcionando, ¿pueden compartir un proceso paso a paso para que funcione?

He investigado las siguientes opciones hasta ahora:

  • WWSAPI: no es bueno, no funcionará en clientes Win 2000.
  • Servidor ATL, usado siguiente guía como una referencia. Seguí los pasos descritos (eliminar referencias de política y aplanar WSDL), sin embargo, el WSDL resultante todavía no puede ser utilizado por sproxy

¿Alguna idea más? Responda solo si realmente lo tiene funcionando usted mismo.

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

  • Pido disculpas por la demora. He actualizado mi respuesta. Espero eso ayude.

    –Matt Davis

    28 de marzo de 2009 a las 0:06

  • Puede modificar el servicio WCF para ofrecer extremos SOAP y REST y luego usar el extremo REST de C++. (Siempre que sus tipos de datos se analicen fácilmente en C++). Ver: stackoverflow.com/questions/186631/…

    – Jesse Chisholm

    21 de marzo de 2013 a las 18:51

1646755393 632 Crear servicio WCF para clientes C no administrados
matt davis

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 puente dll de C++ para cerrar la brecha entre su código C++ no administrado y el código WCF administrado escrito en C#.

Este es 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, cree un servicio de Windows NT siguiendo los pasos de aquí. Utilice los nombres predeterminados ofrecidos por 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 ello, haga clic con el botón derecho en el proyecto en la ventana del Explorador de soluciones y seleccione el elemento de menú Agregar|Nuevo elemento…. En el cuadro de diálogo Agregar nuevo elemento, seleccione la plantilla Servicio WCF de C# 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 {
            }
        }
    }
    
  • Construya el proyecto.

  • Abra el símbolo del sistema de Visual Studio 2008. Navegue hasta el directorio de salida del 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 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 es lo más lejano que puede obtener de WCF. Como ejemplo, simplemente creé una aplicación MFC de diálogo y agregué un Say Hello! botón para ello. Haga clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccione la opción de menú Propiedades. En 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 del vinculador, agregue ..\Debug a los directorios de biblioteca adicionales. Haga clic en el botón Aceptar.
  • En el menú Archivo, seleccione el elemento de menú Agregar|Nuevo proyecto…. Seleccione la plantilla Biblioteca de clases de 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 de menú Propiedades. En la pestaña Generar, cambie la ruta de salida a ..\bin\Debug para que el ensamblado y el archivo app.config estén en el mismo directorio que la aplicación MFC. Esta biblioteca contendrá la referencia del servicio, es decir, la clase de proxy de WCF, para el servicio WCF Hello alojado en el servicio de Windows NT.

  • En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Referencias del 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. Debe 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 Servicio Hello debería aparecer en la lista de Servicios. Haga clic en el botón Aceptar para generar automáticamente las clases 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 solucionarlo es modificar el archivo Reference.cs directamente. El problema suele ser un problema de espacio de nombres y se puede solucionar con un esfuerzo mínimo. Solo tenga en cuenta que esta es una posibilidad. Para este ejemplo, cambié HelloServiceClient.ServiceReference1 simplemente a HelloService (junto con cualquier otro cambio requerido).

  • Para permitir que la aplicación MFC interactúe con el servicio WCF, necesitamos crear una DLL “puente” de C++ administrada. En el menú Archivo, seleccione el elemento de menú Agregar|Nuevo proyecto…. Seleccione la plantilla 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 Proyecto vacío. Haga clic en el botón Finalizar.

  • Lo primero que hay que 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 de menú Propiedades. En la configuración General, cambie el Directorio de salida a ..\bin\Debug y cambie la opción Compatibilidad con Common Language Runtime a Compatibilidad con Common Language Runtime (/clr). En la configuración 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 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 lleva un tiempo acostumbrarse. Modifique 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 el botón Say Hello! botón para generar el controlador 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 hará que la aplicación invoque el método SayHello() del Servicio WCF Hello alojado en el Servicio de Windows NT (que, por cierto, aún debería estar ejecutándose). A continuación, el valor devuelto se muestra en un cuadro de mensaje.

Con suerte, puede extrapolar a partir de este ejemplo simple para satisfacer sus necesidades. Si esto no funciona, por favor hágamelo saber para que pueda arreglar la publicación.

  • Matt, primero quería agradecerte por todo el trabajo que dedicaste a escribir esta guía. Seguramente sería útil para muchos, pero lamentablemente no para mí. Lo que estaba buscando era una llamada pura no administrada al servicio WCF, no un proxy .NET. Es posible que el cliente no tenga marco instalado, lo siento, no lo aclaré

    – galés

    30 de marzo de 2009 a las 15:48

  • @Matt: Lo siento si hay tantas ediciones. Creo que un par de nosotros nos encontramos tratando de arreglar el formato del código.

    – gnovicio

    8 de enero de 2010 a las 21:45

  • gracias, esto es precisamente lo que necesito – ¡gracias por el brillante trabajo!

    – Contango

    5 de octubre de 2010 a las 13:04

  • Creo que debería ser “En la configuración general del enlazador, agregue ..\bin\Debug a los directorios de bibliotecas adicionales. Haga clic en el botón Aceptar”.

    – jaccus

    14 de noviembre de 2012 a las 9:51


  • Gracias, esta técnica funcionó bien para mí. Realmente aprecio que compartas este conocimiento. Es una pena que sproxy se ahogue con el wsdl generado por el servicio WCF y nos obligue a recurrir a esta solución enrevesada.

    – PINTAG

    12 de agosto de 2014 a las 16:27

Crear servicio WCF para clientes C no administrados
galés

Para aquellos que estén interesados, encontré una solución de servidor ATL semifuncional. El siguiente es el código de host, observe que está usando BasicHttpBinding, es el único que funciona con el servidor 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();

se pudo encontrar el código para InlineXsdInWsdlBehavior aquí . Se debe 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 analiza correctamente los alias de espacio de nombres, por lo que wsdl no puede tener alias de espacio de nombres repetidos o sproxy fallará. 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 el 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 C++ resultante maneja tipos complejos bastante decentemente, excepto que no puede asignar NULL a los objetos.

  • Intenté este enfoque, pero sproxy aún no podía manejar el wsdl generado por mi servicio bastante simple.

    – PINTAG

    12 de agosto de 2014 a las 16:28

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

Puede implementar un cliente SOAP con cierta facilidad usando el obsoleto Kit de herramientas de jabón MS. Desafortunadamente, no parece haber un reemplazo para esto fuera de pasar a .NET.

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

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

  • Puede hacer que su servicio WCF ofrezca tanto SOAP como REST y use el punto final REST de C++. Ver: stackoverflow.com/questions/186631/…

    – Jesse Chisholm

    21 de marzo de 2013 a las 18:49


  • Puede hacer que su servicio WCF ofrezca tanto SOAP como REST y use el punto final REST de C++. Ver: stackoverflow.com/questions/186631/…

    – Jesse Chisholm

    21 de marzo de 2013 a las 18:49


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad