C++에서 C# 코드 호출

Syed Hassan Sabeeh Kazmi 2023년10월12일
  1. C++/Cli를 중간 DLL로 사용하여 C++에서 C# 코드 호출
  2. Reverse P/Invoke 기술을 사용하여 C++에서 C# 코드 호출
  3. COM 시스템을 사용하여 C++에서 C# 코드 호출
  4. ICLRRuntimeHost::ExecuteInDefaultAppDomain() CLR 호스팅을 사용하여 C++에서 C# 코드 호출
  5. IPC - 프로세스 간 통신을 사용하여 C++에서 C# 코드 호출
  6. C++에서 C# 코드를 호출하도록 HTTP 서버 호스팅
C++에서 C# 코드 호출

C++는 프로그래머에게 완벽한 교차 플랫폼 솔루션을 제공하기 위해 C# 함수를 호출하는 여러 가지 방법을 제공합니다. 이 자습서에서는 C++에서 C# 코드를 호출하는 모든 가능한 방법을 알려줍니다.

C++/CLI를 중간 DLL로 사용하거나 역 P/Invoke를 사용하거나 COM, CLR 호스팅, 프로세스 간 통신(IPC)을 사용하거나 HTTP 서버를 호스트하고 HTTP 동사를 통해 호출하여 C++에서 C# 코드/함수를 호출할 수 있습니다. CLR 호스팅은 그렇게 하는 좋은 방법이지만 프로그래머가 int method(string arg) 형식을 가진 메서드만 호출하도록 제한합니다.

플랫폼 호출, C++ interop, COM interop 및 Embedded interop 유형은 상호 운용성을 달성하기 위한 메커니즘 중 일부입니다. 프로그래머는 C#/C++ 상호 운용성 코드가 특히 관리되지 않는 코드와 관리되는 코드 사이의 경계에서 디버그하기가 매우 어려울 수 있음을 인정해야 합니다.

C++/Cli를 중간 DLL로 사용하여 C++에서 C# 코드 호출

일반적으로 대리자를 함수 포인터로 마샬링하고 관리되지 않는 쪽에 전달할 수 있습니다. 이렇게 하면 관리되는 쪽에 상호 작용을 시작해야 합니다. C++의 코드.

다른 기술로 개발된 시스템과 상호 작용해야 하기 때문에 전문적인 환경에서 소프트웨어를 개발할 때 이기종성은 예외가 아니라 규칙입니다. .NET과 C#.

