C#调用C++

使用费托管DLL函数

平台调用时一项服务,使托管代码能够调用动态链接库(DLL)中实现的非托管函数,例如Windows API中的非托管函数。此服务定位并调用导出的函数,并根据需要跨交互操作边界封送其自变量(整数、字符串、数组、结构等),

平台调用调用非托管函数时,将执行以下操作序列:

  1. 定位包含函数的 DLL。
  2. 将 DLL 加载到内存中。
  3. 在内存中定位函数的地址并将其参数推送到堆栈上,从而根据需要封送数据
  4. 将控制转移到非托管函数。

平台调用将向托管调用方引发托管函数生成的异常

.NET类库对应

标识DLL中的函数

DLL函数的标识由以下元素组成:

  • 函数名称或序号
  • 可以找到实现的DLL文件的名称

例如,指定User32.dll中的MessageBox函数可标识函数(MessageBox)及其位置(User32.dll,User32或user32)。Windows API可以包含每个处理字符或字符串的函数的两个版本:1字节字符的ANSI版本和2字节字符的Unicode版本,未指定时,由CharSet字段表示的字符集默认为ANSI。

MessageBoxA是MessageBox函数的ANSI入口点;MessageBoxW是Unicode版本??赏ü诵卸嘀置钚泄ぞ吡谐鎏囟―LL(例如user32.dll)的函数名称,例如使用dumpbin /exports user32.dll 或 link /dump /exports user32.dll来获取函数名称。Linux下使用objdump命令
可以将非托管函数重命名为代码内的任意名称,只要将新名称映射到DLL的原始入口点。

创建用于容纳DLL函数的类

将常用的DLL函数包装在托管类中,这是封装平台功能的一种有效方式。在一个类中,为每个要调用的DLL函数定义静态方法。定义中可以包含附加信息,例如传递方法参数使用的字符集和调用约定;如果省略这些信息,则选择默认设置。

在托管代码中创建原型

using System;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DLLImport("user32.dll")]
    internal static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}

包装之后,就可按照调用任何其他类上的静态方法的方式来调用该类上的方法。平台自动处理基础导出函数

为平台调用设计托管类时,应考虑类和DLL函数之间的关系。例如:

  • 在现有类内声明DLL函数
  • 分别为每个DLL函数创建一个类,使函数相互独立,易于查找
  • 为一组相关DLL创建一个类,形成逻辑分组并减少开销

调用DLL函数

尽管调用非托管DLL函数与调用其他托管代码几乎完全相同,但又一些差异会使DLL函数一开始令人感到迷惑。
从平台调用返回的结构必须是在托管代码和非托管代码中表示形式相同的数据类型。这些类型成为blittable类型,因为它们不需要转换。

若要调用返回类型为non-blittable结构的函数,可定义与non-blittable类型大小相同的blittable帮助程序类型,并在函数返回后转换数据。

non-blittable类型和blittable类型

大多数数据类型在托管和非托管内存中具有共同的表示形式,并且不需要互操作封送处理程序进行特殊处理。这种类型称为blittable类型,因为它们在托管和非托管代码之间传递时不需要进行转换。
在非托管环境中,某些托管数据类型要求具有不同的表示形式。必须将这些non-blittable数据类型转换为可以封送的形式。

传递结构

许多未托管代码的函数希望你以函数的形式传递结构成员,或托管代码中定义的类的成员。使用平台调用将结构或类传递给非托管代码时,必须提供其他信息以保留原始布局和对齐方式。

回调函数

回调函数是托管应用程序中的代码,可帮助非托管DLL函数完成一项任务。对回调函数的调用间接从托管应用程序中进行传递、经过DLL函数,再回到托管实现。一些通过平台调用的DLL函数需要托管代码中的回调函数才能正常运行。
若要从托管代码中调用大部分DLL函数,则可以创建函数的托管定义,然后再调用它。
使用需要回调函数的DLL函数还有一些其他步骤。首先,必须通过查看函数的文档来确定该函数是否需要回调。然后,需要在托管应用程序中创建回调函数。最后,调用DLL函数,将指针作为一个参数传递给回调函数。

