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 输出消息。

// PInvokeTest.cs
using System;
using System.Runtime.InteropServices; class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall(); public static void Main()
{
puts("Test");
_flushall();
}
}

代码讨论

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

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

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

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

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

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

示例 2

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

// Marshal.cs
using System;
using System.Runtime.InteropServices; class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(
[MarshalAs(UnmanagedType.LPStr)]
string m);
[DllImport("msvcrt.dll")]
internal static extern int _flushall(); public static void Main()
{
puts("Hello World!");
_flushall();
}
}

代码讨论

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

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

...
[DllImport("msvcrt.dll")]
[return : MarshalAs(UnmanagedType.I4)]
public static extern int puts(
...

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

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

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

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

示例 3

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

请考虑下面的 C 结构:

typedef struct tagLOGFONT
{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;

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

// logfont.cs
// compile with: /target:module
using System;
using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)]
public class LOGFONT
{
public const int LF_FACESIZE = ;
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
public string lfFaceName;
}

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

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

// pinvoke.cs
// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices; class PlatformInvokeTest
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateFontIndirect(
[In, MarshalAs(UnmanagedType.LPStruct)]
LOGFONT lplf // characteristics
); [DllImport("gdi32.dll")]
public static extern bool DeleteObject(
IntPtr handle
); public static void Main()
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = ;
lf.lfFaceName = "Arial";
IntPtr handle = CreateFontIndirect(lf); if (IntPtr.Zero == handle)
{
Console.WriteLine("Can't creates a logical font.");
}
else
{ if (IntPtr.Size == )
Console.WriteLine("{0:X}", handle.ToInt32());
else
Console.WriteLine("{0:X}", handle.ToInt64()); // Delete the logical font created.
if (!DeleteObject(handle))
Console.WriteLine("Can't delete the logical font");
}
}
}

代码讨论

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

注册回调方法

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

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

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

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

public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
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. DB2删除重复数据

    有时候DB2建表时不设置主键,就可能存在脏数据,例如:两条一样数据重复存在,这时候就需要将重复记录删除,然后留下一条记录. )); ----插入重复数据 ,'jack'); 插入数据后,结果如下图: ...

  2. 03-maven学习-eclipse中创建maven项目

    一,更改eclipse默认的maven配置 window->preference 选择本地maven目录 一直确定后 二,更改maven默认settings window->prefere ...

  3. RedHat Enterprise Linux 6 配置Xmanager ,实现图形界面连接

    我们经常见到的几种最为常用的windows下远程管理Linux服务器的方法,基本上都是利用SecureCRT,或者是PUTTY等客户端工具通过ssh服务来实现Windows下管理Linux服务器的,这 ...

  4. linux下vi编辑文件

    vi 文件名.进入读文件模式 按i进入编辑模式 按g切光标换到第一行,按G光标切换到最后一行. 按Esc退出编辑模式 :q退出 :wq保存退出 以上命名后面加上!表示强制运行

  5. 【laravel54】composer install与composer update的区别

    1.基础概念: 我们需要明白laravel项目里面有2个配置文件,composer.json和composer.lock文件,前者是下载的依赖包配置文件,后者是锁定的包版本信息. 使用之前,需要cd ...

  6. 局域网Ubuntu与WinXP实现文件共享

    时间:2008-11-28 11:27:55   从新立得软件包管理器中安装Samba和Smbfs.Samba是在Unix系统中用于共享文件和打印机的应用软件.Smbfs is a filesyste ...

  7. C#:依据目录填充树视图

    #region 依据目录填充树视图 /// <summary> /// 依据文件夹目录,填充树视图 /// </summary> /// <param name=&quo ...

  8. Windows Mobile自动更新

    private static string m_CurrentPath; //取得作业平台 private static string Platform { get { return Environm ...

  9. ubuntu 16.04 安装 ITDB

    其实标题本不应该是这个 之前也部署过itdb,这款软件本身不必多言,来自希腊的精品.最近因为单位需要,又要重新部署.前几年部署itdb的时候用的是ub14,但目前最新的是ub16,于是就部署呗.但在后 ...

  10. hdu 1006 Tick and Tick 有技巧的暴力

    Tick and Tick Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...