Rufen Sie C#-Code aus C++ auf

Syed Hassan Sabeeh Kazmi 12 Oktober 2023
  1. Verwenden Sie C++/Cli als Zwischen-DLL, um C#-Code aus C++ aufzurufen
  2. Verwenden Sie die Reverse P/Invoke-Technologie, um C#-Code aus C++ aufzurufen
  3. Verwenden Sie das COM-System zum Aufrufen von C#-Code aus C++
  4. Verwenden Sie das CLR-Hosting ICLRRuntimeHost::ExecuteInDefaultAppDomain(), um C#-Code aus C++ aufzurufen
  5. Verwenden Sie IPC – Interprozesskommunikation zum Aufrufen von C#-Code aus C++
  6. Hosten Sie einen HTTP-Server zum Aufrufen von C#-Code aus C++
Rufen Sie C#-Code aus C++ auf

C++ bietet mehrere Möglichkeiten zum Aufrufen von C#-Funktionen, um Programmierern eine perfekte plattformübergreifende Lösung zu bieten. In diesem Tutorial lernen Sie jeden möglichen Ansatz zum Aufrufen von C#-Code aus C++ kennen.

Sie können C++/CLI als Zwischen-DLL verwenden, P/Invoke umkehren, COM, CLR-Hosting, Interprozesskommunikation (IPC) verwenden oder einen HTTP-Server hosten und über HTTP-Verben aufrufen, um C#-Code/Funktionen von C++ aufzurufen. CLR-Hosting ist eine großartige Möglichkeit, dies zu tun, aber es beschränkt den Programmierer darauf, nur Methoden aufzurufen, die das Format int method(string arg) haben.

Plattformaufruf, C++-Interop, COM-Interop und eingebettete Interop-Typen sind einige der Mechanismen zum Erreichen von Interoperabilität. Programmierer müssen anerkennen, dass C#/C++-Interop-Code ziemlich schwierig zu debuggen sein kann, insbesondere über die Grenze zwischen nicht verwaltetem/verwaltetem Code.

Verwenden Sie C++/Cli als Zwischen-DLL, um C#-Code aus C++ aufzurufen

Im Allgemeinen können Sie entweder einen Delegaten als Funktionszeiger marshallen und an die nicht verwaltete Seite übergeben, was erfordert, dass die verwaltete Seite die Interaktion beginnt, oder C++/CLI als vermittelnde Schicht verwenden, was eine bessere Möglichkeit darstellt, beim Aufrufen von C# eine optimale Leistung zu erzielen. Code aus C++.

Heterogenität ist die Regel, nicht die Ausnahme, wenn es um die Softwareentwicklung in einem professionellen Umfeld geht, da Sie mit Systemen interagieren müssen, die mit anderen Technologien entwickelt wurden, und natives C++ das Abrufen von Daten mithilfe der objektorientierten API eines anderen Systems erfordern kann, in dem es entworfen wurde .NET mit C#.

Die C++/CLI-Plattform verfügt über die einzigartige Fähigkeit, verwalteten (C#) Code und nativen (C++) Code an einem Ort zu mischen und ist ein ideales Werkzeug, um mithilfe einfacher Wrapper Brücken zwischen diesen beiden Sprachen zu bauen.

Es ist ein unkomplizierter Ansatz, der ein langwieriger und fehleranfälliger Prozess bleibt, außer dass die Komplexität auftreten kann, wenn Installationscode für die Konvertierung in und von verwalteten Typen erforderlich ist.

Die C#-Bibliothek:

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

Der C++/CLI-Wrapper (Header-Datei):

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++-Definitionsdatei:

#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; }

Die native C++-Anwendung:

#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;
}

Ausgang:

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

Verwenden Sie die Reverse P/Invoke-Technologie, um C#-Code aus C++ aufzurufen

