C# DLL Calling SOAP Веб-сервис из приложения C ++ - PullRequest
2 голосов
/ 06 февраля 2020

После многочисленных статей в Интернете я создал простую библиотеку классов DLL C#, в которой есть только один метод с именем Add. Он принимает 2 целых числа и возвращает целое число, как показано ниже. Я также зашел в свойства проекта> build> и выбрал опцию «Зарегистрироваться для 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.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;
}

Если я запусту это, я получу ожидаемый результат, напечатанный в окне консоли. Итак, все в порядке.

Затем я изменил свой метод Add, чтобы вместо добавления 2 целых чисел он вызывал конечную точку SOAP. Я обернул код в try ... catch и я возвращаю 1, если SOAP выполнено так, как я ожидал, 0, если он не вернул то, что я ожидал, и я возвращаю 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;
    }
}

Если я вызываю эту модифицированную DLL из консольного приложения C ++, она всегда возвращает 2 =, что означает, что какое-то исключение было сгенерировано моим кодом выше. Я нахожусь. NET Framework 4.8.

Однако, если я вызываю его из консольного приложения C#, которое я создал, добавив ссылку на мою C# DLL выше, вызов Add вернется 1 = означает, что он успешно выполнен с результатом, который я ожидаю получить от него.

Итак, одна и та же C# DLL возвращает 2 разных результата в зависимости от того, вызывается ли она из C ++ или C# Консольного приложения.

После установки некоторых точек останова и отладки моей библиотеки DLL я обнаружил, что при вызове ответа DoSomething выдается исключение со следующими сообщениями:

Сообщение об исключении: Сообщение = "Произошла ошибка при отправке HTTP-запроса на https://server-address: порт / путь . Это может быть связано с тем, что сертификат сервера не настроен должным образом с HTTP.SYS в случае HTTPS. Это также может быть вызвано несоответствие привязки безопасности между клиентом и сервером. Внутреннее исключительное сообщение: Message = "Аутентификация не удалась, потому что удаленная сторона закрыла транспортный поток."

Почему возникает эта проблема, если DLL вызывается из консольного приложения C ++, а не в том случае, если та же DLL вызывается из консольного приложения C#? Она должна что-то делать с безопасностью, я полагаю, что оба будут работать нормально, если вызывается простой математический метод Add, но если вызывается метод, SOAP запрашивающий, консольное приложение C# будет работать нормально, тогда как консольное приложение C ++ вызовет в DLL исключение?

1 Ответ

1 голос
/ 06 февраля 2020

После долгих чтений выясняется, что причиной этого было то, что SecurityProtocol устанавливается в другое значение SecurityProtocolType, если DLL-библиотека C# вызывается из приложения C# vs C ++.

Чтобы решить, мне нужно было установить 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
}
...