C++에서 C# 코드 호출
- C++/Cli를 중간 DLL로 사용하여 C++에서 C# 코드 호출
-
Reverse P/Invoke
기술을 사용하여 C++에서 C# 코드 호출 - COM 시스템을 사용하여 C++에서 C# 코드 호출
-
ICLRRuntimeHost::ExecuteInDefaultAppDomain()
CLR 호스팅을 사용하여 C++에서 C# 코드 호출 - IPC - 프로세스 간 통신을 사용하여 C++에서 C# 코드 호출
- C++에서 C# 코드를 호출하도록 HTTP 서버 호스팅
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#에서 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에 노출되는 모든 관리 유형, 속성, 이벤트, 메서드 및 필드는 공용이어야 하고, 관리 코드 내의 사용자 지정 특성은 구성 요소의 상호 운용성을 향상할 수 있어야 하며, 개발자는 참조 및 배포와 관련된 단계를 요약해야 합니다. 어셈블리.
또한 어셈블리를 등록하고, .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# 코드를 호출할 때 어려운 부분은 관리 코드와 비관리 코드 사이의 경계를 넘어 데이터를 이동하는 방법을 아는 것입니다. 이 프로세스를 마샬링
이라고 합니다.
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