2009-03-26 4 views
57

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

Я исследовал следующие варианты до сих пор:

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

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

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

+0

Извините за задержку. Я обновил свой ответ. Надеюсь, поможет. –

+1

Вы можете изменить службу WCF для предоставления как конечных точек SOAP, так и REST, а затем использовать конечную точку REST из C++. (Пока ваши типы данных легко анализируются на C++). См. Http://stackoverflow.com/questions/186631/rest-soap-endpoints-for-a-wcf-service –

ответ

11

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

код InlineXsdInWsdlBehavior можно найти here. Одно важное изменение необходимо сделать для 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); 
       } 
      } 
     } 
    } 

Следующий шаг заключается в создании C++ заголовок:

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

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

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; 

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

+0

Я пробовал этот подход, но sproxy все еще не мог справиться с wsdl, который был сгенерирован моей довольно простой услугой. – PIntag

1

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

+0

Вы можете опубликовать образец совместимых проектов WCF и мыльных инструментов? Я создал простой, а вызов soap-инструментального вызова MSSoapInit не похож на wsdl и не скажет мне, что конкретно ему нужно (какое-то фиктивное «сервисное обслуживание Service1 не нашел сообщений о портах») – galets

56

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

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

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

    • Добавить в проект службу WCF под названием HelloService. Для этого щелкните правой кнопкой мыши проект в окне «Обозреватель решений» и выберите пункт «Добавить новый элемент ...». В диалоговом окне «Добавить новый элемент» выберите шаблон службы WCF C# и нажмите кнопку «Добавить». Это добавляет 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/C++ добавьте .. \ HelloServiceClientBridge в дополнительные каталоги включения. В разделе Общие настройки компоновщика добавьте .. \ Отладка в дополнительные библиотеки. Нажмите кнопку OK.
  • В меню «Файл» выберите пункт «Добавить новый проект ...». Выберите шаблон библиотеки классов C#. Измените имя на 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-мост «C++». В меню «Файл» выберите пункт «Добавить новый проект ...». Выберите шаблон проекта C++ 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 использует управляемого C++, к чему привыкает. Измените 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 (...) { 
        } 
    } 
    
  • Запустите приложение и нажмите Say Hello! кнопка. Это заставит приложение вызывать метод SayHello() службы WCF Hello, размещенный в службе Windows NT (кстати, все равно должен быть запущен). Возвращаемое значение затем отображается в окне сообщения.

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

+2

Мэтт, сначала я хотел поблагодарить вас за всю работу, которую вы потратили, написав это руководство. Конечно, это было бы полезно для многих, но, к сожалению, не для меня. То, что я искал, было чистым неуправляемым абонентом службы WCF, а не .NET-прокси. У клиента может быть не установлен фрейм, жаль, что я не дал понять, – galets

+1

@Matt: Извините, если есть так много изменений. Я думаю, что пара из нас столкнулась друг с другом, пытаясь исправить форматирование кода. – gnovice

+1

Спасибо, это точно, что мне нужно - спасибо за блестящую работу! – Contango

2

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

0

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

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

+0

Вы можете сделать свой сервис WCF как SOAP, так и REST и использовать конечную точку REST из C++. См. Http://stackoverflow.com/questions/186631/rest-soap-endpoints-for-a-wcf-service –

Смежные вопросы