• P/Invoke各种总结(十、C++调用C#代码的几种方法总结)


    方法一、使用 C++/CLI 创建一个桥接库

    返回基础数据类型

    1.首先我们新建 一个C#的类库工程 CSharpLib

     

    新建一个ExportClass类,增加一个GetID函数,如下:

    1 public class ExplortClass
    2     {
    3         public int GetID()
    4         {
    5             return 1024;
    6         }
    7     }

    2.新建一个CLR空工程CSBridge,这个库会作为中间桥接的库。将CSBridge工程的输出路径修改为CSharpLib工程的输出路径

    说明:如果没有看到CLR Empty,可以到Visual Studio的安装程序中钩选并安装(直接搜索cli)

     

     新建一个bridge.cpp。输入以下代码

     1 #include <Windows.h>
     2 #include<msclr/marshal_cppstd.h>
     3 
     4 //引用C# dll
     5 #using "./CSharpLib.dll"
     6 
     7 //引用命名空间
     8 using namespace msclr::interop;
     9 using namespace System;
    10 using namespace System::Runtime::InteropServices;
    11 using namespace CSharpLib;
    12 
    13 #define lib_export
    14 #ifdef lib_export
    15 #define cs_lib_api extern "C" __declspec(dllexport)
    16 #else
    17 #define cs_lib_api __declspec(dllimport)
    18 #endif
    19 
    20 typedef int(__stdcall* funGetId)();  //定义函数指针
    21 
    22 //导出函数 供C++调用
    23 //在这个函数里调用 C#的函数,做为中转层
    24 cs_lib_api int GetID()
    25 {
    26     CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();
    27     auto id = c->GetID();
    28     return id;
    29 }

    这样就拥有了一个桥接工程 。

    3. 新建一个C++控制台应用程序,输入以下代码测试。

    // CppInvoke.cpp : This file contains the 'main' function. Program execution begins and ends there.
    //
    
    #include <iostream>
    #include<Windows.h>
    
    typedef int(__stdcall* funGetId)();
    
    int main()
    {
        HMODULE hInstance = LoadLibrary(L"CSBridge.dll");
        if (hInstance)
        {
            funGetId getId = (funGetId)GetProcAddress(hInstance, "GetID");
    
            if (getId)
            {
                auto result = getId();
                std::cout << result << std::endl;
            }
        }
    }

    可以看到输出结果为:1024

    复杂一点的情况,返回一个结构体:

    在CSharpLib中增加一个结构体Computer:

    1     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    2     public struct Computer
    3     {
    4         public int cpuId; 
    5         public string cpuName;
    6         public int osVersion;
    7     }

    增加一个获取Computer的函数

    1   public Computer GetComputer()
    2         {
    3             Computer computer = new Computer();
    4             computer.cpuId = 100000000;
    5             computer.cpuName = "Intel";
    6             computer.osVersion = 11;
    7             return computer;
    8         }

     然后在CSBridge中增加一个用于和Computer交互的类型interop_Computer,这个类型是用于C++中调用时使用,使用C#中的Computer类型转换可以得到interop_Computer。

    1 struct interop_Computer
    2 {
    3     int cpuId;
    4     wchar_t* cpuName;
    5     int osVersion;
    6 };

     再定义一个函数指针和增加一个中转层函数

     1 typedef interop_Computer(__stdcall* funGetComputer)();
     2 
     3 cs_lib_api interop_Computer GetComputer()
     4 {
     5     CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();
     6     auto computer = c->GetComputer(); //调用C#中的函数
     7     System::IntPtr ptr = Marshal::AllocHGlobal(sizeof(interop_Computer));//需要提前分配空间
     8     System::Runtime::InteropServices::Marshal::StructureToPtr(computer, ptr, false);//将C#中的结构体拷贝到Intptr
     9     interop_Computer* rt = (interop_Computer*)(void*)(ptr.ToPointer());//将Intptr强制转换为interop_Computer
    10     return *rt;
    11 }

     然后在CppInvoke中添加测试代码

    HMODULE hInstance = LoadLibrary(L"CSBridge.dll");
        if (hInstance)
        {
          
            funGetComputer getComputer = (funGetComputer)GetProcAddress(hInstance, "GetComputer");
             
            if (getComputer)
            {
                auto computer = getComputer();
                std::wcout << computer.cpuId << "\t" << computer.cpuName << "\t" << computer.osVersion << std::endl;
            }
    
            FreeLibrary(hInstance);
        }

    输出结果为:

    这里还有一种情况,就 是需要 将C++中的参数传到C#中。

     这种情况有两种方法可以实现:

    1、将C++中的参数封送到C#中,转换方式和上面返回结构体的实现方式差不多。大概思路就是把C++结构体转换成IntPtr,再从IntPtr转换到C#中的结构体。

    2、将C#中的函数转换到C++中的函数再调用。这样就可以直接使用C++中的结构体。

    实现方法如下:

    在C#中增加一个函数PrintComputer,需要传入一个Computer结构体。然后再增加对应的委托和获取委托的函数

    1 public void PrintComputer(Computer computer)
    2         {
    3             Console.WriteLine(computer.cpuId);
    4             Console.WriteLine(computer.cpuName);
    5             Console.WriteLine(computer.osVersion);
    6         }
    1 public delegate void PrintComputerDelegate(Computer computer); //声明委托
    2 
    4 public PrintComputerDelegate GetComputerDelegate() => PrintComputer;  //定义返回委托的函数

     在CSBridge中定义一个函数指针,并增加一个导出函数

     1 typedef void(__stdcall* funPrintComputer)(interop_Computer computer);
     2 
     3 cs_lib_api void PrintComputer(interop_Computer computer)
     4 {
     5     CSharpLib::ExplortClass^ c = gcnew CSharpLib::ExplortClass();
     6     auto printDelegate = c->GetComputerDelegate();//获取委托 
     7     IntPtr ptr = Marshal::GetFunctionPointerForDelegate(printDelegate);//将委托转为IntPtr类型
     8     funPrintComputer funcPrint = (funPrintComputer)ptr.ToPointer();//将IntPtr转换为指针,再转换为funPrintComputer
     9     if (funcPrint)
    10     {
    11         funcPrint(computer);
    12     }
    13 }

     这样就可以在C++中的参数传递到C#中。CppInvoke中的调用 代码如下:

    1 funPrintComputer printComputer = (funPrintComputer)GetProcAddress(hInstance, "PrintComputer");
    2         interop_Computer testComputer;
    3         testComputer.cpuId = 18;
    4         testComputer.cpuName = _tcsdup(L"AMD");
    5         testComputer.osVersion = 7;
    6         if (printComputer)
    7         {
    8             printComputer(testComputer);
    9         }

     输出结果为:

    示例代码(需要Visual Studio 2022)

    方法二、将.NET组件导出为COM

    待完成

  • 相关阅读:
    爬虫大作业
    数据结构化与保存
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    综合练习:词频统计
    Hadoop综合大作业
    理解MapReduce
    熟悉HBase基本操作
    熟悉常用的HBase操作
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/16481443.html
Copyright © 2020-2023  润新知