program tip

관리되지 않는 C ++ 클라이언트 용 WCF 서비스 만들기

radiobox 2020. 12. 9. 08:00
반응형

관리되지 않는 C ++ 클라이언트 용 WCF 서비스 만들기


WCF 서비스와 통신하려면 관리되지 않는 Windows C ++ 클라이언트를 가져와야합니다. C ++ 클라이언트는 Win2000 이상에서 실행될 수 있습니다. WCF 서비스와 사용중인 C ++ API를 모두 제어 할 수 있습니다. 독점 응용 프로그램을위한 것이므로 가능하면 GNU 라이센스 API가 아닌 Microsoft 제품을 사용하는 것이 좋습니다. 작동하는 사람들, 작동하도록 만드는 단계별 프로세스를 공유 할 수 있습니까?

지금까지 다음 옵션을 조사했습니다.

  • WWSAPI-좋지 않습니다. Win 2000 클라이언트에서는 작동하지 않습니다.
  • ATL 서버, 다음 가이드 를 참조로 사용합니다. 설명 된 단계 (정책 참조 제거 및 WSDL 평면화)를 따랐지만 결과 WSDL은 여전히 ​​sproxy에서 사용할 수 없습니다.

더 많은 아이디어가 있습니까? 실제로 직접 작동하는 경우에만 대답하십시오.

Edit1 : 혼란 스러울 수있는 모든 사람에게 사과드립니다. 제가 찾고 있던 것은 .NET 프레임 워크가 설치되지 않은 클라이언트에서 WCF 서비스를 호출하는 방법 이었으므로 .NET 기반 도우미 라이브러리를 사용하는 것은 옵션이 아닙니다. 순수 관리되지 않는 C ++이어야합니다.


기본 아이디어는 클라이언트 용 WCF 코드를 C #으로 작성하고 (이 방법이 더 쉽습니다) C ++ 브리지 dll을 사용하여 관리되지 않는 C ++ 코드와 C #으로 작성된 관리되는 WCF 코드 사이의 간격을 메우는 것입니다.

다음은 .NET 3.5 SP1과 함께 Visual Studio 2008을 사용하는 단계별 프로세스입니다.

  1. 가장 먼저 할 일은 WCF 서비스를 만들고이를 호스팅하는 방법입니다. 이미 가지고있는 경우 아래 7 단계로 건너 뜁니다. 그렇지 않으면 여기 의 단계에 따라 Windows NT 서비스를 만듭니다 . 프로젝트 및 프로젝트에 추가 된 모든 클래스에 대해 VS2008에서 제공하는 기본 이름을 사용합니다. 이 Windows NT 서비스는 WCF 서비스를 호스팅합니다.

    • HelloService라는 WCF 서비스를 프로젝트에 추가합니다. 이렇게하려면 솔루션 탐색기 창에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가 | 새 항목 ... 메뉴 항목을 선택합니다. 새 항목 추가 대화 상자에서 C # WCF 서비스 템플릿을 선택하고 추가 단추를 클릭합니다. 그러면 인터페이스 파일 (IHelloService.cs), 클래스 파일 (HelloService.cs) 및 기본 서비스 구성 파일 (app.config)의 형식으로 프로젝트에 HelloService가 추가됩니다.

    • 다음과 같이 HelloService를 정의하십시오.

