Подтвердить что ты не робот

Создать службу WCF для неуправляемых клиентов С++

Мне нужно, чтобы неуправляемые клиенты Windows С++ разговаривали с сервисом WCF. Клиенты С++ могут работать на Win2000 и более поздних версиях. У меня есть контроль над службой WCF и какой С++ API используется. Поскольку для проприетарного приложения предпочтительнее использовать материал Microsoft, где это возможно, а не API, лицензированный GNU. Те из вас, у кого есть работа, можете поделиться пошаговым процессом, как заставить его работать?

Я изучил следующие варианты:

  • WWSAPI - не очень хорошо, не будет работать на клиентах Win 2000.
  • ATL Server, используя следующее руководство в качестве ссылки. Я выполнил описанные шаги (удалите ссылки на политику и сгладьте WSDL), однако полученный WSDL по-прежнему не используется sproxy

Больше идей? Пожалуйста, ответьте только в том случае, если вы на самом деле работаете самостоятельно.

Edit1. Я прошу прощения за всех, кого я мог смутить: то, что я искал, было способом вызвать службу WCF у клиентов, где не установлена ​​.NET framework, поэтому используйте. NET-вспомогательная библиотека не является опцией, она должна быть чистой неуправляемой С++

4b9b3361

Ответ 1

Для тех, кто заинтересован, я нашел одно полурабочее решение ATL Server. Ниже приведен код хоста, обратите внимание, что он использует BasicHttpBinding, это единственный, который работает с сервером 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();

для InlineXsdInWsdlBehavior можно найти здесь. Одно важное изменение необходимо сделать для InlineXsdInWsdlBehavior, чтобы он работал правильно с sproxy, когда задействованы сложные типы. Это вызвано ошибкой в ​​sproxy, которая неправильно заполняет псевдонимы пространства имен, поэтому wsdl не может повторять псевдонимы пространства имен или sproxy. Здесь функции, которые необходимо изменить:

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

Следующий шаг - создать заголовок С++:

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

а затем программа на С++ выглядит следующим образом:

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;

Результат С++-кода обрабатывает сложные типы довольно прилично, за исключением того, что он не может назначить NULL для объектов.

Ответ 2

Основная идея состоит в том, чтобы написать код WCF для ваших клиентов на С# (это просто проще) и использовать dll для сценариев С++ для устранения разрыва между вашим неуправляемым кодом С++ и управляемым кодом WCF, написанным на С#.

Ниже представлен пошаговый процесс с использованием Visual Studio 2008 вместе с .NET 3.5 SP1.

  • Первое, что нужно сделать, это создать службу WCF и средство ее размещения. Если у вас уже есть это, перейдите к шагу 7 ниже. В противном случае создайте службу Windows NT, следуя инструкциям здесь. Используйте имена по умолчанию, предлагаемые VS2008 для проекта, и любые классы, добавленные в проект. Эта служба Windows NT будет обслуживать службу WCF.

    • Добавьте в проект службу WCF с именем HelloService. Для этого щелкните правой кнопкой мыши проект в окне "Обозреватель решений" и выберите пункт "Добавить новый элемент...". В диалоговом окне "Добавить новый элемент" выберите шаблон службы WCF С# и нажмите кнопку "Добавить". Это добавляет HelloService к проекту в виде файла интерфейса (IHelloService.cs), файла класса (HelloService.cs) и файла конфигурации службы по умолчанию (app.config).

    • Определите HelloService следующим образом:

