web-dev-qa-db-ja.com

管理されていないC ++クライアント用のWCFサービスを作成する

管理されていないWindows C++クライアントをWCFサービスと通信させる必要があります。 C++クライアントは、Win2000以降で実行できます。 WCFサービスと使用されているC++ APIの両方を制御できます。プロプライエタリなアプリケーションのためであるため、可能な限りMicrosoftのものを使用することをお勧めします。間違いなくGNUライセンスAPIを使用してください。それを機能させる方法は?

これまでに次のオプションを調査しました。

  • WWSAPI-良くない、Win 2000クライアントでは動作しません。
  • ATLサーバー、 ガイドに従って を参照として使用。概説した手順に従いました(ポリシー参照を削除し、WSDLをフラット化します)が、結果のWSDLはまだsproxyで使用できません

他にアイデアはありますか?実際に自分で動作する場合にのみ答えてください。

Edit1:混乱した可能性のある人には申し訳ありません:私が探していたのは方法でした.NETフレームワークがインストールされていないクライアントからWCFサービスを呼び出すため、.NETベースのヘルパーライブラリを使用することはオプションではないため、純粋なアンマネージC++である必要があります。

59
galets

興味のある人のために、私は1つの準稼働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に1つの重要な変更を加える必要があります。これは、複雑な型が関係する場合にsproxyで適切に動作するためです。これは、ネームスペースエイリアスのスコープが適切に設定されていないsproxyのバグが原因であるため、wsdlはネームスペースエイリアスを繰り返すことができません。変更が必要な関数は次のとおりです。

    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を割り当てることができないことを除いて、複雑な型をかなり適切に処理します。

12
galets

基本的な考え方は、クライアント用のWCFコードをC#で記述し(この方法のほうが簡単です)、C++ブリッジdllを使用して、アンマネージC++コードとC#で記述されたマネージWCFコードのギャップを埋めることです。

Visual Studio 2008と.NET 3.5 SP1を使用した段階的なプロセスを次に示します。

  1. 最初に行うことは、WCFサービスとそれをホストする手段を作成することです。すでにこれがある場合は、以下のステップ7にスキップしてください。それ以外の場合は、 here の手順に従ってWindows NTサービスを作成します。プロジェクトおよびプロジェクトに追加されるクラスに対してVS2008が提供するデフォルト名を使用します。このWindows NTサービスは、WCFサービスをホストします。

    • HelloServiceという名前のWCFサービスをプロジェクトに追加します。これを行うには、ソリューションエクスプローラーウィンドウでプロジェクトを右クリックし、[追加|新しい項目...]メニュー項目を選択します。 [新しい項目の追加]ダイアログで、C#WCFサービステンプレートを選択し、[追加]ボタンをクリックします。これにより、インターフェイスファイル(IHelloService.cs)、クラスファイル(HelloService.cs)、および既定のサービス構成ファイル(app.config)の形式でHelloServiceがプロジェクトに追加されます。

    • 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);
        }
    }
  • 上記の手順1で作成したService1クラスを次のように変更します。

    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サービスがインストールされます。 [サービス]コントロールパネルを開き、Service1サービスを開始します。以下のステップ9が機能するためには、これを行うことが重要です。

    1. Visual Studio 2008の別のインスタンスを開き、MFCアプリケーションを作成します。これは、WCFから取得できる範囲内です。例として、ダイアログMFCアプリケーションを作成し、Say Hello!を追加しました。それへのボタン。ソリューションエクスプローラーでプロジェクトを右クリックし、[プロパティ]メニューオプションを選択します。一般設定で、出力ディレクトリを..\bin\Debugに変更します。 C/C++の一般設定で、..\HelloServiceClientBridgeを追加のインクルードディレクトリに追加します。リンカーの一般設定で、追加のライブラリディレクトリに..\Debugを追加します。 OKボタンをクリックします。
  • [ファイル]メニューから、[追加|新しいプロジェクト...]メニュー項目を選択します。 C#クラスライブラリテンプレートを選択します。名前をHelloServiceClientに変更し、[OK]ボタンをクリックします。ソリューションエクスプローラーでプロジェクトを右クリックし、[プロパティ]メニューオプションを選択します。 [ビルド]タブで、出力パスを..\bin\Debugに変更し、Assemblyおよびapp.configファイルがMFCアプリケーションと同じディレクトリにあるようにします。このライブラリには、Windows NTサービスでホストされるWCF Helloサービスへのサービス参照、つまりWCFプロキシクラスが含まれます。

  • ソリューションエクスプローラーで、HelloServiceClientプロジェクトのReferencesフォルダーを右クリックし、[サービス参照の追加...]メニューオプションを選択します。 [アドレス]フィールドに、Hello Serviceのアドレスを入力します。これは、上記の手順2で作成したapp.configファイルのベースアドレスと同じである必要があります。 [Go]ボタンをクリックします。 Helloサービスが[サービス]リストに表示されます。 [OK]ボタンをクリックして、Helloサービスのプロキシクラスを自動的に生成します。 注:リファレンスでコンパイルの問題が常に発生するようですこのプロセスによって生成された.csファイル。間違っているのか、バグがあるのか​​はわかりませんが、これを修正する最も簡単な方法は、Reference.csファイルを直接変更することです。この問題は通常ネームスペースの問題であり、最小限の労力で修正できます。これは可能性であることに注意してください。この例では、HelloServiceClient.ServiceReference1を単純にHelloServiceに変更しました(他の必要な変更とともに)。

  • MFCアプリケーションがWCFサービスと対話できるようにするには、マネージC++ "ブリッジ" DLLを構築する必要があります。 [ファイル]メニューから、[追加|新しいプロジェクト...]メニュー項目を選択します。 C++ Win32 Projectテンプレートを選択します。名前をHelloServiceClientBridgeに変更し、[OK]ボタンをクリックします。 [アプリケーションの設定]で、[アプリケーションの種類]をDLLに変更し、[空のプロジェクト]チェックボックスをオンにします。[完了]ボタンをクリックします。

  • 最初に行うことは、プロジェクトのプロパティを変更することです。ソリューションエクスプローラーでプロジェクトを右クリックし、[プロパティ]メニューオプションを選択します。 [一般]設定で、[出力ディレクトリ]を..\bin\Debugに変更し、[共通言語ランタイムサポート]オプションを[共通言語ランタイムサポート(/ clr)]に変更します。 [Framework and References]設定の下で、.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フォームで、Say Hello!をダブルクリックします。 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!」をクリックしますボタン。これにより、アプリケーションは、Windows NTサービスでホストされているWCF HelloサービスのSayHello()メソッドを呼び出します(これはまだ実行されているはずです)。戻り値はメッセージボックスに表示されます。

この単純な例から、ニーズに合わせて外挿できることを願っています。これでうまくいかない場合は、投稿を修正できるようお知らせください。

58
Matt Davis

C#マネージクラスを作成してWCFの作業を行い、そのクラスをCOMオブジェクトとしてC++クライアントに公開します。

2
kenny

非推奨の MS Soap Toolkit を使用すると、SOAPクライアントを簡単に実装できます。 。

1
Eclipse

REST Webサービスを公開し、MSXML COMライブラリを使用できます-既にインストールされている必要があり、XMLパーサーとHTTPライブラリがあります。

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

0
krisragh MSFT