Es ermöglicht C++-Programmierern den Zugriff auf Strukturen, Rückrufe und Funktionen in verwalteten Bibliotheken aus Ihrem nicht verwalteten Code oder umgekehrt. In C# enthalten die Namespaces System und System.Runtime.InteropServices die P/Invoke-APIs und ermöglichen Ihnen die Kommunikation mit den nativen Komponenten.

Sie können beides ausführen, indem Sie entweder C#- oder C++-Code schreiben, und die Laufzeit ermöglicht, dass diese Kommunikation in beide Richtungen fließt. Es ermöglicht Ihnen, von nativen Funktionen in verwalteten Code zurückzurufen, indem Sie Funktionszeiger (Delegat – das, was Funktionszeigern am nächsten kommt) verwenden, um Rückrufe von nativem Code in verwalteten Code zu ermöglichen.

In dieser Methode definieren Sie einen Delegaten für einen Live-Callback, der mit der Signatur übereinstimmt, und übergeben diesen an die externe Methode, und die Laufzeit kümmert sich um alles. Es ist wichtig, die Signaturen der nicht verwalteten Funktionen zu überprüfen, bevor Sie den C++-Code ausführen, der C#-Funktionen enthält. zum Beispiel die BOOL EnumWindows (WNDENUMPROC lpEnumFunc, LPARAM lParam); ist ein Funktionsaufruf zum Auflisten aller Fenster und des BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM lParam); ist eine Callback-Signatur.

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);
  }
}

Ausgang:

Hello World!

Verwenden Sie das COM-System zum Aufrufen von C#-Code aus C++

Das Component Object Model – COM (als plattformunabhängiges, objektorientiertes und verteiltes System zum Erstellen von interagierbaren binären Softwarekomponenten) kann dabei helfen, die C#-Funktion aufzurufen, wenn .NET-Komponenten dafür verfügbar gemacht werden. Ein Programmierer kann COM verwenden, indem er einen .NET-Typ schreibt und diesen Typ aus dem nicht verwalteten Code nutzt.

Es gibt einige Voraussetzungen, die bekanntermaßen wichtig sind, bevor Sie COM verwenden, und dazu gehören: Alle verwalteten Typen, Eigenschaften, Ereignisse, Methoden und Felder, die für COM verfügbar gemacht werden, müssen öffentlich sein, benutzerdefinierte Attribute in verwaltetem Code müssen in der Lage sein, die Interoperabilität einer Komponente zu verbessern, und der Entwickler muss die Schritte zusammenfassen, die beim Verweisen und Bereitstellen erforderlich sind die Versammlungen.

Registrieren Sie außerdem Assemblys, verweisen Sie auf .NET-Typen und rufen Sie ein .NET-Objekt mit COM auf, um einen verwalteten Typ von COM zu verwenden, sowie eine Assembly mit starkem Namen, die im globalen Assemblycache installiert werden kann und eine Signatur von ihrem Herausgeber erfordert .

Das folgende Beispiel demonstriert die Zusammenarbeit eines COM-Clients und eines .NET-Servers, der Hypothekenberechnungen durchführt. Der Client erstellt und ruft eine Instanz einer verwalteten Klasse auf und übergibt vier Argumente an die Instanz, um Berechnungen anzuzeigen.

COM-Client:

// 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-Server:

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

Ausgang:

[load] sample.snk

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

Verwenden Sie das CLR-Hosting ICLRRuntimeHost::ExecuteInDefaultAppDomain(), um C#-Code aus C++ aufzurufen

Die Methode ICLRRuntimeHost::ExecuteInDefaultAppDomain ruft die angegebene Methode des angegebenen Typs in der angegebenen verwalteten Assembly auf. Seine Syntax ist so etwas wie HRESULT ExecuteInDefaultAppDomain ([in] LPCWSTR pwzAssemblyPath, [in] LPCWSTR pwzTypeName, [in] LPCWSTR pwzMethodName, [in] LPCWSTR pwzArgument, [out] DWORD *pReturnValue ); wobei jeder Parameter den Pfad, den Typ, den Namen, den Zeichenfolgenparameter zum Übergeben der Methode und den von der aufgerufenen Methode zurückgegebenen ganzzahligen Wert darstellt.