``

    [ServiceContract]
    public interface IHelloService
    {
        [OperationContract]
        string SayHello(string name);
    }
    public class HelloService : IHelloService
    {
        public string SayHello(string name)
        {
            return String.Format("Hello, {0}!", name);
        }
    }
  • Измените класс Service1, созданный на шаге 1 выше, чтобы выглядеть так:

    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 {
            }
        }
    }
    
  • Создайте проект.

  • Откройте командную строку Visual Studio 2008. Перейдите к выходному каталогу для проекта. Введите следующее: `installutil WindowsService1.exe 'Это устанавливает службу Windows NT на вашем локальном компьютере. Откройте панель управления Services и запустите службу Service1. Важно сделать это для того, чтобы работать с шагом 9.

    1. Откройте еще один экземпляр Visual Studio 2008 и создайте приложение MFC, которое находится примерно так же далеко, как вы можете получить из WCF. В качестве примера я просто создал диалоговое приложение MFC и добавил "Привет! к нему. Щелкните правой кнопкой мыши проект в обозревателе решений и выберите опцию меню" Свойства ". В разделе" Общие" измените выходной каталог на.. \bin\Debug. В общих настройках C/С++ добавьте.. \HelloServiceClientBridge в дополнительные каталоги включения. В разделе Общие настройки компоновщика добавьте..\Отладка в дополнительные библиотеки. Нажмите кнопку OK.

В меню "Файл" выберите пункт "Добавить новый проект...". Выберите шаблон библиотеки классов С#. Измените имя на HelloServiceClient и нажмите кнопку OK. Щелкните правой кнопкой мыши проект в Обозревателе решений и выберите пункт меню "Свойства". На вкладке "Сборка" измените путь вывода на.. \bin\Debug, чтобы файл сборки и app.config находился в том же каталоге, что и приложение MFC. Эта библиотека будет содержать ссылку службы, т.е. Класс прокси WCF, в службу WCF Hello, размещенную в службе Windows NT.

В обозревателе решений щелкните правой кнопкой мыши папку "Ссылки" для проекта HelloServiceClient и выберите пункт "Добавить ссылку на службу...". В поле Адрес введите адрес службы Hello. Это должно быть равно базовому адресу в файле app.config, созданном на шаге 2 выше. Нажмите кнопку "Перейти". Служба Hello появится в списке "Службы". Нажмите кнопку OK, чтобы автоматически генерировать прокси-класса для службы Hello. ПРИМЕЧАНИЕ. Я, кажется, всегда сталкиваюсь с проблемами компиляции с файлом Reference.cs, сгенерированным этим процессом. Я не знаю, выполняю ли я это неправильно или если есть ошибка, но самый простой способ исправить это - напрямую изменить файл Reference.cs. Проблема обычно связана с проблемой имен и может быть исправлена ​​с минимальными усилиями. Просто имейте в виду, что это возможность. В этом примере я изменил HelloServiceClient.ServiceReference1 на просто HelloService (наряду с любыми другими необходимыми изменениями).

Чтобы разрешить MFC-приложению взаимодействовать с сервисом WCF, нам нужно создать управляемую DLL-мост "С++". В меню "Файл" выберите пункт "Добавить новый проект...". Выберите шаблон проекта С++ Win32. Измените имя на HelloServiceClientBridge и нажмите кнопку OK. Для параметров приложения измените тип приложения на DLL и установите флажок "Пустое проект". Нажмите кнопку "Готово".

Первое, что нужно сделать, это изменить свойства проекта. Щелкните правой кнопкой мыши проект в Обозревателе решений и выберите пункт меню "Свойства ". В разделе" Общие настройки" измените выходной каталог на "\ bin\Debug" и измените параметр "Поддержка продолжительности обычного языка" на Common Language Runtime Support (/clr). В рамках и "Ссылки", добавьте ссылку на сборки .NET System, System.ServiceModel и mscorlib. Нажмите кнопку OK.

Добавьте в проект HelloServiceClientBridge следующие файлы: HelloServiceClientBridge.h, IHelloServiceClientBridge.h и HelloServiceClientBridge.cpp.

Измените IHelloServiceClientBridge.h, чтобы выглядеть так:

#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__

Измените HelloServiceClientBridge.h таким образом:

#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__

В синтаксисе для .cpp файла используется управляемый С++, к чему привыкает. Измените HelloServiceClientBridge.cpp так:

#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;
}

Осталось только обновить приложение MFC, чтобы вызвать SayHello() WCF сервисный звонок. В форме MFC дважды щелкните значок "Привет! для создания обработчика событий ButtonClicked. Сделайте обработчик событий следующим:

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

Запустите приложение и нажмите "Привет! кнопка. Это приведет к тому, что приложение вызывают метод SayHello() службы WCF Hello, размещенной в службе Windows NT (кстати, она все равно должна быть запущена). Возвращаемое значение затем отображается в окне сообщения.

Надеюсь, вы сможете экстраполировать этот простой пример в соответствии с вашими потребностями. Если это не сработает, сообщите мне, чтобы я мог исправить сообщение.

Ответ 3

Я бы создал управляемый класс на С#, чтобы выполнить работу WCF и выставить класс как COM-объект для клиентов С++.

Ответ 4

Вы можете легко реализовать SOAP-клиент, используя устаревший MS Soap Toolkit. К сожалению, похоже, что это не замена для этого вне перехода на .NET.

Ответ 5

Можете ли вы опубликовать веб-службу REST и использовать библиотеку COM MSXML - должны быть уже установлены, иметь XML-парсер и HTTP-библиотеку.

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