C# 代码有以下两种可以直接调用非托管代码的方法:

  • 直接调用从 DLL 导出的函数。
  • 调用 COM 对象上的接口方法。

对于这两种技术,都必须向 C# 编译器提供非托管函数的声明,并且还可能需要向 C# 编译器提供如何封送与非托管代码之间传递的参数和返回值的说明。

  • 直接从 C# 调用 DLL 导出
  • 默认封送处理和为非托管方法的参数指定自定义封送处理
  • 为用户定义的结构指定自定义封送处理
  • 注册回调方法

直接从 C# 调用 DLL 导出

要声明一个方法使其具有来自 DLL 导出的实现,请执行下列操作:

  • 使用 C# 关键字 static 和 extern 声明方法。
  • 将 DllImport 属性附加到该方法。DllImport 属性允许您指定包含该方法的 DLL 的名称。通常的做法是用与导出的方法相同的名称命名 C# 方法,但也可以对 C# 方法使用不同的名称。
  • 还可以为方法的参数和返回值指定自定义封送处理信息,这将重写 .NET Framework 的默认封送处理。

示例 1

本示例显示如何使用 DllImport 属性通过调用 msvcrt.dll 中的 puts 输出消息。

  1. // PInvokeTest.cs
  2. using System;
  3. using System.Runtime.InteropServices;
  4.  
  5. class PlatformInvokeTest
  6. {
  7. [DllImport("msvcrt.dll")]
  8. public static extern int puts(string c);
  9. [DllImport("msvcrt.dll")]
  10. internal static extern int _flushall();
  11.  
  12. public static void Main()
  13. {
  14. puts("Test");
  15. _flushall();
  16. }
  17. }

代码讨论

前面的示例显示了声明在非托管 DLL 中实现的 C# 方法的最低要求。PlatformInvokeTest.puts 方法用 static 和 extern 修饰符声明并且具有 DllImport 属性,该属性使用默认名称 puts 通知编译器此实现来自 msvcrt.dll。若要对 C# 方法使用不同的名称(如 putstring),则必须在 DllImport 属性中使用 EntryPoint 选项,如下所示:

  1. [DllImport("msvcrt.dll", EntryPoint="puts")]

有关 DllImport 属性的语法的更多信息,请参见 DllImportAttribute 类。

默认封送处理和为非托管方法的参数指定自定义封送处理

当从 C# 代码中调用非托管函数时,公共语言运行库必须封送参数和返回值。

对于每个 .NET Framework 类型均有一个默认非托管类型,公共语言运行库将使用此非托管类型在托管到非托管的函数调用中封送数据。例如,C# 字符串值的默认封送处理是封送为 LPTSTR(指向 TCHAR 字符缓冲区的指针)类型。可以在非托管函数的 C# 声明中使用 MarshalAs 属性重写默认封送处理。

示例 2

本示例使用 DllImport 属性输出一个字符串。它还显示如何通过使用 MarshalAs 属性重写函数参数的默认封送处理。

  1. // Marshal.cs
  2. using System;
  3. using System.Runtime.InteropServices;
  4.  
  5. class PlatformInvokeTest
  6. {
  7. [DllImport("msvcrt.dll")]
  8. public static extern int puts(
  9. [MarshalAs(UnmanagedType.LPStr)]
  10. string m);
  11. [DllImport("msvcrt.dll")]
  12. internal static extern int _flushall();
  13.  
  14. public static void Main()
  15. {
  16. puts("Hello World!");
  17. _flushall();
  18. }
  19. }

代码讨论

在前面的示例中,puts 函数的参数的默认封送处理已从默认值 LPTSTR 重写为 LPSTR。

