为非托管c++客户端创建WCF服务

时间:2022-09-01 09:29:14

I need to get unmanaged Windows C++ clients to talk to a WCF service. C++ clients could be running on Win2000 and later. I have a control over both WCF service and which C++ API is being used. Since it's for a proprietary application, it is preferable to use Microsoft stuff where possible, definitely not GNU licensed APIs. Those of you who have it working, can you share a step-by-step process how to make it working?

我需要让非托管的Windows c++客户端与WCF服务进行对话。c++客户端可以在Win2000和以后的版本中运行。我对WCF服务有一个控制,并且使用了c++ API。因为这是一个私有的应用程序,所以最好在可能的地方使用Microsoft的东西,而不是GNU许可的api。如果你们中有这样的人,你们能分享一个循序渐进的过程吗?

I have researched following options so far:

到目前为止,我已经研究了以下选项:

  • WWSAPI - not good, will not work on Win 2000 clients.
  • WWSAPI -不太好,不会在Win 2000客户端上工作。
  • ATL Server, used following guide as a reference. I followed the steps outlined (remove policy refs and flatten WSDL), however the resulting WSDL is still not usable by sproxy
  • ATL服务器,使用以下指南作为参考。我遵循了概述的步骤(删除策略引用并简化WSDL),但是结果的WSDL仍然不能被sproxy使用

Any more ideas? Please answer only if you actually have it working yourself.

还有更多的想法吗?请只回答如果你真的让它自己工作。

Edit1: I apologize for anyone who I might have confused: what I was looking for was a way to call WCF service from client(s) where no .NET framework is installed, so using .NET-based helper library is not an option, it must be pure unmanaged C++

Edit1:我要为那些可能让我感到困惑的人道歉:我要寻找的是一种从客户端调用WCF服务的方法,在那里没有安装。net框架,所以使用基于.NET的helper库不是一个选项,它必须是纯非托管的c++

5 个解决方案

#1


56  

The basic idea is to write the WCF code for your clients in C# (it's just easier this way) and use a C++ bridge dll to bridge the gap between your unmanaged C++ code and the managed WCF code written in C#.

基本的想法是用c#为您的客户编写WCF代码(这样更简单),并使用c++桥接dll来消除非托管c++代码与c#托管WCF代码之间的差距。

Here is the step-by-step process using Visual Studio 2008 along with .NET 3.5 SP1.

