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++ 相互運用、COM 相互運用、および埋め込み相互運用の種類は、相互運用性を実現するためのメカニズムの一部です。 プログラマーは、C#/C++ 相互運用コードのデバッグが非常に困難になる可能性があることを認識する必要があります。

C++/Cli を中間 DLL として使用して、C++ から C# コードを呼び出す

一般に、デリゲートを関数ポインターとしてマーシャリングし、それをアンマネージド側に渡すことができます。これには、マネージド側が対話を開始する必要があります。または、C# の呼び出し中に最適なパフォーマンスを達成するためのより良い方法である仲介レイヤーとして C++/CLI を使用することができます。 C++ からのコード。

他の技術で開発されたシステムとやり取りする必要があり、ネイティブ C++ では、C++ で設計された別のシステムのオブジェクト指向 API を使用してデータを取得する必要があるため、専門的な環境でのソフトウェア開発に関しては、異質性は例外ではなく規則です。 .NET と C#。

C++/CLI プラットフォームは、マネージド (C#) コードとネイティブ (C++) コードを 1 か所に混在させる独自の機能を備えており、単純なラッパーを使用してこれら 2つの言語間のブリッジを構築するための理想的なツールとして機能します。

これは単純なアプローチであり、面倒でエラーが発生しやすいプロセスのままですが、マネージ型との間の変換に配管コードが必要な場合に複雑さが生じる可能性があります。

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# では、System および System.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 に公開されるすべてのマネージド型、プロパティ、イベント、メソッド、およびフィールドはパブリックである必要があり、マネージド コード内のカスタム属性はコンポーネントの相互運用性を強化できる必要があり、開発者は参照と展開に関連する手順を要約する必要があります。 アセンブリ。

さらに、COM を使用してアセンブリを登録し、.NET 型を参照し、.NET オブジェクトを呼び出して、COM からマネージド型を使用するだけでなく、グローバル アセンブリ キャッシュにインストールでき、その発行元からの署名を必要とする厳密な名前のアセンブリを使用します。 .

次の例は、住宅ローンの計算を実行する COM クライアントと .NET サーバーの相互運用を示しています。 クライアントはマネージド クラスのインスタンスを作成して呼び出し、4つの引数をインスタンスに渡して計算を表示します。

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 ホスティングを使用して任意の CLR アセンブリをネイティブ C++ プログラムに埋め込むことができます。これが、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();
    }
  }
}

main.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 からのプロセスは、相互に通信して、相互のメソッドを呼び出したり、相互のデータを使用したりできます。 実行中、2つのプロセスが作成され、それぞれが異なるテクノロジのコードを保持します。

C# と C++ の場合、2つのプロセスが作成され、ファイル共有可能な接続が作成されます。 さらに、ローカル マシン上のソケットを使用して、名前付きパイプを使用して IPC のプロセスを接続したり、パイプ サーバーと 1つ以上のパイプ クライアントとの間で、プロセス間のメッセージの送受信に使用できるストリームを使用して二重通信を行うことができます。

名前付きパイプには FIFO (先入れ先出し) 動作があり、次の例では 2つ見つかります。

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 とは異なり、豊富なドキュメントがあり、HTTPs のサポートを維持しています。 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 言語の特徴の 1つは、マネージ コードを使用することです。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