``

    [ServiceContract]
    public interface IHelloService
    {
        [OperationContract]
        string SayHello(string name);
    }
    public class HelloService : IHelloService
    {
        public string SayHello(string name)
        {
            return String.Format("Hello, {0}!", name);
        }
    }
  • 위의 1 단계에서 만든 Service1 클래스를 다음과 같이 수정합니다.

    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 {
            }
        }
    }
    
  • 프로젝트를 빌드하십시오.

  • Visual Studio 2008 명령 프롬프트를 엽니 다. 프로젝트의 출력 디렉터리로 이동합니다. 다음을 입력하십시오.`installutil WindowsService1.exe '로컬 시스템에 Windows NT 서비스를 설치합니다. 서비스 제어판을 열고 Service1 서비스를 시작합니다. 아래의 9 단계가 작동하려면이 작업을 수행하는 것이 중요합니다.

    1. Open another instance of Visual Studio 2008 and create an MFC application, which is about as far away as you can get from WCF. As an example, I simply created a dialog MFC application and added a Say Hello! button to it. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug. Under the C/C++ General settings, add ..\HelloServiceClientBridge to the Additional Include Directories. Under the Linker General settings, add ..\Debug to the Additional Library Directories. Click the OK button.
  • File 메뉴에서 Add | New Project ... 메뉴 항목을 선택합니다. C # 클래스 라이브러리 템플릿을 선택합니다. 이름을 HelloServiceClient로 변경하고 확인 버튼을 클릭합니다. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성 메뉴 옵션을 선택합니다. 빌드 탭에서 어셈블리 및 app.config 파일이 MFC 애플리케이션과 동일한 디렉터리에 있도록 출력 경로를 .. \ bin \ Debug로 변경합니다. 이 라이브러리에는 Windows NT 서비스에서 호스팅되는 WCF Hello 서비스에 대한 서비스 참조, 즉 WCF 프록시 클래스가 포함됩니다.

  • 솔루션 탐색기에서 HelloServiceClient 프로젝트의 참조 폴더를 마우스 오른쪽 단추로 클릭하고 서비스 참조 추가 ... 메뉴 옵션을 선택하십시오. 주소 필드에 Hello 서비스의 주소를 입력하십시오. 위의 2 단계에서 만든 app.config 파일의 기본 주소와 동일해야합니다. 이동 버튼을 클릭합니다. Hello 서비스가 서비스 목록에 표시되어야합니다. 확인 버튼을 클릭하여 Hello 서비스에 대한 프록시 클래스를 자동으로 생성합니다. 노트: 이 프로세스에서 생성 된 Reference.cs 파일에서 항상 컴파일 문제가 발생하는 것 같습니다. 내가 잘못하고 있는지 또는 버그가 있는지는 모르겠지만이를 수정하는 가장 쉬운 방법은 Reference.cs 파일을 직접 수정하는 것입니다. 이 문제는 일반적으로 네임 스페이스 문제이며 최소한의 노력으로 해결할 수 있습니다. 이것이 가능성이 있다는 것을 알아 두십시오. 이 예에서는 HelloServiceClient.ServiceReference1을 단순히 HelloService로 변경했습니다 (다른 필수 변경 사항 포함).

  • MFC 응용 프로그램이 WCF 서비스와 상호 작용할 수 있도록하려면 관리되는 C ++ "브리지"DLL을 빌드해야합니다. File 메뉴에서 Add | New Project ... 메뉴 항목을 선택합니다. C ++ Win32 프로젝트 템플릿을 선택합니다. 이름을 HelloServiceClientBridge로 변경하고 확인 버튼을 클릭합니다. 응용 프로그램 설정의 경우 응용 프로그램 유형을 DLL로 변경하고 빈 프로젝트 확인란을 선택합니다. 마침 버튼을 클릭합니다.

  • 가장 먼저 할 일은 프로젝트 속성을 수정하는 것입니다. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성 메뉴 옵션을 선택합니다. 일반 설정에서 출력 디렉토리를 .. \ bin \ Debug로 변경하고 공용 언어 런타임 지원 옵션을 공용 언어 런타임 지원 (/ clr)으로 변경하십시오. 프레임 워크 및 참조 설정에서 .NET System, System.ServiceModel 및 mscorlib 어셈블리에 대한 참조를 추가합니다. 확인 버튼을 클릭합니다.

  • HelloServiceClientBridge 프로젝트에 HelloServiceClientBridge.h, IHelloServiceClientBridge.h 및 HelloServiceClientBridge.cpp 파일을 추가하십시오.

  • IHelloServiceClientBridge.h를 다음과 같이 수정합니다.

    #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__
    
  • HelloServiceClientBridge.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__
    
  • .cpp 파일의 구문은 관리되는 C ++를 사용하므로 익숙해집니다. HelloServiceClientBridge.cpp를 다음과 같이 수정하십시오.

    #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;
    }
    
  • 남은 작업은 MFC 응용 프로그램을 업데이트하여 SayHello () WCF 서비스 호출을 호출하는 것뿐입니다. MFC 양식에서 Say Hello! 버튼을 클릭하여 ButtonClicked 이벤트 핸들러를 생성합니다. 이벤트 핸들러를 다음과 같이 만드십시오.

    #include "IHelloServiceClientBridge.h"
    #include <string>
    void CMFCApplicationDlg::OnBnClickedButton1()
    {
        try {
            std::string message = IHelloServiceClientBridge::SayHello("Your Name Here");
            AfxMessageBox(CString(message.c_str()));
        } catch (...) {
        }
    }
    
  • 애플리케이션을 실행하고 Say Hello! 단추. 이렇게하면 응용 프로그램이 Windows NT 서비스에서 호스팅되는 WCF Hello 서비스의 SayHello () 메서드를 호출하게됩니다 (하지만 여전히 실행 중이어야 함). 그런 다음 반환 값이 메시지 상자에 표시됩니다.

이 간단한 예제에서 필요에 맞게 추정 할 수 있기를 바랍니다. 그래도 문제가 해결되지 않으면 알려 주시면 게시물을 수정할 수 있습니다.


관심있는 사람들을 위해 반 작동 ATL 서버 솔루션을 찾았습니다. 다음은 호스트 코드입니다. BasicHttpBinding을 사용하고 있으며 ATL 서버에서 작동하는 유일한 코드입니다.

        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을 할당 할 수 없다는 점을 제외하고는 복잡한 유형을 상당히 적절하게 처리합니다.


I would create a C# managed class to do the WCF work and expose the class as a COM object to the C++ clients.


You can implement a SOAP client somewhat easily using the deprecated MS Soap Toolkit. Unfortunately, there doesn't seem to be a replacement for this outside of moving to .NET.


Can you publish a REST Web Service and use the MSXML COM library -- should be already installed, has an XML parser, and an HTTP library.

http://msdn.microsoft.com/en-us/library/ms763742.aspx

참고URL : https://stackoverflow.com/questions/686452/create-wcf-service-for-unmanaged-c-clients

반응형