Darüber hinaus muss die aufgerufene Methode die Signatur static int pwzMethodName (String pwzArgument) aufweisen, wobei pwzArgument einen Zeichenfolgenwert darstellt, der als Parameter an diese Aufrufmethode übergeben wird. Im Allgemeinen können Sie mit dem CLR-Hosting jede CLR-Assembly in ein natives C++-Programm einbetten, und das ist der Grund, warum native Programme wie SQL Server .NET-Codeerweiterungen wie SQL CLR unterstützen und mit CorBindToRuntimeEx() in die massiven Prozesse geladen werden können. oder CLRCreateInstance() hängt von der .NET-Framework-Version 2.0 bzw. 4 ab.

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();
    }
  }
}

Ausgang:

Hello from C# primary_method!

Verwenden Sie IPC – Interprozesskommunikation zum Aufrufen von C#-Code aus C++

Die Welt der Interprozesskommunikation hält viele Methoden bereit, um diese Aufgabe zu erledigen, einschließlich; Named Pipes, RPC, Shared Memory usw. Kurz gesagt führt der IPC Ihr C++-Programm in einem Prozess aus, um seinen C#-Code zu aktivieren.

Die Prozesse von IPC können miteinander kommunizieren und die Methoden der anderen aufrufen oder die Daten der anderen verwenden. Während der Ausführung werden zwei Prozesse erstellt, die jeweils Code aus unterschiedlichen Technologien enthalten.

Im Fall von C# und C++ werden zwei Prozesse erstellt und eine Dateifreigabeverbindung erstellt. Darüber hinaus können Sie Sockets auf dem lokalen Computer verwenden, um Prozesse für IPC mit benannten Pipes zu verbinden oder eine Duplexkommunikation zwischen einem Pipe-Server und einem oder mehreren Pipe-Clients mit einem Stream herzustellen, der zum Senden/Empfangen von Nachrichten zwischen Prozessen verwendet werden kann.

Die benannten Pipes haben ein FIFO-Verhalten (First in – First out), und im folgenden Beispiel finden Sie zwei.

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);
}

Ausgang:

[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:

Hosten Sie einen HTTP-Server zum Aufrufen von C#-Code aus C++

Das curlpp ermöglicht es C++-Programmierern, HTTP anzufordern, indem sie einen HTTP-Server hosten und über HTTP-Verben wie eine API im REST-Stil aufrufen. In C++ ist curlpp selbstverständlich und hilft dabei, den Inhalt einer URL abzurufen und auf den C#-Code zuzugreifen.

Auf der anderen Seite ist POCO eine weitere Möglichkeit, einen HTTP-Server zu hosten, und im Gegensatz zu curlpp verfügt es über eine umfangreiche Dokumentation und gepflegte HTTPs. Da das POCO kostenlos und Open Source ist, kann es ein guter Start für Anfänger sein, und in den folgenden Beispielen erfahren Sie, wie Sie diese Ansätze implementieren können.

#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;
}

Ausgang:

source displayed!
Press ANY key to close.

Ein Merkmal der .NET-Sprachen ist, dass sie den verwalteten Code verwenden, wobei CLR die Verantwortung für die Verwaltung des Speichers und der Ressourcen des Programms übernimmt, indem es Garbage Collection durchführt, die Lebensdauer von Objekten steuert, die Debugging-Funktionalität verbessert und vieles mehr.

Die Laufzeitsysteme wissen wenig über den Speicher und die Ressourcen, die das Programm in nicht verwaltetem Code wie dem von C++ verwendet, und können nur minimale Dienste bereitstellen. Der schwierige Teil beim Aufrufen von C#-Code mit C++ besteht darin, zu wissen, wie Daten über die Grenze zwischen verwaltetem und nicht verwaltetem Code verschoben werden, da dieser Prozess als marshaling bezeichnet wird.

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