下面是使用Visual Studio 2008和。net 3.5 SP1的逐步过程。

  1. The first thing to do is create the WCF Service and a means to host it. If you already have this, skip to Step 7 below. Otherwise, create a Windows NT Service following the steps from here. Use the default names offered by VS2008 for the project and any classes that are added to the project. This Windows NT Service will host the WCF Service.

    首先要做的是创建WCF服务并提供托管服务的方法。如果您已经有了,请跳到下面的第7步。否则,按照下面的步骤创建一个Windows NT服务。使用VS2008提供的项目和添加到项目中的任何类的默认名称。此Windows NT服务将托管WCF服务。

    • Add a WCF Service named HelloService to the project. To do this, right-click the project in the Solution Explorer window and select the Add|New Item... menu item. In the Add New Item dialog, select the C# WCF Service template and click the Add button. This adds the HelloService to the project in the form of an interface file (IHelloService.cs), a class file (HelloService.cs), and a default service configuration file (app.config).

      向项目添加名为HelloService的WCF服务。为此,在解决方案资源管理器窗口中右键单击项目,并选择Add|New Item…菜单项。在Add New Item对话框中,选择c# WCF服务模板并单击Add按钮。这将以接口文件(IHelloService.cs)、类文件(HelloService.cs)和默认服务配置文件(app.config)的形式向项目添加HelloService。

    • Define the HelloService like this:

      像这样定义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);
        }
    }
  • Modify the Service1 class created in Step 1 above to look like this:

    修改上面步骤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 {
            }
        }
    }
    
  • Build the project.

    构建项目。

  • Open the Visual Studio 2008 command prompt. Navigate to the output directory for the project. Type the following: `installutil WindowsService1.exe' This installs the Windows NT Service on your local machine. Open the Services control panel and start the Service1 service. It is important to do this in order for Step 9 below to work.

    打开Visual Studio 2008命令提示符。导航到项目的输出目录。输入以下内容:“installutil WindowsService1。在本地机器上安装Windows NT服务。打开服务控制面板并启动Service1服务。为了完成下面的第9步,这样做是很重要的。

    1. Open another instance of Visual Studio 2008 and create an MFC application, which is about as far away as you can get from WCF. As an example, I simply created a dialog MFC application and added a Say Hello! button to it. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug. Under the C/C++ General settings, add ..\HelloServiceClientBridge to the Additional Include Directories. Under the Linker General settings, add ..\Debug to the Additional Library Directories. Click the OK button.
    2. 打开Visual Studio 2008的另一个实例并创建一个MFC应用程序,这个应用程序离WCF的距离差不多。例如,我创建了一个对话框MFC应用程序并添加了一个Say Hello!按钮。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在常规设置下,将输出目录更改为.\bin\Debug。在C/ c++的一般设置下,添加。\HelloServiceClientBridge到附加的包含目录。在链接器一般设置下,添加。\调试到附加的库目录。单击OK按钮。
  • From the File menu, select the Add|New Project... menu item. Select the C# Class Library template. Change the name to HelloServiceClient and click the OK button. Right-click the project in the Solution Explorer and select the Properties menu option. In the Build tab, change the output path to ..\bin\Debug so the assembly and app.config file will be in the same directory as the MFC application. This library will contain the service reference, i.e., the WCF proxy class, to the WCF Hello Service hosted in the Windows NT Service.

    在文件菜单中,选择Add|New Project…菜单项。选择c#类库模板。将名称更改为HelloServiceClient并单击OK按钮。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在Build选项卡中,将输出路径更改为..\bin\Debug使程序集和app.config文件与MFC应用程序位于同一个目录中。该库将包含服务引用,即, WCF代理类,到托管在Windows NT服务中的WCF Hello服务。

  • In the Solution Explorer, right-click the References folder for the HelloServiceClient project and select the Add Service Reference... menu option. In the Address field, type the address of Hello Service. This should be equal to the base address in the app.config file created in Step 2 above. Click the Go button. The Hello Service should show up in the Services list. Click the OK button to automatically generate the proxy class(es) for the Hello Service. NOTE: I seem to always run into compilation problems with the Reference.cs file generated by this process. I don't know if I'm doing it wrong or if there is a bug, but the easiest way to fix this is modify the Reference.cs file directly. The problem is usually a namespacing issue and can be fixed with minimal effort. Just be aware that this is a possibility. For this example, I've changed the HelloServiceClient.ServiceReference1 to simply HelloService (along with any other required changes).

    在解决方案资源管理器中,右键单击HelloServiceClient项目的References文件夹,并选择Add Service Reference…菜单选项。在Address字段中,键入Hello服务的地址。这应该等于上面第2步中创建的app.config文件中的基本地址。单击Go按钮。Hello服务应该显示在服务列表中。单击OK按钮自动生成Hello服务的代理类。注意:我似乎总是在引用时遇到编译问题。这个过程生成的cs文件。我不知道我做错了还是有错误,但是最简单的方法是修改引用。直接cs文件。这个问题通常是一个命名空间问题,可以用最少的努力来解决。只要意识到这是一种可能。在本例中,我更改了HelloServiceClient。ServiceReference1用于简单的HelloService(以及任何其他需要的更改)。

  • To allow the MFC Application to interact with the WCF service, we need to build a managed C++ "bridge" DLL. From the File menu, select the Add|New Project... menu item. Select the C++ Win32 Project template. Change the name to HelloServiceClientBridge and click the OK button. For the Application Settings, change the Application Type to DLL and check the Empty project checkbox. Click the Finish button.

    为了允许MFC应用程序与WCF服务交互,我们需要构建一个托管的c++“桥”DLL。在文件菜单中,选择Add|New Project…菜单项。选择c++ Win32项目模板。将名称更改为HelloServiceClientBridge并单击OK按钮。对于应用程序设置,将应用程序类型更改为DLL并选中“空项目”复选框。单击Finish按钮。

  • The first thing to do is modify the project properties. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug and change the Common Language Runtime Support option to Common Language Runtime Support (/clr). Under the Framework and References settings, add a reference to the .NET System, System.ServiceModel, and mscorlib assemblies. Click the OK button.

    首先要做的是修改项目属性。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在常规设置下,将输出目录更改为..\bin\Debug并将公共语言运行时支持选项更改为公共语言运行时支持(/clr)。在框架和引用设置下,向。net系统添加一个引用。ServiceModel,mscorlib组件。单击OK按钮。

  • Add the following files to the HelloServiceClientBridge project - HelloServiceClientBridge.h, IHelloServiceClientBridge.h, and HelloServiceClientBridge.cpp.

    向HelloServiceClientBridge项目添加以下文件—HelloServiceClientBridge。h,IHelloServiceClientBridge。h,HelloServiceClientBridge.cpp。

  • Modify the IHelloServiceClientBridge.h to look like this:

    修改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__
    
  • Modify the HelloServiceClientBridge.h to look like this:

    修改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__
    
  • The syntax for the .cpp file uses managed C++, which takes some getting used to. Modify the HelloServiceClientBridge.cpp to look like this:

    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;
    }
    
  • The only thing left to do is update the MFC application to invoke the SayHello() WCF service call. On the MFC form, double-click the Say Hello! button to generate the ButtonClicked event handler. Make the event handler look like this:

    惟一要做的就是更新MFC应用程序以调用SayHello() WCF服务调用。在MFC表单上,双击Say Hello!按钮以生成按钮单击事件处理程序。让事件处理程序看起来像这样:

    #include "IHelloServiceClientBridge.h"
    #include <string>
    void CMFCApplicationDlg::OnBnClickedButton1()
    {
        try {
            std::string message = IHelloServiceClientBridge::SayHello("Your Name Here");
            AfxMessageBox(CString(message.c_str()));
        } catch (...) {
        }
    }
    
  • Run the application and click the Say Hello! button. This will cause the application to invoke the SayHello() method of the WCF Hello Service hosted in the Windows NT Service (which should still be running, by the way). The return value is then displayed in a message box.

    运行应用程序并单击“Hello!”按钮。这将导致应用程序调用在Windows NT服务中托管的WCF Hello服务的SayHello()方法(顺便说一下,该服务应该还在运行)。然后返回值显示在消息框中。

Hopefully you can extrapolate from this simple example to fit your needs. If this does not work, please let me know so I can fix the post.

希望你能从这个简单的例子中推断出适合你的需求。如果不行,请让我知道,这样我就可以修好这个帖子了。

#2


11  

For those who are interested, I found one semi-working ATL Server solution. Following is the host code, notice it is using BasicHttpBinding, it's the only one which works with ATL Server:

对于那些感兴趣的人,我找到了一个半工作的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();

code for InlineXsdInWsdlBehavior could be found here . One important change needs to be done to the InlineXsdInWsdlBehavior in order for it to work properly with sproxy when complex types are involved. It is caused by the bug in sproxy, which does not properly scope the namespace aliases, so wsdl cannot have repeating namespace aliases or sproxy will crap out. Here's the functions which needs to change:

可以在这里找到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);
                }
            }
        }
    }

Next step is to generate C++ header:

下一步是生成c++ header:

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

and then C++ program looks like this:

然后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;

Resulting C++ code handles complex types pretty decently, except that it cannot assign NULL to the objects.

得到的c++代码处理复杂类型的方式相当不错,但它不能为对象赋值NULL。

#3


2  

I would create a C# managed class to do the WCF work and expose the class as a COM object to the C++ clients.

我将创建一个c#托管类来完成WCF工作,并将该类作为COM对象公开给c++客户机。

#4


1  

You can implement a SOAP client somewhat easily using the deprecated MS Soap Toolkit. Unfortunately, there doesn't seem to be a replacement for this outside of moving to .NET.

您可以使用弃用的MS SOAP工具包轻松地实现SOAP客户机。不幸的是,除了迁移到。net之外,似乎没有什么可以替代这一点。

#5


0  

Can you publish a REST Web Service and use the MSXML COM library -- should be already installed, has an XML parser, and an HTTP library.

您是否可以发布一个REST Web服务并使用MSXML COM库——应该已经安装了、有一个XML解析器和一个HTTP库。

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

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

#1


56  

The basic idea is to write the WCF code for your clients in C# (it's just easier this way) and use a C++ bridge dll to bridge the gap between your unmanaged C++ code and the managed WCF code written in C#.

基本的想法是用c#为您的客户编写WCF代码(这样更简单),并使用c++桥接dll来消除非托管c++代码与c#托管WCF代码之间的差距。

Here is the step-by-step process using Visual Studio 2008 along with .NET 3.5 SP1.

下面是使用Visual Studio 2008和。net 3.5 SP1的逐步过程。

  1. The first thing to do is create the WCF Service and a means to host it. If you already have this, skip to Step 7 below. Otherwise, create a Windows NT Service following the steps from here. Use the default names offered by VS2008 for the project and any classes that are added to the project. This Windows NT Service will host the WCF Service.

    首先要做的是创建WCF服务并提供托管服务的方法。如果您已经有了,请跳到下面的第7步。否则,按照下面的步骤创建一个Windows NT服务。使用VS2008提供的项目和添加到项目中的任何类的默认名称。此Windows NT服务将托管WCF服务。

    • Add a WCF Service named HelloService to the project. To do this, right-click the project in the Solution Explorer window and select the Add|New Item... menu item. In the Add New Item dialog, select the C# WCF Service template and click the Add button. This adds the HelloService to the project in the form of an interface file (IHelloService.cs), a class file (HelloService.cs), and a default service configuration file (app.config).

      向项目添加名为HelloService的WCF服务。为此,在解决方案资源管理器窗口中右键单击项目,并选择Add|New Item…菜单项。在Add New Item对话框中,选择c# WCF服务模板并单击Add按钮。这将以接口文件(IHelloService.cs)、类文件(HelloService.cs)和默认服务配置文件(app.config)的形式向项目添加HelloService。

    • Define the HelloService like this:

      像这样定义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);
        }
    }
  • Modify the Service1 class created in Step 1 above to look like this:

    修改上面步骤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 {
            }
        }
    }
    
  • Build the project.

    构建项目。

  • Open the Visual Studio 2008 command prompt. Navigate to the output directory for the project. Type the following: `installutil WindowsService1.exe' This installs the Windows NT Service on your local machine. Open the Services control panel and start the Service1 service. It is important to do this in order for Step 9 below to work.

    打开Visual Studio 2008命令提示符。导航到项目的输出目录。输入以下内容:“installutil WindowsService1。在本地机器上安装Windows NT服务。打开服务控制面板并启动Service1服务。为了完成下面的第9步,这样做是很重要的。

    1. Open another instance of Visual Studio 2008 and create an MFC application, which is about as far away as you can get from WCF. As an example, I simply created a dialog MFC application and added a Say Hello! button to it. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug. Under the C/C++ General settings, add ..\HelloServiceClientBridge to the Additional Include Directories. Under the Linker General settings, add ..\Debug to the Additional Library Directories. Click the OK button.
    2. 打开Visual Studio 2008的另一个实例并创建一个MFC应用程序,这个应用程序离WCF的距离差不多。例如,我创建了一个对话框MFC应用程序并添加了一个Say Hello!按钮。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在常规设置下,将输出目录更改为.\bin\Debug。在C/ c++的一般设置下,添加。\HelloServiceClientBridge到附加的包含目录。在链接器一般设置下,添加。\调试到附加的库目录。单击OK按钮。
  • From the File menu, select the Add|New Project... menu item. Select the C# Class Library template. Change the name to HelloServiceClient and click the OK button. Right-click the project in the Solution Explorer and select the Properties menu option. In the Build tab, change the output path to ..\bin\Debug so the assembly and app.config file will be in the same directory as the MFC application. This library will contain the service reference, i.e., the WCF proxy class, to the WCF Hello Service hosted in the Windows NT Service.

    在文件菜单中,选择Add|New Project…菜单项。选择c#类库模板。将名称更改为HelloServiceClient并单击OK按钮。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在Build选项卡中,将输出路径更改为..\bin\Debug使程序集和app.config文件与MFC应用程序位于同一个目录中。该库将包含服务引用,即, WCF代理类,到托管在Windows NT服务中的WCF Hello服务。

  • In the Solution Explorer, right-click the References folder for the HelloServiceClient project and select the Add Service Reference... menu option. In the Address field, type the address of Hello Service. This should be equal to the base address in the app.config file created in Step 2 above. Click the Go button. The Hello Service should show up in the Services list. Click the OK button to automatically generate the proxy class(es) for the Hello Service. NOTE: I seem to always run into compilation problems with the Reference.cs file generated by this process. I don't know if I'm doing it wrong or if there is a bug, but the easiest way to fix this is modify the Reference.cs file directly. The problem is usually a namespacing issue and can be fixed with minimal effort. Just be aware that this is a possibility. For this example, I've changed the HelloServiceClient.ServiceReference1 to simply HelloService (along with any other required changes).

    在解决方案资源管理器中,右键单击HelloServiceClient项目的References文件夹,并选择Add Service Reference…菜单选项。在Address字段中,键入Hello服务的地址。这应该等于上面第2步中创建的app.config文件中的基本地址。单击Go按钮。Hello服务应该显示在服务列表中。单击OK按钮自动生成Hello服务的代理类。注意:我似乎总是在引用时遇到编译问题。这个过程生成的cs文件。我不知道我做错了还是有错误,但是最简单的方法是修改引用。直接cs文件。这个问题通常是一个命名空间问题,可以用最少的努力来解决。只要意识到这是一种可能。在本例中,我更改了HelloServiceClient。ServiceReference1用于简单的HelloService(以及任何其他需要的更改)。

  • To allow the MFC Application to interact with the WCF service, we need to build a managed C++ "bridge" DLL. From the File menu, select the Add|New Project... menu item. Select the C++ Win32 Project template. Change the name to HelloServiceClientBridge and click the OK button. For the Application Settings, change the Application Type to DLL and check the Empty project checkbox. Click the Finish button.

    为了允许MFC应用程序与WCF服务交互,我们需要构建一个托管的c++“桥”DLL。在文件菜单中,选择Add|New Project…菜单项。选择c++ Win32项目模板。将名称更改为HelloServiceClientBridge并单击OK按钮。对于应用程序设置,将应用程序类型更改为DLL并选中“空项目”复选框。单击Finish按钮。

  • The first thing to do is modify the project properties. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug and change the Common Language Runtime Support option to Common Language Runtime Support (/clr). Under the Framework and References settings, add a reference to the .NET System, System.ServiceModel, and mscorlib assemblies. Click the OK button.

    首先要做的是修改项目属性。右键单击解决方案资源管理器中的项目,并选择Properties菜单选项。在常规设置下,将输出目录更改为..\bin\Debug并将公共语言运行时支持选项更改为公共语言运行时支持(/clr)。在框架和引用设置下,向。net系统添加一个引用。ServiceModel,mscorlib组件。单击OK按钮。

  • Add the following files to the HelloServiceClientBridge project - HelloServiceClientBridge.h, IHelloServiceClientBridge.h, and HelloServiceClientBridge.cpp.

    向HelloServiceClientBridge项目添加以下文件—HelloServiceClientBridge。h,IHelloServiceClientBridge。h,HelloServiceClientBridge.cpp。

  • Modify the IHelloServiceClientBridge.h to look like this:

    修改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__
    
  • Modify the HelloServiceClientBridge.h to look like this:

    修改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__
    
  • The syntax for the .cpp file uses managed C++, which takes some getting used to. Modify the HelloServiceClientBridge.cpp to look like this:

    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;
    }
    
  • The only thing left to do is update the MFC application to invoke the SayHello() WCF service call. On the MFC form, double-click the Say Hello! button to generate the ButtonClicked event handler. Make the event handler look like this:

    惟一要做的就是更新MFC应用程序以调用SayHello() WCF服务调用。在MFC表单上,双击Say Hello!按钮以生成按钮单击事件处理程序。让事件处理程序看起来像这样:

    #include "IHelloServiceClientBridge.h"
    #include <string>
    void CMFCApplicationDlg::OnBnClickedButton1()
    {
        try {
            std::string message = IHelloServiceClientBridge::SayHello("Your Name Here");
            AfxMessageBox(CString(message.c_str()));
        } catch (...) {
        }
    }
    
  • Run the application and click the Say Hello! button. This will cause the application to invoke the SayHello() method of the WCF Hello Service hosted in the Windows NT Service (which should still be running, by the way). The return value is then displayed in a message box.

    运行应用程序并单击“Hello!”按钮。这将导致应用程序调用在Windows NT服务中托管的WCF Hello服务的SayHello()方法(顺便说一下,该服务应该还在运行)。然后返回值显示在消息框中。

