迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C++ >

从 C++ 调用 C# 代码

作者:迹忆客 最近更新:2023/03/31 浏览次数:

C++提供了多种调用C#函数的方式,为程序员提供了完美的跨平台解决方案。 本教程将教您从 C++ 调用 C# 代码的所有可能方法。

您可以将 C++/CLI 用作中间 DLL、反向 P/调用、使用 COM、CLR 托管、进程间通信 (IPC),或托管 HTTP 服务器并通过 HTTP 动词调用以从 C++ 调用 C# 代码/函数。 CLR 托管是一种很好的方法,但它限制了程序员只能调用具有 int method(string arg) 格式的方法。

平台调用、C++ 互操作、COM 互操作和嵌入式互操作类型是实现互操作性的一些机制。 程序员必须承认 C#/C++ 互操作代码可能很难调试,尤其是跨非托管/托管代码之间的边界。

使用 C++/Cli 作为中间 DLL 从 C++ 调用 C# 代码

通常,您可以将委托编组为函数指针并将其传递给非托管端,这需要托管端开始交互,或者使用 C++/CLI 作为中介层,这是在调用 C# 时实现最佳性能的更好方法 来自 C++ 的代码。

当涉及到专业环境中的软件开发时,异构性是规则,而不是例外,因为您需要与使用其他技术开发的系统进行交互,而原生 C++ 可能需要使用在其他系统中设计的面向对象的 API 来检索数据 .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

使用反向 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) 的函数调用; 是回调签名。

使用 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 服务器的互操作。 客户端将创建并调用托管类的一个实例,并将四个参数传递给该实例以显示计算结果。

客户端:

// ConLoan.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#import "..\LoanLib\_azythro_call.tlb" raw_interfaces_only
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 框架版本 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 "custom_made.h" // a user created/defined library | customizable according to your preference
#include "SharedMemory.h"

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 中的进程可以相互通信并调用彼此的方法或使用彼此的数据。 在执行过程中,它会创建两个进程,每个进程都持有来自不同技术的代码。

对于 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:

托管 HTTP 服务器以从 C++ 调用 C# 代码

curlpp 使 C++ 程序员能够通过托管 HTTP 服务器并通过 HTTP 动词调用(如 REST 风格的 API)来请求 HTTP。 在 C++ 中,curlpp 很自然,有助于获取 URL 的内容并访问 C# 代码。

另一方面,POCO 是另一种托管 HTTP 服务器的方式,与 curlpp 不同的是,它具有丰富的文档和维护的 HTTP 支持。 由于 POCO 是免费和开源的,它可以成为初学者的一个很好的开始,在下面的示例中,您将学习如何实现这些方法。

#include <windows.h>
#include <sstream>
#include <string.h>
#include <iostream>
#include <winsock2.h>
#include <vector>
#include <locale>

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# 代码的困难部分是知道如何跨托管代码和非托管代码之间的边界移动数据,因为此过程称为封送处理。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

在 C++ 中通过掷骰子生成随机值

发布时间:2023/04/09 浏览次数:169 分类:C++

本文解释了如何使用时间因子方法和模拟 C++ 中的掷骰子的任意数方法生成随机数。了解它是如何工作的以及它包含哪些缺点。提供了一个 C++ 程序来演示伪数生成器。

在 C++ 中使用模板的链表

发布时间:2023/04/09 浏览次数:158 分类:C++

本文解释了使用模板在 C++ 中创建链表所涉及的各个步骤。工作程序演示了一个链表,该链表使用模板来避免在创建新变量时声明数据类型的需要。

在 C++ 中添加定时延迟

发布时间:2023/04/09 浏览次数:142 分类:C++

本教程将为你提供有关在 C++ 程序中添加定时延迟的简要指南。这可以使用 C++ 库为我们提供的一些函数以多种方式完成。

在 C++ 中创建查找表

发布时间:2023/04/09 浏览次数:155 分类:C++

本文重点介绍如何创建查找表及其在不同场景中的用途。提供了三个代码示例以使理解更容易,并附有代码片段以详细了解代码。

如何在 C++ 中把字符串转换为小写

发布时间:2023/04/09 浏览次数:63 分类:C++

介绍了如何将 C++ std::string 转换为小写的方法。当我们在考虑 C++ 中的字符串转换方法时,首先要问自己的是我的输入字符串有什么样的编码

如何在 C++ 中确定一个字符串是否是数字

发布时间:2023/04/09 浏览次数:163 分类:C++

本文介绍了如何检查给定的 C++ 字符串是否是数字。在我们深入研究之前,需要注意的是,以下方法只与单字节字符串和十进制整数兼容。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便