为非托管 C++ 客户端创建 WCF 服务
我需要让非托管 Windows C++ 客户端与 WCF 服务对话.C++ 客户端可以在 Win2000 及更高版本上运行.我可以控制 WCF 服务和正在使用的 C++ API.由于它用于专有应用程序,因此最好尽可能使用 Microsoft 的东西,绝对不是 GNU 许可的 API.那些已经开始工作的人,你能分享一个如何让它工作的分步过程吗?
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?
到目前为止,我已经研究了以下选项:
I have researched following options so far:
- WWSAPI - 不好,不能在 Win 2000 客户端上运行.
- ATL 服务器,使用 以下指南 作为参考.我遵循了概述的步骤(删除策略引用并扁平化 WSDL),但是生成的 WSDL 仍然不能被 sproxy 使用
- WWSAPI - not good, will not work on Win 2000 clients.
- 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
还有什么想法吗?请仅在您自己实际使用时才回答.
Any more ideas? Please answer only if you actually have it working yourself.
Edit1:对于我可能感到困惑的任何人,我深表歉意:我正在寻找一种从没有 .NET 的客户端调用 WCF 服务的方法框架已安装,因此不能使用基于 .NET 的帮助程序库,它必须是纯非托管 C++
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++
推荐答案
对于那些有兴趣的人,我找到了一个半工作的 ATL Server 解决方案.以下是主机代码,注意它使用的是 BasicHttpBinding,这是唯一一个与 ATL Server 一起工作的代码:
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:
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 会失败.以下是需要更改的功能:
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:
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++头文件:
Next step is to generate C++ header:
sproxy.exe /wsdl http://localhost:8200/Service1?wsdl
然后C++程序看起来像这样:
and then C++ program looks like this:
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 分配给对象.
Resulting C++ code handles complex types pretty decently, except that it cannot assign NULL to the objects.
相关文章