2009-03-26 56 views
57

Tôi cần có các máy khách Windows C++ không được quản lý để nói chuyện với dịch vụ WCF. Các máy khách C++ có thể chạy trên Win2000 và sau đó. Tôi có quyền kiểm soát cả hai dịch vụ WCF và C++ API đang được sử dụng. Vì nó dành cho một ứng dụng độc quyền, nên sử dụng các công cụ của Microsoft nếu có thể, chắc chắn không phải các API được cấp phép GNU. Những người trong số các bạn có nó hoạt động, bạn có thể chia sẻ quy trình từng bước một để làm cho nó hoạt động không?Tạo dịch vụ WCF cho các máy khách C++ không được quản lý

Tôi đã nghiên cứu lựa chọn sau cho đến nay:

  • WWSAPI - không tốt, sẽ không hoạt động trên Win 2000 khách hàng.
  • Máy chủ ATL, đã sử dụng following guide làm tham chiếu. Tôi đã làm theo các bước được phác thảo (loại bỏ chính sách refs và flatten WSDL), tuy nhiên WSDL kết quả vẫn không thể sử dụng bởi sproxy

Còn ý tưởng nào khác? Hãy trả lời chỉ khi bạn thực sự có nó làm việc cho mình.

Edit1: Tôi xin lỗi vì bất cứ ai tôi có thể đã nhầm lẫn: những gì tôi đang tìm kiếm là một cách để gọi dịch vụ WCF từ khách hàng (s), nơi không có khuôn khổ NET được cài đặt, vì vậy sử dụng .NET- dựa trên thư viện trợ giúp không phải là một lựa chọn, nó phải được tinh khiết không được quản lý C++

+0

Xin lỗi vì sự chậm trễ. Tôi đã cập nhật câu trả lời của mình. Hy vọng nó giúp. –

+1

Bạn có thể sửa đổi dịch vụ WCF để cung cấp cả hai điểm cuối SOAP và REST, sau đó sử dụng điểm cuối REST từ C++. (Miễn là các kiểu dữ liệu của bạn được phân tích cú pháp dễ dàng trong C++). Xem: http://stackoverflow.com/questions/186631/rest-soap-endpoints-for-a-wcf-service –

Trả lời

11

Đối với những người quan tâm, tôi đã tìm thấy một giải pháp máy chủ ATL bán làm việc. Dưới đây là các mã chủ nhà, thấy nó đang sử dụng BasicHttpBinding, đó là người duy nhất mà làm việc với 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(); 

mã cho InlineXsdInWsdlBehavior thể được tìm thấy here. Một thay đổi quan trọng cần được thực hiện cho InlineXsdInWsdlBehavior để nó hoạt động đúng với sproxy khi các kiểu phức tạp có liên quan. Nó được gây ra bởi lỗi trong sproxy, mà không đúng phạm vi bí danh không gian tên, do đó, wsdl không thể có lặp lại bí danh không gian tên hoặc sproxy sẽ crap ra.Dưới đây là các chức năng mà cần phải thay đổi:

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

Bước tiếp theo là tạo ra C++ tiêu đề:

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

và sau đó chương trình C++ trông như thế này:

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; 

Kết quả C++ xử lý phức tạp loại khá decently, ngoại trừ việc nó không thể gán NULL cho các đối tượng.

+0

Tôi đã thử phương pháp này nhưng sproxy vẫn không thể xử lý wsdl được tạo ra bởi dịch vụ khá đơn giản của tôi. – PIntag

1

Bạn có thể thực hiện một khách hàng SOAP một cách dễ dàng bằng cách sử dụng không được chấp nhận MS Soap Toolkit. Thật không may, có vẻ như không phải là một sự thay thế cho việc này ngoài việc chuyển sang .NET.

+0

bạn có thể đăng một mẫu các dự án WCF và bộ công cụ xà phòng tương thích không? Tôi tạo ra một đơn giản, và bộ công cụ xà phòng MSSoapInit gọi không giống như wsdl, và sẽ không cho tôi biết những gì cụ thể nó cần (một số "dịch vụ xử lý không có dịch vụ 1 tìm thấy không có định nghĩa cổng" tin nhắn) – galets

56

Ý tưởng cơ bản là viết mã WCF cho khách hàng của bạn trong C# (nó dễ dàng hơn theo cách này) và sử dụng dll cầu C++ để thu hẹp khoảng cách giữa mã C++ không được quản lý của bạn và mã WCF được quản lý được viết bằng C#.