MarshalAs 属性可以放置在方法参数、方法返回值以及结构和类的字段上。若要设置方法返回值的封送处理,请将 MarshalAs 属性与返回属性位置重写一起放置在方法上的属性块中。例如,若要显式设置 puts方法返回值的封送处理:

  1. ...
  2. [DllImport("msvcrt.dll")]
  3. [return : MarshalAs(UnmanagedType.I4)]
  4. public static extern int puts(
  5. ...

有关 MarshalAs 属性的语法的更多信息,请参见 MarshalAsAttribute 类。

注意   In 和 Out 属性可用于批注非托管方法的参数。它们与 MIDL 源文件中的 in 和 out 修饰符的工作方式类似。请注意,Out 属性与 C# 参数修饰符 out 不同。有关 In 和 Out 属性的更多信息,请参见 InAttribute 类和 OutAttribute 类。

为用户定义的结构指定自定义封送处理

可以为传递到非托管函数或从非托管函数返回的结构和类的字段指定自定义封送处理属性。通过向结构或类的字段中添加 MarshalAs 属性可以做到这一点。还必须使用 StructLayout 属性设置结构的布局,还可以控制字符串成员的默认封送处理,并设置默认封装大小。

示例 3

本示例说明如何为结构指定自定义封送处理属性。

请考虑下面的 C 结构:

  1. typedef struct tagLOGFONT
  2. {
  3. LONG lfHeight;
  4. LONG lfWidth;
  5. LONG lfEscapement;
  6. LONG lfOrientation;
  7. LONG lfWeight;
  8. BYTE lfItalic;
  9. BYTE lfUnderline;
  10. BYTE lfStrikeOut;
  11. BYTE lfCharSet;
  12. BYTE lfOutPrecision;
  13. BYTE lfClipPrecision;
  14. BYTE lfQuality;
  15. BYTE lfPitchAndFamily;
  16. TCHAR lfFaceName[LF_FACESIZE];
  17. } LOGFONT;

在 C# 中,可以使用 StructLayout 和 MarshalAs 属性描述前面的结构,如下所示:

  1. // logfont.cs
  2. // compile with: /target:module
  3. using System;
  4. using System.Runtime.InteropServices;
  5.  
  6. [StructLayout(LayoutKind.Sequential)]
  7. public class LOGFONT
  8. {
  9. public const int LF_FACESIZE = ;
  10. public int lfHeight;
  11. public int lfWidth;
  12. public int lfEscapement;
  13. public int lfOrientation;
  14. public int lfWeight;
  15. public byte lfItalic;
  16. public byte lfUnderline;
  17. public byte lfStrikeOut;
  18. public byte lfCharSet;
  19. public byte lfOutPrecision;
  20. public byte lfClipPrecision;
  21. public byte lfQuality;
  22. public byte lfPitchAndFamily;
  23. [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
  24. public string lfFaceName;
  25. }

有关 StructLayout 属性的语法的更多信息,请参见 StructLayoutAttribute 类。

然后即可将该结构用在 C# 代码中,如下所示:

  1. // pinvoke.cs
  2. // compile with: /addmodule:logfont.netmodule
  3. using System;
  4. using System.Runtime.InteropServices;
  5.  
  6. class PlatformInvokeTest
  7. {
  8. [DllImport("gdi32.dll", CharSet=CharSet.Auto)]
  9. public static extern IntPtr CreateFontIndirect(
  10. [In, MarshalAs(UnmanagedType.LPStruct)]
  11. LOGFONT lplf // characteristics
  12. );
  13.  
  14. [DllImport("gdi32.dll")]
  15. public static extern bool DeleteObject(
  16. IntPtr handle
  17. );
  18.  
  19. public static void Main()
  20. {
  21. LOGFONT lf = new LOGFONT();
  22. lf.lfHeight = ;
  23. lf.lfFaceName = "Arial";
  24. IntPtr handle = CreateFontIndirect(lf);
  25.  
  26. if (IntPtr.Zero == handle)
  27. {
  28. Console.WriteLine("Can't creates a logical font.");
  29. }
  30. else
  31. {
  32.  
  33. if (IntPtr.Size == )
  34. Console.WriteLine("{0:X}", handle.ToInt32());
  35. else
  36. Console.WriteLine("{0:X}", handle.ToInt64());
  37.  
  38. // Delete the logical font created.
  39. if (!DeleteObject(handle))
  40. Console.WriteLine("Can't delete the logical font");
  41. }
  42. }
  43. }

代码讨论

在前面的示例中,CreateFontIndirect 方法使用了一个 LOGFONT 类型的参数。MarshalAs 和 In 属性用于限定此参数。程序将由此方法返回的数值显示为十六进制大写字符串。

注册回调方法

若要注册调用非托管函数的托管回调,请用相同的参数列表声明一个委托并通过 PInvoke 传递它的一个实例。在非托管端,它将显示为一个函数指针。有关 PInvoke 和回调的更多信息,请参见平台调用详解

例如,考虑以下非托管函数 MyFunction,此函数要求 callback 作为其参数之一:

  1. typedef void (__stdcall *PFN_MYCALLBACK)();
  2. int __stdcall MyFunction(PFN_ MYCALLBACK callback);

若要从托管代码调用 MyFunction,请声明该委托,将 DllImport 附加到函数声明,并根据需要封送任何参数或返回值:

  1. public delegate void MyCallback();
  2. [DllImport("MYDLL.DLL")]
  3. public static extern void MyFunction(MyCallback callback);

同时,请确保委托实例的生存期覆盖非托管代码的生存期;否则,委托在经过垃圾回收后将不再可用。

C# 直接调用非托管代码的方法的更多相关文章

  1. c# 调用c++DLL方法及注意事项

    引用命名空间 using System.Runtime.InteropServices 调用方法: 一.静态加载 用DllImprot方式来加载c++DLL.如下格式: //对应c++方法 //voi ...

  2. C#如何直接调用非托管代码

    C#如何直接调用非托管代码,通常有2种方法: 1.  直接调用从 DLL 导出的函数. 2.  调用 COM 对象上的接口方法 我主要讨论从dll中导出函数,基本步骤如下: 1.使用 C# 关键字 s ...

  3. js调用php和php调用js的方法举例

    js调用php和php调用js的方法举例1 JS方式调用PHP文件并取得php中的值 举一个简单的例子来说明: 如在页面a.html中用下面这句调用: <script type="te ...

  4. JavaScript调用函数的方法

    摘要:这篇文章详细的介绍了Javascript中各种函数调用的方法及其原理,对于理解JavaScript的函数有很大的帮助! 一次又一次的,我发现,那些有bug的Javascript代码是由于没有真正 ...

  5. WPF中实例化Com组件,调用组件的方法时报System.Windows.Forms.AxHost+InvalidActiveXStateException的异常

    WPF中实例化Com组件,调用组件的方法时报System.Windows.Forms.AxHost+InvalidActiveXStateException的异常 在wpf中封装Com组件时,调用组件 ...

  6. Android NDK 初探,生成so文件以及调用so文件方法

    因为最近业务上涉及安全的问题 然后有一些加密解密的方法和key的存储问题 本来想存储到手机里面,但是网上说一般敏感信息不要存储到手机Sdcard上 而且我这个如果从网络建立通信获取的话,又太耗时,所以 ...

  7. Office word 2013中直接调用MathType的方法

    Office word 2013中直接调用MathType的方法 | 浏览:4403 | 更新:2014-02-20 14:45 | 标签: word 使用Office word 2013的用户肯定早 ...

  8. Lua 调用 Opencv 的方法

    Lua 调用 Opencv 的方法 最近想用 Lua 调用 Opencv 进行相关像素级操作,如:bitwise_and 或者 bitwise_or,从而完成图像 IoU 的计算. 那么,怎么用 Lu ...

  9. asp.net 手工调用 WS(Get)方法:

    asp.net 手工调用 WS(Get)方法: 通过手工HttpWebRequest,HttpWebResponse来模拟调用.核心代码:string strurl="http://loca ...

随机推荐

  1. 【jquery操作cookie】JQuery中$.cookie()方法的使用(同名cookie会覆盖)

    jquery.cookie.js插件: <script type="text/javascript" src="js/jquery-1.6.2.min.js&quo ...

  2. SyntaxError: Non-ASCII character '\xe5' in file index.py on line 6, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

    python入门,hhh 在慕课网上学习python入门,编写汉诺塔的递归调用时,代码正确.但是加上注释后编译不通过 报如下错误: SyntaxError: Non-ASCII character , ...

  3. Ubuntu14.04单机版kubernetes安装指导

    转:http://dockone.io/article/950 概述本文主要讲述如何在Ubuntu系统上安装kubernetes,网络上也有许多相关的文章,感觉都不是很清晰,这里我将自己的安装实践给大 ...

  4. verilog之四位全加器的编译及仿真(用开源免费的软件——iverilog+GTKWave)

    verilog之四位全加器的编译及仿真(用开源免费的软件——iverilog+GTKWave) 四位全加器的verilog的代码比比皆是,这里上一个比较简单的: /* 4位全加器全加器需要有输入输出, ...

  5. jsp学习之scriptlet的使用方法

    scriptlet的使用 jsp页面中分三种scriptlet: 第一种:<%  %>  可以在里面写java的代码.定义java变量以及书写java语句. 第二种:<%! %> ...

  6. SpringBoot 整合 Security5

    https://my.oschina.net/yunduansing/blog/2032475 https://blog.csdn.net/SWPU_Lipan/article/details/805 ...

  7. ssm开发系的统架构图

  8. Ubuntu 16.04安装有道词典

    以前用Ubuntu 14.04 的时候,直接下载有道词典官方deb安装包,就安装好了,现在换成Ubuntu 16.04因为有些依赖问题就无法安装成功.于是Google之,成功解决,也顺便熟悉了一下dp ...

  9. python操作word(改课文格式)【最终版】

    python操作word的一些方法,前面写了一些感悟,有点跑题,改了下题目,方便能搜索到.心急的可以直接拉到最后看代码,我都加了比较详细的注释. 从8.3号早上9点,到8.8号下午5点半下班,终于把这 ...

  10. 一款基于jQuery的图片分组切换焦点图插件

    这是一款基于jQuery的图片切换焦点图插件,这款jQuery焦点图插件的特点是图片可以分组切换,也就是说一次可以切换多张图片,相比其他焦点图插件,它能节省更多的空间,可以向用户展示更多的图片,非常实 ...