C# DLL从C++应用程序调用SOAP Web服务

3

在网上阅读了许多文章后,我创建了一个简单的C# DLL类库,其中只有一个名为Add的方法。它接受两个整数并返回一个整数,如下所示。我还进入了项目属性>生成>并选择了“注册COM互操作”选项:

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.dllCSDLL.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;
}

如果我运行这个程序,我会在控制台窗口上打印出预期的结果,所以一切都很好。

接下来,我修改了我的Add方法,使其不是添加两个整数,而是调用一个SOAP端点。我将代码包装在try...catch中,如果SOAP按照我预期执行,则返回1,如果它没有返回我期望的内容,则返回0,并且我从catch块内返回2,表示有异常,如下所示:

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,它总是返回2 = 意味着某个异常被抛出并被我的代码捕获。我使用的是.NET Framework 4.8。
然而,如果我从一个添加了对上述C# DLL引用的C#控制台应用程序中调用它,调用Add会返回1 = 意味着它成功执行并得到我期望得到的结果。
因此,同一个C# DLL返回2种不同的结果,具体取决于它是从C++还是C#控制台应用程序中调用的。
在设置一些断点和调试我的DLL后,我发现对DoSomething 的调用响应会抛出以下消息的异常:
异常消息: Message =“在进行HTTP请求https://server-address:port/path时发生错误。这可能是由于服务器证书在HTTPS情况下没有正确配置HTTP.SYS造成的。这也可能是由于客户端和服务器之间的安全绑定不匹配。 内部异常消息: Message =“由于远程方关闭了传输流,所以身份验证失败。”
如果从C++控制台应用程序中调用DLL,为什么会出现这个问题,而从C#控制台应用程序中调用相同的DLL却不会出现这个问题?我猜这与安全有关,因为如果调用简单的数学Add方法,则两者都将执行得很好,但如果调用执行SOAP请求的方法,则C#控制台应用程序将执行得很好,而C++控制台应用程序将导致DLL抛出异常。
1个回答

3
在大量阅读后,我发现原因是当从C#与C++应用程序中调用C# DLL时,SecurityProtocol会被设置为不同的 SecurityProtocolType
为了解决这个问题,在进行任何请求之前,我需要像下面这样设置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
}

那救了我的一天!对于那些看到这篇文章的人,这段代码必须放在创建网络请求之前,这听起来很明显,但最好还是说一下。如果你想启用TLS 1.3,你可以使用以下小技巧:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; | (SecurityProtocolType)12288; // Tls13; - Gerald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接