Hopefully you can extrapolate from this simple example to fit your needs. If this does not work, please let me know so I can fix the post.

希望你能从这个简单的例子中推断出适合你的需求。如果不行,请让我知道,这样我就可以修好这个帖子了。

#2


11  

For those who are interested, I found one semi-working ATL Server solution. Following is the host code, notice it is using BasicHttpBinding, it's the only one which works with ATL Server:

对于那些感兴趣的人,我找到了一个半工作的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();

code for InlineXsdInWsdlBehavior could be found here . One important change needs to be done to the InlineXsdInWsdlBehavior in order for it to work properly with sproxy when complex types are involved. It is caused by the bug in sproxy, which does not properly scope the namespace aliases, so wsdl cannot have repeating namespace aliases or sproxy will crap out. Here's the functions which needs to change:

可以在这里找到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);
                }
            }
        }
    }

Next step is to generate C++ header:

下一步是生成c++ header:

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

and then C++ program looks like this:

然后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;

Resulting C++ code handles complex types pretty decently, except that it cannot assign NULL to the objects.

得到的c++代码处理复杂类型的方式相当不错,但它不能为对象赋值NULL。

#3


2  

I would create a C# managed class to do the WCF work and expose the class as a COM object to the C++ clients.

我将创建一个c#托管类来完成WCF工作,并将该类作为COM对象公开给c++客户机。

#4


1  

You can implement a SOAP client somewhat easily using the deprecated MS Soap Toolkit. Unfortunately, there doesn't seem to be a replacement for this outside of moving to .NET.

您可以使用弃用的MS SOAP工具包轻松地实现SOAP客户机。不幸的是,除了迁移到。net之外,似乎没有什么可以替代这一点。

#5


0  

Can you publish a REST Web Service and use the MSXML COM library -- should be already installed, has an XML parser, and an HTTP library.

您是否可以发布一个REST Web服务并使用MSXML COM库——应该已经安装了、有一个XML解析器和一个HTTP库。

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

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