我需要让非托管的Windows C ++客户端与WCF服务通话。C ++客户端可以在Win2000及更高版本上运行。我可以控制WCF服务和正在使用的C ++ API。由于它是专有应用程序,所以最好在可能的情况下使用Microsoft的东西,绝对不是GNU许可的API。那些有它工作的人,你能分享一步一步的过程如何使它工作?
到目前为止,我已经研究了以下选项:
还有什么想法?请仅在您自己确实有效时才回答。
我为任何我可能感到困惑的人道歉:我寻找的是从没有安装.NET框架的客户端调用WCF服务的方法,因此使用基于.NET的帮助程序库不是一种选择,它必须是纯粹的非托管C ++
发布于 2018-03-27 09:45:01
基本的想法是用C#编写客户端的WCF代码(这样做更简单),并使用C ++桥接dll来弥合非托管C ++代码与用C#编写的托管WCF代码之间的差距。
以下是使用Visual Studio 2008和.NET 3.5 SP1的分步过程。
``
[ServiceContract]
public interface IHelloService
{
[OperationContract]
string SayHello(string name);
}
public class HelloService : IHelloService
{
public string SayHello(string name)
{
return String.Format("Hello, {0}!", name);
}
}
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 { } } }
#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__
#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__
#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; }
#include "IHelloServiceClientBridge.h" #include <string> void CMFCApplicationDlg::OnBnClickedButton1() { try { std::string message = IHelloServiceClientBridge::SayHello("Your Name Here"); AfxMessageBox(CString(message.c_str())); } catch (...) { } }
希望你能从这个简单的例子中推断出适合你的需求。如果这不起作用,请让我知道,所以我可以修复这个帖子。
发布于 2018-03-27 10:49:05
对于那些有兴趣的人,我找到了一个半工作的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的代码可以在这里找到。需要对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分配给对象。
https://stackoverflow.com/questions/-100003222
复制相似问题