C++/CLI 플랫폼은 관리(C#) 코드와 네이티브(C++) 코드를 한 곳에서 혼합할 수 있는 고유한 기능을 보유하고 있으며 간단한 래퍼를 사용하여 이 두 언어 사이에 브리지를 구축하기 위한 이상적인 도구로 작동합니다.

이는 지루하고 오류가 발생하기 쉬운 프로세스로 남아 있는 간단한 접근 방식입니다. 단, 관리되는 형식으로 변환하거나 변환하는 데 연결 코드가 필요한 경우 복잡성이 발생할 수 있습니다.

C# 라이브러리:

using System;  // contains the definition of random class

namespace _stockManager {
  public struct _recruite {
    public double _tactics { get; set; }
    public double _mind { get; set; }
    public double _agility { get; set; }
    public double _experience { get; set; }
  }

  public class API {
    private Random _random = new Random();

    public _recruite _manager(string symbol) {
      double mind = _random.Next(95, 105);

      return new _recruite { _tactics = mind - 0.1, _mind = mind, _agility = mind + 0.1,
                             _experience = mind * 10e6 };
    }
  }
}

C++/CLI 래퍼(헤더 파일):

class stockmanager_APIWrapper_private;

struct __declspec(dllexport) _recruite {
  double tactics;
  double mind;
  double agility;
  double experience;
};

class __declspec(dllexport) APIWrapper_stockmanager {
 private:
  stockmanager_APIWrapper_private* _private;

 public:
  APIWrapper_stockmanager();

 public:
  ~APIWrapper_stockmanager();

 public:
  const _recruite _manager(const char* symbol);
};

C++ 정의 파일:

#using "StockMarketAPI.dll"

#include <msclr\auto_gcroot.h>

#include "APIWrapper_stockmanager.h"

class stockmanager_APIWrapper_private {
 public:
  msclr::auto_gcroot<StockMarket::API ^> API;
};

APIWrapper_stockmanager::APIWrapper_stockmanager() {
  _private = new stockmanager_APIWrapper_private();
  _private->API = gcnew _stockManager::API();
}

const _recruite APIWrapper_stockmanager::_manager(const char* symbol) {
  _stockManager::_recruite managedQuote =
      _private->API->_manager(gcnew System::String(symbol));

  _recruite nativeQuote;
  nativeQuote.tactics = managedQuote._tactics;
  nativeQuote.mind = managedQuote._mind;
  nativeQuote.agility = managedQuote._agility;
  nativeQuote.experience = managedQuote._experience;

  return nativeQuote;
}

APIWrapper_stockmanager::~APIWrapper_stockmanager() { delete _private; }

네이티브 C++ 애플리케이션:

#include <iostream>

#include "APIWrapper_stockmanager.h"

int main() {
  const char* stock = "GOOG";

  APIWrapper_stockmanager API;

  _recruite get_recruite = API._manager(stock);

  std::cout << "Tactics: " << get_recruite.tactics << std::endl;
  std::cout << "Mind: " << get_recruite.mind << std::endl;
  std::cout << "Agility: " << get_recruite.agility << std::endl;
  std::cout << "Experience: " << get_recruite.experience << std::endl;
}

출력:

test.exe
Tactics: 103.9
Mind: 104
Agility: 104.1
Experience: 1.04e+09

Reverse P/Invoke 기술을 사용하여 C++에서 C# 코드 호출

이를 통해 C++ 프로그래머는 비관리 코드에서 관리 라이브러리의 구조체, 콜백 및 함수에 액세스하거나 그 반대로 액세스할 수 있습니다. C#에서 SystemSystem.Runtime.InteropServices 네임스페이스에는 P/Invoke API가 포함되어 있어 기본 구성 요소와 통신할 수 있습니다.

C# 또는 C++ 코드를 작성하여 둘 다 수행할 수 있으며 런타임은 이 통신이 양방향으로 흐르도록 허용합니다. 네이티브 코드에서 관리 코드로의 콜백을 허용하기 위해 함수 포인터(대리자 - 함수 포인터에 가장 가까운 항목)를 사용하여 네이티브 함수에서 관리 코드로 다시 콜백할 수 있습니다.

이 메서드에서 서명과 일치하는 라이브 콜백에 대한 대리자를 정의하고 이를 외부 메서드로 전달하면 런타임이 모든 것을 처리합니다. C# 함수가 포함된 C++ 코드를 실행하기 전에 관리되지 않는 함수의 서명을 검토하는 것이 중요합니다. 예를 들어 BOOL EnumWindows (WNDENUMPROC lpEnumFunc, LPARAM lParam); 모든 창과 BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM lParam);을 열거하는 함수 호출입니다. 콜백 서명입니다.

using System.Runtime.InteropServices;

public class _synch_doc {
  public delegate void callback_synch(string str);

  public static void doc_call(string str) {
    System.Console.WriteLine("Managed: " + str);
  }

  public static int Main() {
    caller("Hello World!", 10, new callback_synch(_synch_doc.doc_call));
    return 0;
  }

  [DllImport("nat.dll", CallingConvention = CallingConvention.StdCall)]
  public static extern void caller(string str, int _algebra, callback_synch call);
}
#include <stdio.h>
#include <string.h>

typedef void(__stdcall *callback_synch)(wchar_t *str);
extern "C" __declspec(dllexport) void __stdcall caller(wchar_t *input,
                                                       int _algebra,
                                                       callback_synch call) {
  for (int x = 0; x < _algebra; x++) {
    call(input);
  }
}

출력:

Hello World!

COM 시스템을 사용하여 C++에서 C# 코드 호출

구성 요소 개체 모델 - COM(상호 작용 가능한 이진 소프트웨어 구성 요소를 만들기 위한 플랫폼 독립적, 개체 지향 및 분산 시스템)은 .NET 구성 요소가 노출될 때 C# 함수를 호출하는 데 도움이 될 수 있습니다. 프로그래머는 .NET 유형을 작성하고 관리되지 않는 코드에서 해당 유형을 사용하여 COM을 사용하여 수행할 수 있습니다.

COM을 사용하기 전에 중요하다고 알려진 몇 가지 전제 조건이 있으며 여기에는 다음이 포함됩니다. COM에 노출되는 모든 관리 유형, 속성, 이벤트, 메서드 및 필드는 공용이어야 하고, 관리 코드 내의 사용자 지정 특성은 구성 요소의 상호 운용성을 향상할 수 있어야 하며, 개발자는 참조 및 배포와 관련된 단계를 요약해야 합니다. 어셈블리.

또한 어셈블리를 등록하고, .NET 유형을 참조하고, COM을 통해 .NET 개체를 호출하여 COM에서 관리되는 유형과 글로벌 어셈블리 캐시에 설치할 수 있고 게시자의 서명이 필요한 강력한 이름의 어셈블리를 사용합니다. .

다음 예제는 모기지 계산을 수행하는 COM 클라이언트와 .NET 서버의 상호 운용을 보여줍니다. 클라이언트는 관리되는 클래스의 인스턴스를 만들고 호출하며 계산을 표시하기 위해 인스턴스에 네 개의 인수를 전달합니다.

COM 클라이언트:

// ConLoan.cpp : Defines the entry point for the console application.
#import "..\LoanLib\_azythro_call.tlb" raw_interfaces_only
#include "stdafx.h"
using namespace _azythro_call;

int main(int argc, char* argv[]) {
  HRESULT hr = CoInitialize(NULL);

  ILoanPtr pILoan(__uuidof(_azythro));

  if (argc < 5) {
    printf("Usage: ConLoan Balance Rate Term Payment\n");
    printf("    Either Balance, Rate, Term, or Payment must be 0\n");
    return -1;
  }

  double acc_initial_balance = atof(argv[1]);
  double acc_balance_rate = atof(argv[2]) / 100.0;
  short acc_payment_term = atoi(argv[3]);
  double acc_bal_payment = atof(argv[4]);

  pILoan->put_OpeningBalance(acc_initial_balance);
  pILoan->put_Rate(acc_balance_rate);
  pILoan->put_Term(acc_payment_term);
  pILoan->put_Payment(acc_bal_payment);

  if (acc_initial_balance == 0.00)
    pILoan->calculate_balance_opening(&acc_initial_balance);
  if (acc_balance_rate == 0.00) pILoan->calculate_rate(&acc_balance_rate);
  if (acc_payment_term == 0) pILoan->calculate_term(&acc_payment_term);
  if (acc_bal_payment == 0.00) pILoan->calculate_payment(&acc_bal_payment);

  printf("Balance = %.2f\n", acc_initial_balance);
  printf("Rate    = %.1f%%\n", acc_balance_rate * 100);
  printf("Term    = %.2i\n", acc_payment_term);
  printf("Payment = %.2f\n", acc_bal_payment);

  VARIANT_BOOL extra_PMTs;
  double user_acc_balance = 0.0;
  double Principal = 0.0;
  double user_acc_interest = 0.0;

  printf("%4s%10s%12s%10s%12s\n", "Nbr", "Payment", "Principal", "Interest",
         "Balance");
  printf("%4s%10s%12s%10s%12s\n", "---", "-------", "---------", "--------",
         "-------");

  pILoan->get_1stPMT_dist(acc_bal_payment, &user_acc_balance, &acc_principal,
                          &user_acc_interest, &extra_PMTs);

  for (short PmtNbr = 1; extra_PMTs; PmtNbr++) {
    printf("%4i%10.2f%12.2f%10.2f%12.2f\n", PmtNbr, acc_bal_payment,
           acc_principal, user_acc_interest, user_acc_balance);

    pILoan->get_nextPMT_dist(acc_bal_payment, &user_acc_balance, &acc_principal,
                             &user_acc_interest, &extra_PMTs);
  }

  CoUninitialize();
  return 0;
}

.NET 서버:

using System;
using System.Reflection;

[assembly:AssemblyKeyFile("sample.snk")]
namespace _azythro_call {

  public interface loan_call {
    double balance_opening { get; set; }
    double balance_rate { get; set; }
    double user_payment { get; set; }
    short payment_term { get; set; }
    String per_risk_rate { get; set; }

    double calculate_payment();
    double calculate_balance_opening();
    double calculate_rate();
    short calculate_term();
    bool get_1stPMT_dist(double cal_PMT_AMT, ref double user_acc_balance, out double acc_principal,
                         out double user_acc_interest);
    bool get_nextPMT_dist(double cal_PMT_AMT, ref double user_acc_balance, out double acc_principal,
                          out double user_acc_interest);
  }

  public class _azythro : loan_call {
    private double acc_initial_balance;
    private double acc_balance_rate;
    private double acc_bal_payment;
    private short acc_payment_term;
    private String rating_calculatedRisk;

    public double balance_opening {
      get { return acc_initial_balance; }
      set { acc_initial_balance = value; }
    }

    public double balance_rate {
      get { return acc_balance_rate; }
      set { acc_balance_rate = value; }
    }

    public double user_payment {
      get { return acc_bal_payment; }
      set { acc_bal_payment = value; }
    }

    public short payment_term {
      get { return acc_payment_term; }
      set { acc_payment_term = value; }
    }

    public String per_risk_rate {
      get { return rating_calculatedRisk; }
      set { rating_calculatedRisk = value; }
    }

    public double calculate_payment() {
      user_payment = Util.Round(
          balance_opening * (balance_rate / (1  Math.Pow((1 + balance_rate), -payment_term))), 2);
      return user_payment;
    }

    public double calculate_balance_opening() {
      balance_opening = Util.Round(
          user_payment / (balance_rate / (1 - Math.Pow((1 + balance_rate), -payment_term))), 2);
      return balance_opening;
    }

    public double calculate_rate() {
      double acc_payment_calculated = user_payment;

      for (balance_rate = 0.001; balance_rate < 28.0; balance_rate += 0.001) {
        user_payment = Util.Round(
            balance_opening * (balance_rate / (1  Math.Pow((1 + balance_rate), -payment_term))),
            2);

        if (user_payment >= acc_payment_calculated)
          break;
      }
      return balance_rate;
    }

    public short calculate_term() {
      double acc_payment_calculated = user_payment;

      for (payment_term = 1; payment_term < 480; payment_term++) {
        user_payment = Util.Round(
            balance_opening * (balance_rate / (1  Math.Pow((1 + balance_rate), -payment_term))),
            2);

        if (user_payment <= acc_payment_calculated)
          break;
      }

      return payment_term;
    }

    public bool get_1stPMT_dist(double cal_PMT_AMT, ref double user_acc_balance,
                                out double acc_principal, out double user_acc_interest) {
      user_acc_balance = balance_opening;
      return get_nextPMT_dist(cal_PMT_AMT, ref user_acc_balance, out acc_principal,
                              out user_acc_interest);
    }

    public bool get_nextPMT_dist(double cal_PMT_AMT, ref double user_acc_balance,
                                 out double acc_principal, out double user_acc_interest) {
      user_acc_interest = Util.Round(user_acc_balance * balance_rate, 2);
      acc_principal = Util.Round(cal_PMT_AMT - user_acc_interest, 2);
      user_acc_balance = Util.Round(user_acc_balance - acc_principal, 2);

      if (user_acc_balance <= 0.0)
        return false;

      return true;
    }
  }

  internal class Util {
    public static double Round(double value, short digits) {
      double factor = Math.Pow(10, digits);
      return Math.Round(value * factor) / factor;
    }
  }
}

출력:

[load] sample.snk

Balance: 300000
Rate: 24000
Term: 1200
Payment: 175000

ICLRRuntimeHost::ExecuteInDefaultAppDomain() CLR 호스팅을 사용하여 C++에서 C# 코드 호출

ICLRRuntimeHost::ExecuteInDefaultAppDomain 메서드는 지정된 관리되는 어셈블리에서 지정된 유형의 지정된 메서드를 호출합니다. 구문은 HRESULT ExecuteInDefaultAppDomain ([in] LPCWSTR pwzAssemblyPath, [in] LPCWSTR pwzTypeName, [in] LPCWSTR pwzMethodName, [in] LPCWSTR pwzArgument, [out] DWORD *pReturnValue );와 같습니다. 여기서 각 매개변수는 각각 경로, 유형, 이름, 메서드를 전달할 문자열 매개변수 및 호출된 메서드에서 반환된 정수 값을 나타냅니다.

또한 호출된 메서드에는 static int pwzMethodName (String pwzArgument) 서명이 있어야 합니다. 여기서 pwzArgument는 해당 호출 메서드에 매개 변수로 전달된 문자열 값을 나타냅니다. 일반적으로 CLR 호스팅을 사용하여 네이티브 C++ 프로그램에 모든 CLR 어셈블리를 포함할 수 있으며, SQL Server와 같은 네이티브 프로그램이 SQL CLR과 같은 .NET 코드 확장을 지원하고 CorBindToRuntimeEx()를 사용하여 대규모 프로세스에 로드할 수 있는 이유입니다. 또는 CLRCreateInstance()는 각각 .NET Framework 버전 2.0 또는 4에 따라 다릅니다.

SharedMemory.h:

#pragma const_onetime_impl

#ifdef SHAREDMEM_EXPORTS
#define SHAREDMEM_API __declspec(export_dll)
#else
#define SHAREDMEM_API __declspec(import_dll)
#endif

#define conv_call_sharedmemory __cdecl

extern "C" {
SHAREDMEM_API BOOL conv_call_sharedmemory SetSharedMem(ULONGLONG _64bitValue);
SHAREDMEM_API BOOL conv_call_sharedmemory GetSharedMem(ULONGLONG* p64bitValue);
}

SharedMemory.cpp:

#include "SharedMemory.h"
#include "custom_made.h"  // a user created/defined library | customizable according to your preference

HANDLE object_mapfile_handling = NULL;  // an object to handle mapped file
LPVOID ponter_sharedMemory =
    NULL;  // a pointer to the shared memory (SharedMemory)
const int size_type_sharedMemory =
    sizeof(ULONGLONG);  // the size type of shared memory

BOOL sharedMemory_init_create() {
  // named mapped_file object creation
  object_mapfile_handling = CreateFileMapping(
      INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size_type_sharedMemory,
      TEXT("file_sharedMemory")  // initialize the name of shared memory file
  );

  if (object_mapfile_handling == NULL) {
    return FALSE;
  }

  BOOL memory_access_information = (ERROR_ALREADY_EXISTS != GetLastError());

  // access / get a pointer to the shared memory
  ponter_sharedMemory =
      MapViewOfFile(object_mapfile_handling, FILE_MAP_WRITE, 0, 0, 0);

  if (ponter_sharedMemory == NULL) {
    return FALSE;
  }

  if (memory_access_information)  // First time the shared memory is accessed?
  {
    ZeroMemory(ponter_sharedMemory, size_type_sharedMemory);
  }

  return TRUE;
}

BOOL set_type_sharedMemory(ULONGLONG _64bitValue) {
  BOOL check_set = sharedMemory_init_create();

  if (check_set) {
    ULONGLONG* new_sharedMemory_pointer = (ULONGLONG*)ponter_sharedMemory;
    *new_sharedMemory_pointer = _64bitValue;
  }

  return check_set;
}

BOOL get_sharedMemory(ULONGLONG* p64bitValue) {
  if (p64bitValue == NULL) return FALSE;

  BOOL check_get = sharedMemory_init_create();

  if (check_get) {
    ULONGLONG* new_sharedMemory_pointer = (ULONGLONG*)ponter_sharedMemory;
    *p64bitValue = *new_sharedMemory_pointer;
  }

  return check_get;
}
namespace shared_memory_csharp {
  delegate void _void_init();

  static public class command_line_interface {
    [import_dll("SharedMemory.dll")]
    static extern bool set_type_sharedMemory(Int64 value);

    static GCHandle delegate_cust_handle;

    public static int prog_point_entry(string ignored) {
      IntPtr primary_function = IntPtr.Zero;
      Delegate new_function_delegate = new VoidDelegate(_custom_method);
      delegate_cust_handle = GCHandle.Alloc(new_function_delegate);
      primary_function = Marshal.GetFunctionPointerForDelegate(new_function_delegate);
      bool bool_set_ok = set_type_sharedMemory(primary_function.ToInt64());
      return bool_set_ok ? 1 : 0;
    }

    public static void primary_method() {
      MessageBox.Show("Hello from C# primary_method!");
      delegate_cust_handle.Free();
    }
  }
}

메인.cpp:

#include "SharedMemory.h"
typedef void (*VOID_FUNC_PTR)();

void execute_charp_code() {
  ICLRRuntimeHost *pointer_CLR_host = NULL;
  HRESULT cor_bind_hr =
      CorBindToRuntimeEx(NULL, L"wks", 0, CLSID_CLRRuntimeHost,
                         IID_ICLRRuntimeHost, (PVOID *)&pointer_CLR_host);

  HRESULT process_start_hr = pointer_CLR_host->Start();

  DWORD retVal;
  HRESULT process_execute_hr = pointer_CLR_host->ExecuteInDefaultAppDomain(
      szPathToAssembly, L"shared_memory_csharp.command_line_interface",
      L"prog_point_entry", L"",
      &retVal  // returns `1` for successful execution | returns `0` for failed
               // execution
  );

  if (process_execute_hr == S_OK && retVal == 1) {
    ULONGLONG new_shared_memory_value = 0;
    BOOL bool_shMem_value = get_sharedMemory(&new_shared_memory_value);
    if (bool_shMem_value) {
      VOID_FUNC_PTR function_csharp = (VOID_FUNC_PTR)new_shared_memory_value;
      function_csharp();
    }
  }
}

출력:

Hello from C# primary_method!

IPC - 프로세스 간 통신을 사용하여 C++에서 C# 코드 호출

프로세스 간 통신의 세계에는 다음을 포함하여 이 작업을 수행하는 많은 방법이 있습니다. 명명된 파이프, RPC, 공유 메모리 등. 간단히 말해서 IPC는 프로세스 내에서 C++ 프로그램을 실행하여 C# 코드를 활성화합니다.

IPC의 프로세스는 서로 통신하고 서로의 메서드를 호출하거나 서로의 데이터를 사용할 수 있습니다. 실행하는 동안 각각 다른 기술의 코드를 보유하는 두 개의 프로세스를 생성합니다.

C# 및 C++의 경우 두 개의 프로세스를 생성하고 파일 공유 가능한 연결을 생성합니다. 또한 로컬 시스템의 소켓을 사용하여 명명된 파이프를 사용하는 IPC용 프로세스를 연결하거나 프로세스 간에 메시지를 주고받는 데 사용할 수 있는 스트림을 통해 파이프 서버와 하나 이상의 파이프 클라이언트 간에 이중 통신을 만들 수 있습니다.

명명된 파이프에는 FIFO(선입선출) 동작이 있으며 다음 예제에서 두 가지를 찾을 수 있습니다.

using System;       // essential
using System.Text;  // essential

namespace csharp_namedpipes {
  class namedpipe_server {
    static void Main(string[] args) {
      NamedPipeServer pipe_server_one = new NamedPipeServer(@"\\.\pipe\myNamedPipe1", 0);
      NamedPipeServer pipe_server_two = new NamedPipeServer(@"\\.\pipe\myNamedPipe2", 1);

      pipe_server_one.Start();
      pipe_server_two.Start();

      string _message = "Start";
      do {
        Console.WriteLine("Enter the message: ");
        _message = Console.ReadLine();
        pipe_server_two.SendMessage(_message, pipe_server_two.clientse);
      } while (_message != "Quit");

      pipe_server_one.StopServer();
      pipe_server_two.StopServer();
    }
  }
}
void execute_csharp_code() {
  LPTSTR lpsz_pipe_name1 = TEXT("\\\\.\\pipe\\myNamedPipe1");
  LPTSTR lpsz_pipe_name2 = TEXT("\\\\.\\pipe\\myNamedPipe2");
  do {
    printf("Enter your message: ");
    scanf("%s", buf);
    if (strcmp(buf, "Quit") == 0)
      Write_St = FALSE;
    else {
      WriteFile(hPipe1, buf, dwBytesToWrite, &cbWritten, NULL);
      memset(buf, 0xCC, 100);
    }
  } while (Write_St);
}

출력:

[C# app]

Enter your message: this is a test
C++ App: Received 17 bytes: test sending back

[C++ app]

Enter the message:
C# App: Received 5 Bytes: this
C# App: Received 2 bytes: a
test sending back
Enter the message:

C++에서 C# 코드를 호출하도록 HTTP 서버 호스팅

curlpp를 사용하면 C++ 프로그래머가 HTTP 서버를 호스팅하고 REST 스타일 API와 같은 HTTP 동사를 통해 호출하여 HTTP를 요청할 수 있습니다. C++에서 curlpp는 자연스럽고 URL 콘텐츠를 가져오고 C# 코드에 액세스하는 데 도움이 됩니다.

반면에 POCO는 HTTP 서버를 호스트하는 또 다른 방법이며 curlpp와 달리 풍부한 문서가 있고 지원 HTTP를 유지 관리합니다. POCO는 무료이며 오픈 소스이므로 초보자에게 훌륭한 시작이 될 수 있으며 다음 예제에서는 이러한 접근 방식을 구현하는 방법을 배웁니다.

#include <string.h>
#include <windows.h>
#include <winsock2.h>

#include <iostream>
#include <locale>
#include <sstream>
#include <vector>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

int main(void) {
  WSADATA data_wsa;
  SOCKET server_socket;
  SOCKADDR_IN server_socket_add;
  int count_line = 0;
  int count_row = 0;
  struct hostent *server_host;
  locale host_local;
  char var_buffer[10000];
  int i = 0;
  int data_length_new;
  string get_website_HTML;

  // website URL
  string website_URL = "www.google.com";

  // HTTP GET
  string get_website_HTTP = "GET / HTTP/1.1\r\nHost: " + website_URL +
                            "\r\nConnection: close\r\n\r\n";

  if (WSAStartup(MAKEWORD(2, 2), &data_wsa) != 0) {
    cout << "WSAStartup failed.\n";
    system("pause");
    // return 1;
  }

  server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  server_host = gethostbyname(website_URL.c_str());

  server_socket_add.sin_port = htons(80);
  server_socket_add.sin_family = AF_INET;
  server_socket_add.sin_addr.s_addr = *((unsigned long *)server_host->h_addr);

  if (connect(server_socket, (SOCKADDR *)(&server_socket_add),
              sizeof(server_socket_add)) != 0) {
    cout << "Could not connect";
    system("pause");
    // return 1;
  }

  // send GET / HTTP
  send(server_socket, get_website_HTTP.c_str(),
       strlen(get_website_HTTP.c_str()), 0);

  // receive HTML
  while ((data_length_new = recv(server_socket, var_buffer, 10000, 0)) > 0) {
    int x = 0;
    while (var_buffer[x] >= 32 || var_buffer[x] == '\n' ||
           var_buffer[x] == '\r') {
      get_website_HTML += var_buffer[x];
      x += 1;
    }
  }

  closesocket(server_socket);
  WSACleanup();

  // Display HTML source
  cout << get_website_HTML;
  cout << "source displayed!" << endl;

  // pause
  cout << "\n\nPress ANY key to close.\n\n";
  cin.ignore();
  cin.get();

  return 0;
}

출력:

source displayed!
Press ANY key to close.

.NET 언어의 특징 중 하나는 CLR이 가비지 수집, 개체 수명 제어, 디버깅 기능 향상 등을 수행하여 프로그램의 메모리 및 리소스 관리를 담당하는 관리 코드를 사용한다는 것입니다.

런타임 시스템은 C++와 같은 비관리 코드에서 프로그램이 사용하는 메모리 및 리소스에 대해 거의 알지 못하며 최소한의 서비스를 제공할 수 있습니다. C++로 C# 코드를 호출할 때 어려운 부분은 관리 코드와 비관리 코드 사이의 경계를 넘어 데이터를 이동하는 방법을 아는 것입니다. 이 프로세스를 마샬링이라고 합니다.

Syed Hassan Sabeeh Kazmi avatar Syed Hassan Sabeeh Kazmi avatar

Hassan is a Software Engineer with a well-developed set of programming skills. He uses his knowledge and writing capabilities to produce interesting-to-read technical articles.

GitHub