Đây là quy trình từng bước sử dụng Visual Studio 2008 cùng với .NET 3.5 SP1.

  1. Điều đầu tiên cần làm là tạo WCF Service và phương tiện lưu trữ nó. Nếu bạn đã có điều này, hãy chuyển sang Bước 7 bên dưới. Nếu không, hãy tạo Dịch vụ Windows NT theo các bước từ here. Sử dụng tên mặc định được cung cấp bởi VS2008 cho dự án và bất kỳ lớp nào được thêm vào dự án. Dịch vụ Windows NT này sẽ lưu trữ Dịch vụ WCF.

    • Thêm dịch vụ WCF có tên HelloService vào dự án. Để làm điều này, nhấn chuột phải vào dự án trong cửa sổ Solution Explorer và chọn mục Add | New Item .... Trong hộp thoại Add New Item, chọn mẫu C# WCF Service và kích nút Add. Điều này thêm HelloService vào dự án dưới dạng một tệp giao diện (IHelloService.cs), một tệp lớp (HelloService.cs) và tệp cấu hình dịch vụ mặc định (app.config).

    • Xác định HelloService như thế này:

''

[ServiceContract] 
    public interface IHelloService 
    { 
     [OperationContract] 
     string SayHello(string name); 
    } 
    public class HelloService : IHelloService 
    { 
     public string SayHello(string name) 
     { 
      return String.Format("Hello, {0}!", name); 
     } 
    } 
  • Sửa đổi các lớp Service1 tạo ở bước 1 ở trên để trông như thế này:

    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 { 
         } 
        } 
    } 
    
  • Xây dựng dự án.

  • Mở lời nhắc lệnh Visual Studio 2008. Điều hướng đến thư mục đầu ra cho dự án. Gõ như sau: `installutil WindowsService1.exe 'Thao tác này sẽ cài đặt Dịch vụ Windows NT trên máy cục bộ của bạn. Mở bảng điều khiển Dịch vụ và khởi động dịch vụ Service1. Điều quan trọng là làm điều này để Bước 9 dưới đây hoạt động.

    1. Mở một phiên bản khác của Visual Studio 2008 và tạo ứng dụng MFC, cách xa bạn có thể nhận được từ WCF. Ví dụ, tôi chỉ đơn giản là tạo ra một ứng dụng MFC thoại và thêm một Xin chào! nút cho nó. Nhấn chuột phải vào dự án trong Solution Explorer và chọn tùy chọn trình đơn Properties. Trong phần General settings, thay đổi Output Directory thành .. \ bin \ Debug. Bên dưới cài đặt chung C/C++, thêm .. \ HelloServiceClientBridge vào Thư mục Bổ sung Bao gồm. Trong cài đặt Trình liên kết chung, hãy thêm .. \ Debug vào Thư mục Thư viện Bổ sung. Nhấp vào nút OK.
  • Từ menu Tệp, chọn mục menu Thêm | Dự án mới .... Chọn mẫu C# Class Library. Thay đổi tên thành HelloServiceClient và nhấn nút OK. Nhấn chuột phải vào dự án trong Solution Explorer và chọn tùy chọn trình đơn Properties. Trong tab Build, thay đổi đường dẫn đầu ra thành .. \ bin \ Debug để tập tin assembly và app.config nằm trong cùng thư mục với ứng dụng MFC. Thư viện này sẽ chứa tham chiếu dịch vụ, tức là lớp proxy WCF, với WCF Hello Service được lưu trữ trong Dịch vụ Windows NT.

  • Trong Solution Explorer, bấm chuột phải vào thư mục Tham khảo cho dự án HelloServiceClient và chọn tùy chọn trình đơn Thêm dịch vụ tham chiếu .... Trong trường Địa chỉ, nhập địa chỉ của Dịch vụ Xin chào. Địa chỉ này phải bằng địa chỉ cơ sở trong tệp app.config được tạo ở Bước 2 ở trên. Nhấp vào nút Bắt đầu. Dịch vụ Hello sẽ hiển thị trong danh sách Dịch vụ. Nhấp vào nút OK để tự động tạo các lớp proxy cho Dịch vụ Xin chào. LƯU Ý:Tôi dường như luôn gặp sự cố biên dịch với tệp Reference.cs được tạo bởi quá trình này. Tôi không biết nếu tôi đang làm sai hoặc nếu có lỗi, nhưng cách dễ nhất để sửa lỗi này là sửa đổi tệp Reference.cs trực tiếp. Vấn đề thường là vấn đề về không gian tên và có thể được khắc phục với nỗ lực tối thiểu. Chỉ cần lưu ý rằng đây là một khả năng. Đối với ví dụ này, tôi đã thay đổi HelloServiceClient.ServiceReference1 thành HelloService đơn giản (cùng với bất kỳ thay đổi bắt buộc nào khác).

  • Để cho phép Ứng dụng MFC tương tác với dịch vụ WCF, chúng tôi cần tạo một DLL "C++" được quản lý. Từ menu Tệp, chọn mục trình đơn Thêm | Dự án Mới .... Chọn mẫu C++ Win32 Project. Thay đổi tên thành HelloServiceClientBridge và nhấp vào nút OK. Đối với Cài đặt ứng dụng, thay đổi Loại ứng dụng thành DLL và chọn hộp kiểm Rỗng dự án. Nhấp vào nút Kết thúc.

  • Việc đầu tiên cần làm là sửa đổi thuộc tính dự án. Nhấn chuột phải vào dự án trong Solution Explorer và chọn tùy chọn trình đơn Properties. Trong phần General settings, thay đổi Output Directory thành .. \ bin \ Debug và thay đổi tùy chọn Common Language Runtime Support thành Common Language Runtime Support (/ clr). Trong khung và tham khảo cài đặt, hãy thêm tham chiếu đến hệ thống .NET, System.ServiceModel và mscorlib. Nhấp vào nút OK.

  • Thêm các tệp sau vào dự án HelloServiceClientBridge - HelloServiceClientBridge.h, IHelloServiceClientBridge.h và HelloServiceClientBridge.cpp.

  • Sửa đổi IHelloServiceClientBridge.h trông như thế này:

    #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__ 
    
  • Sửa đổi HelloServiceClientBridge.h trông như thế này:

    #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__ 
    
  • Cú pháp cho các tập tin cpp sử dụng được quản lý C + +, mà phải mất một số nhận được sử dụng để. Sửa đổi HelloServiceClientBridge.cpp trông như thế này:

    #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; 
    } 
    
  • Điều duy nhất còn lại để làm là cập nhật các ứng dụng MFC để gọi SayHello() WCF cuộc gọi dịch vụ. Trên biểu mẫu MFC, bấm đúp vào lời chào! để tạo trình xử lý sự kiện ButtonClicked. Đặt trình xử lý sự kiện như sau:

    #include "IHelloServiceClientBridge.h" 
    #include <string> 
    void CMFCApplicationDlg::OnBnClickedButton1() 
    { 
        try { 
         std::string message = IHelloServiceClientBridge::SayHello("Your Name Here"); 
         AfxMessageBox(CString(message.c_str())); 
        } catch (...) { 
        } 
    } 
    
  • Chạy ứng dụng và nhấp vào lời chào! nút. Điều này sẽ khiến ứng dụng gọi phương thức SayHello() của Dịch vụ Xin chào WCF được lưu trữ trong Dịch vụ Windows NT (vẫn nên chạy bằng cách này). Giá trị trả về sau đó được hiển thị trong một hộp tin nhắn.

Hy vọng bạn có thể ngoại suy từ ví dụ đơn giản này cho phù hợp với nhu cầu của bạn. Nếu cách này không hiệu quả, vui lòng cho tôi biết để tôi có thể sửa bài đăng.

+2

Matt, đầu tiên tôi muốn cảm ơn bạn cho tất cả công việc bạn đã chi tiêu bằng văn bản hướng dẫn này. Nó chắc chắn sẽ hữu ích cho nhiều người, nhưng thật đáng buồn là không phải với tôi. Những gì tôi đang tìm kiếm là một người gọi không được quản lý thuần túy đối với dịch vụ WCF, chứ không phải proxy .NET. Khách hàng có thể không có khung được cài đặt, xin lỗi tôi đã không làm cho nó rõ ràng – galets

+1

@Matt: Xin lỗi nếu có quá nhiều chỉnh sửa. Tôi nghĩ rằng một vài người trong chúng ta đã chạy vào nhau cố sửa định dạng mã. – gnovice

+1

Cảm ơn, đây là * chính xác * những gì tôi cần - cảm ơn vì công việc tuyệt vời! – Contango

2

Tôi sẽ tạo một lớp C# được quản lý để thực hiện công việc WCF và hiển thị lớp như một đối tượng COM cho các trình khách C++.

0

Bạn có thể xuất bản Dịch vụ web REST và sử dụng thư viện MSXML COM - nên đã được cài đặt, có trình phân tích cú pháp XML và thư viện HTTP không.

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

+0

Bạn có thể làm cho dịch vụ WCF của bạn cung cấp cả SOAP và REST và sử dụng điểm cuối REST từ C++. Xem: http://stackoverflow.com/questions/186631/rest-soap-endpoints-for-a-wcf-service –

Các vấn đề liên quan