C# DLL 从 C++ 应用程序调用 SOAP Web 服务
C# DLL Calling SOAP Web Service From C++ App
根据大量在线文章,我创建了一个简单的 C# DLL Class 库,它只有一个名为 Add 的方法。它接受 2 个整数和 returns 一个整数,如下所示。我还去了项目属性 > 构建 > 并选择了 "Register for COM interop" 选项:
namespace CSDLL
{
[ComVisible(true)]
public interface IMyClass
{
int Add(int x, int y);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class MyClass : IMyClass
{
public int Add(int x, int y)
{
return x + y;
}
}
}
我成功构建了它(作为管理员)并且它生成了 CSDLL.dll
和 CSDLL.tlb
文件。
接下来我创建了一个简单的 C++ 控制台应用程序,如下所示:
#include "stdafx.h"
#include <Windows.h>
#import "C:\path\to\my\CSDLL.tlb" no_namespace
int main()
{
CoInitialize(NULL);
IMyClassPtr obj;
obj.CreateInstance(__uuidof(MyClass));
printf("Add result = %d\n", obj->Add(2, 5));
CoUninitialize();
getchar();
return 0;
}
如果我 运行 这样做,我会在控制台 window 上打印预期结果。所以,没关系。
接下来我修改了我的 Add
方法,这样它就不会添加 2 个整数,而是调用 SOAP 端点。我将代码包装在 try...catch 中,如果 SOAP 按照我的预期执行,我 return 1,如果它没有 return 我所期望的,我 return 2,我从内部 return 2一个 catch 块,意味着有一个像这样的异常:
public int Add()
{
try
{
BasicHttpsBinding binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.UseDefaultWebProxy = false;
EndpointAddress address = new EndpointAddress(myEndPointString);
// create service myService by passing it binding and endpoint address
// pass necessary parameters to the service
// set ClientCredentials for my service
// get response from the service
MyServiceResponse resp = myService.DoSomething();
if (resp.acknowledged)
{
return 1;
}
else
{
return 0;
}
}
catch (Exception ex)
{
// just return 2 in case of an exception
return 2;
}
}
如果我从 C++ 控制台应用程序调用这个修改后的 DLL,它总是 return 2 = 意味着我上面的代码抛出了一些异常。我正在使用 .NET Framework 4.8。
但是,如果我从通过在上面添加对我的 C# DLL 的引用创建的 C# 控制台应用程序调用它,对 Add
returns 1 = 的调用意味着它成功执行并产生结果我希望从中得到。
因此,相同的 C# DLL return会产生 2 个不同的结果,具体取决于它是从 C++ 还是从 C# 控制台应用程序调用的。
设置一些断点并调试我的 DLL 后,我发现调用 DoSomething
响应抛出异常并显示以下消息:
异常信息:
Message = "向 https://server-address:port/path 发出 HTTP 请求时发生错误。这可能是由于服务器证书在 HTTPS 情况下没有正确配置 HTTP.SYS。这也可能是由于客户端和服务器之间的安全绑定不匹配。
内部异常消息:
留言 = "Authentication failed because the remote party has closed the transport stream."
如果从 C++ 控制台应用程序调用 DLL 而不是从 C# 控制台应用程序调用相同的 DLL,为什么会出现此问题?我猜它必须做一些安全的事情,因为如果调用简单的数学 Add
方法,两者都会执行得很好,但如果调用一个执行 SOAP 请求的方法,C# 控制台应用程序将执行得很好,而 C++ 控制台应用程序将导致DLL 抛出异常?
经过大量阅读,结果发现这是因为 SecurityProtocol
被设置为不同的 SecurityProtocolType
如果从 C# 与 C++ 应用程序调用 C# DLL。
为了解决问题,我必须在发出任何请求之前像下面这样设置 SecurityProtocol
:
if (ServicePointManager.SecurityProtocol == (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls))
{
// This was crucial to do to make web service call work when C# DLL is called
// from a C++ app. It turns out that if webservice call is made:
// 1) from C# console app calling C# DLL that makes web service call,
// the SecurityProtocol is set to TLS | TLS11 | TLS12 | TLS13
// 2) from C++ console app calling C# DLL that makes web service call,
// the SecurityProtocol is set to SSL3 | TLS
// In case of 2) above, the call would throw exception as explained above
// whereas 1) would work just fine.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
// TLS13 was not available for some reason, so did not set it here
}
根据大量在线文章,我创建了一个简单的 C# DLL Class 库,它只有一个名为 Add 的方法。它接受 2 个整数和 returns 一个整数,如下所示。我还去了项目属性 > 构建 > 并选择了 "Register for COM interop" 选项:
namespace CSDLL
{
[ComVisible(true)]
public interface IMyClass
{
int Add(int x, int y);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class MyClass : IMyClass
{
public int Add(int x, int y)
{
return x + y;
}
}
}
我成功构建了它(作为管理员)并且它生成了 CSDLL.dll
和 CSDLL.tlb
文件。
接下来我创建了一个简单的 C++ 控制台应用程序,如下所示:
#include "stdafx.h"
#include <Windows.h>
#import "C:\path\to\my\CSDLL.tlb" no_namespace
int main()
{
CoInitialize(NULL);
IMyClassPtr obj;
obj.CreateInstance(__uuidof(MyClass));
printf("Add result = %d\n", obj->Add(2, 5));
CoUninitialize();
getchar();
return 0;
}
如果我 运行 这样做,我会在控制台 window 上打印预期结果。所以,没关系。
接下来我修改了我的 Add
方法,这样它就不会添加 2 个整数,而是调用 SOAP 端点。我将代码包装在 try...catch 中,如果 SOAP 按照我的预期执行,我 return 1,如果它没有 return 我所期望的,我 return 2,我从内部 return 2一个 catch 块,意味着有一个像这样的异常:
public int Add()
{
try
{
BasicHttpsBinding binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.UseDefaultWebProxy = false;
EndpointAddress address = new EndpointAddress(myEndPointString);
// create service myService by passing it binding and endpoint address
// pass necessary parameters to the service
// set ClientCredentials for my service
// get response from the service
MyServiceResponse resp = myService.DoSomething();
if (resp.acknowledged)
{
return 1;
}
else
{
return 0;
}
}
catch (Exception ex)
{
// just return 2 in case of an exception
return 2;
}
}
如果我从 C++ 控制台应用程序调用这个修改后的 DLL,它总是 return 2 = 意味着我上面的代码抛出了一些异常。我正在使用 .NET Framework 4.8。
但是,如果我从通过在上面添加对我的 C# DLL 的引用创建的 C# 控制台应用程序调用它,对 Add
returns 1 = 的调用意味着它成功执行并产生结果我希望从中得到。
因此,相同的 C# DLL return会产生 2 个不同的结果,具体取决于它是从 C++ 还是从 C# 控制台应用程序调用的。
设置一些断点并调试我的 DLL 后,我发现调用 DoSomething
响应抛出异常并显示以下消息:
异常信息: Message = "向 https://server-address:port/path 发出 HTTP 请求时发生错误。这可能是由于服务器证书在 HTTPS 情况下没有正确配置 HTTP.SYS。这也可能是由于客户端和服务器之间的安全绑定不匹配。 内部异常消息: 留言 = "Authentication failed because the remote party has closed the transport stream."
如果从 C++ 控制台应用程序调用 DLL 而不是从 C# 控制台应用程序调用相同的 DLL,为什么会出现此问题?我猜它必须做一些安全的事情,因为如果调用简单的数学 Add
方法,两者都会执行得很好,但如果调用一个执行 SOAP 请求的方法,C# 控制台应用程序将执行得很好,而 C++ 控制台应用程序将导致DLL 抛出异常?
经过大量阅读,结果发现这是因为 SecurityProtocol
被设置为不同的 SecurityProtocolType
如果从 C# 与 C++ 应用程序调用 C# DLL。
为了解决问题,我必须在发出任何请求之前像下面这样设置 SecurityProtocol
:
if (ServicePointManager.SecurityProtocol == (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls))
{
// This was crucial to do to make web service call work when C# DLL is called
// from a C++ app. It turns out that if webservice call is made:
// 1) from C# console app calling C# DLL that makes web service call,
// the SecurityProtocol is set to TLS | TLS11 | TLS12 | TLS13
// 2) from C++ console app calling C# DLL that makes web service call,
// the SecurityProtocol is set to SSL3 | TLS
// In case of 2) above, the call would throw exception as explained above
// whereas 1) would work just fine.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
// TLS13 was not available for some reason, so did not set it here
}