.Net Framework Application.Call passes a pointer to the callback function -> DLL function -> .Net Framework Application.Implementation of the callback function

回调函数非常适合用于需要重复执行一项任务的情况。另一个常见用法是与枚举函数配合使用,如Windows API中的"EnumFontFamilies","EnumPrinters"和"EnumWindows"。 EnumWindows函数通过计算机上所有现有的窗口进行枚举,调用每个窗口上的回调函数以执行任务。

如何:实现回调函数

下面的过程和示例演示使用平台调用的托管应用程序如何在本地计算机上打印每个窗口的句柄值。 具体而言,过程和示例使用"EnumWindows"函数来逐句通过窗口列表,使用托管回调函数(Callback)来打印窗口句柄的值

  1. 在进一步实现之前,请查看"EnumWindows"函数的签名,"EnumWindows"具有以下签名:
BOOL EnumWindows(wndenumproc lpEnumFunc, LPARAM lParam)

此函数需要回调的线索之一是存在"lpEnumFunc"自变量。经常可以看到在采用指向回调函数的指针的参数名称中"lp"(长指针)前缀与"Func"后缀结合在一起。

  1. 创建托管回调函数,此示例声明一个名为CallBack的委托类型,该类型采用两个自变量("hwnd"和"lparam") ,第一个参数是窗口的句柄;第二个参数是应用程序定义的。在此版本中,这两个自变量都必须是整数?;氐骱ǔ7祷胤橇阒道粗甘境晒?,返回零值来指示失败。此示例将返回值显式设置为"true"以继续进行枚举。

  2. 创建一个委托,并将其作为自变量传递到"EnumWindows"函数。平台调用自动将该委托转换为常见的回调格式。

  3. 确保在回调函数完成其工作之前,垃圾回收器不会回收委托。当将委托作为参数传递,或传递作为字段包括到结构中的委托时,在调用期间不会对其进行回收。因此,正如下面的枚举示例一样,调用返回并不再需要托管调用方执行任何其他操作之前,回调函数完成其工作。

using System;  
using System.Runtime.InteropServices;  
  
public delegate bool CallBack(int hwnd, int lParam);  
  
public class EnumReportApp  
{  
    [DllImport("user32")]  
    public static extern int EnumWindows(CallBack x, int y);   
  
    public static void Main()   
    {  
        CallBack myCallBack = new CallBack(EnumReportApp.Report);  
        EnumWindows(myCallBack, 0);  
    }  
  
    public static bool Report(int hwnd, int lParam)  
    {   
        Console.Write("Window handle is ");  
        Console.WriteLine(hwnd);  
        return true;  
    }  
}  

用平台调用封送数据

若要调用从非托管库中导出的函数,.NET Framework 应用程序需要托管代码中表示非托管函数的函数原型。 若要创建使平台调用能正确封送数据的原型,必须执行以下操作:
将 DllImportAttribute 特性应用于托管代码中的静态函数或方法。
用托管数据类型替换非托管数据类型。
通过应用具有可选字段的特性以及用托管数据类型替换非托管数据类型,可用附有非托管函数的文档来构造等效的托管原型。 有关如何应用 DllImportAttribute 的说明,请参阅使用非托管 DLL 函数。

平台调用 (P/Invoke)

using System;
using System.Runtime.InteropServices;

namespace PInvokeSamples
{
    public static class Program
    {
        // Import the libc shared library and define the method
        // corresponding to the native function.
        [DllImport("libc.so.6")]
        private static extern int getpid();

        public static void Main(string[] args)
        {
            // Invoke the function and get the process ID.
            int pid = getpid();
            Console.WriteLine(pid);
        }
    }
}

DllImportAttribute类

Building a cross-platform C++ library to call from .NET Core

Part1

Part2

Interop.manual.Unix.cs

https://github.com/dotnet/corefx/src/System.Console/src/Interop/Interop.manual.Unix.cs

mono - Interop with Native Libraries

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容