1      DLLImport的使用

using System;

using System.Runtime.InteropServices; //命名空间

class Example

{

//用DllImport 导入Win32的MessageBox函数

[DllImport("user32.dll", CharSet =CharSet.Unicode)]

public static extern int MessageBox(IntPtr hWnd, String text, Stringcaption, uint type);

//方法被声明为 static。这是 P/Invoke 方法所要求的,因为在该 Windows API 中没有//一致的实例概念。接下来,还要注意该方法被标记为 extern。这是提示编译器该方法是通//过一个从 DLL 导出的函数实现的,因此不需要提供方法体。

static void Main()

{

// Call the MessageBox functionusing platform invoke.

MessageBox(new IntPtr(0),"Hello World!", "Hello Dialog", 0);

}

}

使用非托管DLL函数并不困难,下面我们可以详细的了解上面的代码的含义。首先介绍什么是托管代码,什么是非托管代码。然后再详细介绍DLLImport的使用方法和各字段的意义。

2      托管代码 (managed code)

.NET Framework的核心是其运行库的执行环境,称为公共语言运行库(CLR)或.NET运行库。通常将在CLR的控制下运行的代码称为托管代码(managed code)。

运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。

  托管代码是可以使用20多种支持Microsoft .NET Framework的高级语言编写的代码,它们包括:C#, J#, Microsoft Visual Basic .NET, Microsoft JScript .NET, 以及C++。所有的语言共享统一的类库集合,并能被编码成为中间语言(IL)。运行库编译器(runtime-aware ompiler)在托管执行环境下编译中间语言(IL)使之成为本地可执行的代码,并使用数组边界和索引检查,异常处理,垃圾回收等手段确保类型的安全。

  在托管执行环境中使用托管代码及其编译,可以避免许多典型的导致安全黑洞和不稳定程序的编程错误。同样,许多不可靠的设计也自动的被增强了安全性,例如类型安全检查,内存管理和释放无效对象。程序员可以花更多的精力关注程序的应用逻辑设计并可以减少代码的编写量。这就意味着更短的开发时间和更健壮的程序。

简单点说,托管代码是microsoft的中间语言,他主要的作用是在.NET FRAMEWORK的CLR执行代码前去编译源代码,也就是说托管代码充当着翻译的作用,源代码在运行时分为两个阶段:

1.源代码编译为托管代码;(所以源代码可以有很多种,如VB,C#,J#)

2.托管代码编译为microsoft系统的.net平台专用文件(如类库、可执行文件等)。

2.1     非托管代码 (unmanaged code)

  在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。

.net中托管代码的含义

2.2     什么是托管?托管是什么意思?

托管代码就是基于.net元数据格式的代码,运行于.net平台之上,所有的与操作系统的交换有.net来完成,就像是把这些功能委托给.net,所以称之为托管代码。非托管代码则反之。

举个例子l

Vc.net还可以使用mfc,atl来编写程序,他们基于MFC或者ATL,而不是.NET,所以是非托管代码,如果基于.net比如C#,VB.net则是托管代码

非托管代码是指.NET解释不了的

简单的说,托管代码的话,.net可以自动释放资料,非托管代码需要手动释放资料.

什么是托管C++

托管是.NET的一个专门概念,它倡导一种新的编程理念,因此我们完全可以把“托管”视为“.NET”。由托管概念所引发的C++应用程序包括托管代码、托管数据和托管类三个组成部分。

托管代码

.Net环境提供了许多核心的运行(RUNTIME)服务,比如异常处理和安全策略。为了能使用这些服务,必须要给运行环境提供一些信息代码(元数据),这种代码就是托管代码。所有的C#、VB.NET、JScript.NET默认时都是托管的,但Visual C++默认时不是托管的,必须在编译器中使用命令行选项(/CLR)才能产生托管代码。

托管数据

与托管代码密切相关的是托管数据。托管数据是由公共语言运行的垃圾回收器进行分配和释放的数据。默认情况下,C#、Visual Basic 和 JScript.NET 数据是托管数据。不过,通过使用特殊的关键字,C# 数据可以被标记为非托管数据。Visual C++数据在默认情况下是非托管数据,即使在使用 /CLR 开关时也不是托管的。

托管类

尽管Visual C++数据在默认情况下是非托管数据,但是在使用C++的托管扩展时,可以使用“__gc”关键字将类标记为托管类。就像该名称所显示的那样,它表示类实例的内存由垃圾回收器管理。另外,一个托管类也完全可以成为 .NET 框架的成员,由此可以带来的好处是,它可以与其他语言编写的类正确地进行相互操作,如托管的C++类可以从Visual Basic类继承等。但同时也有一些限制,如托管类只能从一个基类继承等。

2.3     托管代码如何调用非托管代码(c sharp如何调用c++代码)?

两种常用的做法:

下载:

http://download.microsoft.com/download/f/2/7/f279e71e-efb0-4155-873d-5554a0608523/CLRInsideOut2007_01.exe

1. COM interop

具体操作:

a. 用atl写com服务程序

b. 使用Tlbimp将atl写的com程序转换成 COM DLL

用如下命令:

tlbimp 你写的com.dll

tlbimp是 .NETFramework SDK中附带的类型库导入程序。用这个命令即是把生成一个非托管com dll的托管包装。

c. 托管客户端非常简单

直接new一下,然后调用对应的方法即可。

2. P/Invoke

a. 在托管客户端增加一条 DllImport语句和一个方法的调用。

介绍一个P/Invoke网站,http://pinvoke.net/

这个网站主要是一个wiki,允许开发者发现,编辑,增加PInvoke的签名,用户自定义类型和从托管代码(指c#和VB.net开发语言)访问win32和其他非托管api的信息。

世界各地的.Net开发者可以很容易分享自己有价值的东西给社区,

2.4     托管代码和非托管代码效率的对比

参加原文来自http://www.cnblogs.com/loverswordsman/articles/1367131.html

更详细的信息http://www.cnblogs.com/loverswordsman/articles/1367131.html

3     DllImportAttribute 的字段

在对托管代码进行 P/Invoke 调用时,DllImportAttribute 类型扮演着重要的角色。DllImportAttribute 的主要作用是给 CLR 指示哪个 DLL 导出您想要调用的函数。相关 DLL 的名称被作为一个构造函数参数传递给DllImportAttribute。

下表列出了所有与平台调用相关的特性字段。对于每个字段,下表都将包含其默认值,并且会提供一个链接,用于获取有关如何使用这些字段定义非托管 DLL 函数的信息。http://msdn.microsoft.com/zh-cn/library/w4byd5y4.aspx

字段

说明

BestFitMapping

启用或禁用最佳匹配映射。

CallingConvention

指定用于传递方法参数的调用约定。默认值为 WinAPI,该值对应于基于 32 位 Intel 的平台的 __stdcall。

CharSet

控制名称重整以及将字符串参数封送到函数中的方式。默认值为 CharSet.Ansi。

EntryPoint

指定要调用的 DLL 入口点。

ExactSpelling

控制是否应修改入口点以对应于字符集。对于不同的编程语言,默认值将有所不同。

PreserveSig

控制托管方法签名是否应转换成返回 HRESULT 并且返回值有一个附加的 [out, retval] 参数的非托管签名。

默认值为 true(不应转换签名)。

SetLastError

允许调用方使用 Marshal.GetLastWin32Error API 函数来确定执行该方法时是否发生了错误。在 Visual Basic 中,默认值为 true;在 C# 和 C++ 中,默认值为 false。

ThrowOnUnmappableChar

控件引发的异常,将无法映射的 Unicode 字符转换成一个 ANSI"?"字符。

除了指出宿主 DLL 外,DllImportAttribute 还包含了一些可选属性,其中四个特别有趣:EntryPoint、CharSet、SetLastError 和CallingConvention。http://www.360doc.com/content/11/0105/09 /3877783_84071078.shtml

3.1     entrypoint

入口点用于标识函数在 DLL 中的位置。在托管对象中,目标函数的原名或序号入口点将标识跨越交互操作边界的函数。此外,您可以将入口点映射到一个不同的名称,这实际上是将函数重命名。

以下列出了重命名 DLL 函数的可能原因:

·        避免使用区分大小写的 API 函数名

·        符合现行的命名标准

·        提供采用不同数据类型的函数(通过声明同一 DLL 函数的多个版本)

·        简化对包含 ANSI 和 Unicode 版本的 API 的使用

您可以使用 DllImportAttribute.EntryPoint 字段按名称或序号指定 DLL 函数。如果函数在方法定义中的名称与入口点在 DLL 的名称相同,则不必用 EntryPoint 字段来显式地标识函数。否则,使用以下属性形式之一来指示名称或序号:

l  [DllImport("dllname",EntryPoint="Functionname")]

l  [DllImport("dllname", EntryPoint="#123")]

l  指定入口点名称时,您可以提供一个字符串来指示包含入口点的 DLL 的名称,或者也可以按序号来标识入口点。序号以 # 符号为前缀,如 #1。如果省略此字段,则公共语言运行库将使用以DllImportAttribute 标记的 .NET 方法的名称。

下面的示例演示如何使用 EntryPoint 字段将代码中的 MessageBoxA 替换为 MsgBox

using System.Runtime.InteropServices;

public class Win32 {

[DllImport("user32.dll", EntryPoint="MessageBoxA")]

public static extern int MsgBox(int hWnd, String text,String caption, uint type);

}

3.2     CharSet

以下来自http://msdn.microsoft.com/zh-cn/library/7b93s42f(v=VS.80).aspx

DllImportAttribute.CharSet 字段控制字符串封送处理并确定平台调用在 DLL 中查找函数名的方式。本主题将介绍这两种行为。

对于采用字符串参数的函数,有些 API 将导出它们的两个版本:窄版本 (ANSI) 和宽版本 (Unicode)。例如,Win32 API 包含 MessageBox 函数的以下入口点名称:

·        MessageBoxA

提供单字节字符 ANSI 格式,其特征是在入口点名称后附加一个“A”。对 MessageBoxA 的调用始终会以 ANSI 格式封送字符串,它常见于 Windows 95 和 Windows 98 平台。

·        MessageBoxW

提供双字节字符 Unicode 格式,其特征是在入口点名称后附加一个“W”。对 MessageBoxW 的调用始终会以 Unicode 格式封送字符串,它常见于 Windows NT、Windows 2000 和 Windows XP 平台。

CharSet 字段接受以下值:

CharSet.Ansi(默认值)

·        字符串封送处理

平台调用将字符串从托管格式 (Unicode) 封送为 ANSI 格式。

·        名称匹配

在 DllImportAttribute.ExactSpelling 字段为 true(它是 Visual Basic 2005 中的默认值)时,平台调用将只搜索您指定的名称。例如,如果指定MessageBox,则平台调用将搜索 MessageBox,如果它找不到完全相同的拼写则失败。

当 ExactSpelling 字段为 false(它是 C++ 和 C# 中的默认值)时,平台调用将首先搜索未处理的别名 (MessageBox),如果找不到未处理的别名,则将搜索已处理的名称 (MessageBoxA)。请注意,ANSI 名称匹配行为与 Unicode 名称匹配行为不同。

CharSet.Unicode

·        字符串封送处理

平台调用会将字符串从托管格式 (Unicode) 复制为 Unicode 格式。

·        名称匹配

当 ExactSpelling 字段为 true(它是 Visual Basic 2005 中的默认值)时,平台调用将只搜索您指定的名称。例如,如果指定MessageBox,则平台调用将搜索 MessageBox,如果它找不到完全相同的拼写则失败。

当 ExactSpelling 字段为 false(它是 C++ 和 C# 中的默认值)时,平台调用将首先搜索已处理的名称 (MessageBoxW),如果找不到已处理的名称,则将搜索未处理的别名 (MessageBox)。请注意,Unicode 名称匹配行为与 ANSI 名称匹配行为不同。

CharSet.Auto

·        平台调用在运行时根据目标平台在 ANSI 和 Unicode 格式之间进行选择。

下面的示例演示用于指定字符集的 MessageBox 函数的三个托管定义。在第一个定义中,通过省略,使 CharSet 字段默认为 ANSI 字符集。

[DllImport("user32.dll")]

public static extern int MessageBoxA(int hWnd, String text, Stringcaption, uint type);

[DllImport("user32.dll", CharSet=CharSet.Unicode)]

public static extern int MessageBoxW(int hWnd, String text, Stringcaption, uint type);

[DllImport("user32.dll", CharSet=CharSet.Auto)]

public static extern int MessageBox(int hWnd, String text, String caption,uint type);

CharSet.Ansi 和 CharSet.Unicode的名称匹配规则大不相同。对于 Ansi 来说,如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果 DLL 中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于 Unicode 来说则正好相反。如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果 DLL 中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是 Auto,则匹配规则与平台有关(在 Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。如果 ExactSpelling 设置为 true,则只有当 DLL 中存在“MyMethod”时才返回“MyMethod”。

如果 DLL 函数不以任何方式处理文本,则可以忽略 DllImportAttribute 的 CharSet 属性。然而,当 Char 或 String 数据是等式的一部分时,应该将 CharSet 属性设置为 CharSet.Auto。这样可以使 CLR 根据宿主 OS 使用适当的字符集。如果没有显式地设置 CharSet 属性,则其默认值为 CharSet.Ansi。这个默认值是有缺点的,因为对于在 Windows 2000、Windows XP 和 Windows NT? 上进行的 interop 调用,它会消极地影响文本参数封送处理的性能。

应该显式地选择 CharSet.Ansi 或 CharSet.Unicode 的 CharSet 值而不是使用 CharSet.Auto 的唯一情况是:您显式地指定了一个导出函数,而该函数特定于这两种 Win32 OS 中的某一种。ReadDirectoryChangesW API 函数就是这样的一个例子,它只存在于基于 Windows NT 的操作系统中,并且只支持 Unicode;在这种情况下,您应该显式地使用 CharSet.Unicode。

有时,Windows API 是否有字符集关系并不明显。一种决不会有错的确认方法是在 Platform SDK 中检查该函数的 C 语言头文件。(如果您无法肯定要看哪个头文件,则可以查看 Platform SDK 文档中列出的每个 API 函数的头文件。)如果您发现该 API 函数确实定义为一个映射到以 A 或 W 结尾的函数名的宏,则字符集与您尝试调用的函数有关系。Windows API 函数的一个例子是在 WinUser.h 中声明的 GetMessage API,您也许会惊讶地发现它有 A 和 W 两种版本。

3.3    SetLastError

SetLastError 错误处理非常重要,但在编程时经常被遗忘。当您进行 P/Invoke 调用时,也会面临其他的挑战 — 处理托管代码中 Windows API 错误处理和异常之间的区别。我可以给您一点建议。

如果您正在使用 P/Invoke 调用 Windows API 函数,而对于该函数,您使用 GetLastError 来查找扩展的错误信息,则应该在外部方法的DllImportAttribute 中将 SetLastError 属性设置为 true。这适用于大多数外部方法。

这会导致 CLR 在每次调用外部方法之后缓存由 API 函数设置的错误。然后,在包装方法中,可以通过调用类库的System.Runtime.InteropServices.Marshal 类型中定义的 Marshal.GetLastWin32Error 方法来获取缓存的错误值。我的建议是检查这些期望来自 API 函数的错误值,并为这些值引发一个可感知的异常。对于其他所有失败情况(包括根本就没意料到的失败情况),则引发在 System.ComponentModel 命名空间中定义的Win32Exception,并将 Marshal.GetLastWin32Error 返回的值传递给它。

3.4    CallingConvention

CallingConvention.Cdecl : 调用方清理堆栈。它使您能够调用具有 varargs 的函数。

CallingConvention.StdCall : 被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

CallingConvention 字段的默认值为 Winapi,而后者又默认为 StdCall 约定。

可能是最不重要的一个 DllImportAttribute 属性是 CallingConvention。通过此属性,可以给 CLR 指示应该将哪种函数调用约定用于堆栈中的参数。CallingConvention.Winapi 的默认值是最好的选择,它在大多数情况下都可行。然而,如果该调用不起作用,则可以检查 Platform SDK 中的声明头文件,看看您调用的 API 函数是否是一个不符合调用约定标准的异常 API。

通常,本机函数(例如 Windows API 函数或 C- 运行时 DLL 函数)的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数 Windows API 函数都是首先将函数的最后一个参数推入堆栈,然后由被调用的函数负责清理该堆栈。相反,许多 C-运行时 DLL 函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈,将堆栈清理工作交给调用者。

幸运的是,要让 P/Invoke 调用工作只需要让外围设备理解调用约定即可。通常,从默认值CallingConvention.Winapi 开始是最好的选择。然后,在 C 运行时 DLL 函数和少数函数中,可能需要将约定更改为 CallingConvention.Cdecl。

3.5    ExactSpelling

ExactSpelling 指示是否应修改非托管 DLL 中的入口点的名称,以与 CharSet 字段中指定的 CharSet 值相对应。如果为 true,则当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Ansi 值时,向方法名称中追加字母 A,当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Unicode 值时,向方法的名称中追加字母 W。此字段的默认值是 false。

3.6    PreserveSig

PreserveSig指示托管方法签名不应转换成返回 HRESULT、并且可能有一个对应于返回值的附加 [out, retval]参数的非托管签名。

4      参数类型

l  数值型直接用对应的就可。(DWORD ->int , WORD -> Int16)

l  API中字符串指针类型 -> .net中string

l  API中句柄 (dWord) ->.net中IntPtr

l  API中结构 -> .net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类

5      Win32 API 中几个常用的 DLL

DLL

内容说明

GDI32.dll

用于设备输出的图形设备接口 (GDI) 函数,例如用于绘图和字体管理的函数。

Kernel32.dll

用于内存管理和资源处理的低级别操作系统函数。

User32.dll

用于消息处理、计时器、菜单和通信的 Windows 管理函数。

6      wince 中的DLL

DLL

内容说明

coredll.dll

超详细解析托管与非托管相关推荐

  1. 有关Dispose,Finalize,GC.SupressFinalize函数-托管与非托管资源释放的模式

    //这段代码来自官方示例,删除了其中用处不大的细节using System; using System.ComponentModel;/**** 这个模式搞的这么复杂,目的是:不管使用者有没有手动调用 ...

  2. Android技能树 — 网络小结(6)之 OkHttp超超超超超超超详细解析

    前言: 本文也做了一次标题党,哈哈,其实写的还是很水,各位原谅我O(∩_∩)O. 介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看 ...

  3. 单片机数字钟(调时,调时闪烁,万年历,年月日)超详细解析

    2019/07/13 单片机数字钟(调时,调时闪烁,万年历,年月日)超详细解析 发表日期:2019/07/13 单片机开发板:巫妖王2.0, 使用同款开发板可直接上板测试 文档说明: 实现功能 : 一 ...

  4. C++/CLI 托管C++的托管与非托管字符串的相互转换【1】

    Git 源码地址 C++/CLI 托管C++的托管与非托管字符串的相互转换[1] C++/CLI 托管C++的数据类型介绍[2] C++/CLI 托管C++的基本数据类型及函数[3] C++/CLI ...

  5. .net中的托管、非托管

    1.托管代码Managed Code 托管代码是编译器创建,被编译成中间语言(IL),IL被保存在程序集(描述代码的类.方法它和属性的元数据文件)中. 托管代码运行在CLR中: 托管代码独立于平台和语 ...

  6. 托管和非托管的区别。

    NET Framework 是一种新的计算平台,它简化了在高度分布式 Internet 环境中的应用程序开发..NET Framework 旨在实现下列目标: 提供一个一致的面向对象的编程环境,而无论 ...

  7. 计算机网络之交换机的工作原理---超详细解析,谁都看得懂!!

    在了解交换机的工作原理之前,我们先要了解几个概念. 一.相关概念  1.OSI七层模型是哪七层? 自上而下分别是: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 交换机工作在数据链路层, ...

  8. 托管和非托管_技术和托管新闻综述

    托管和非托管 Welcome to our latest round-up of news from the technology and hosting world. Here's what we' ...

  9. 托管与非托管的混合编程问题

    (急,先放技术区几天再移动) 写C#应用,需要MDI界面,其中一个子窗体是使用浏览器界面的,单独调试都好好的,使用到MDI中就出问题了,不能打开连接,显示忙信息,没有其他任何响应信息. 现在想通过使用 ...

最新文章

  1. 【log】12/11 checking project:(Laravel)snsTest
  2. SQL Server 预编译执行SQLs
  3. Java设计模式之行为型:迭代器模式
  4. MySQL发展史重大事件
  5. mysql解释器_atitit.java解析sql语言解析器解释器的实现
  6. 在Java 里面怎么比较字符串
  7. python json.dumps慢_python json.dumps中文乱码问题解决
  8. iOS:个人浅谈工厂模式
  9. Linux IO 测试工具 fio命令
  10. MFC初探 —— 基于对话框的MFC显示控制台
  11. SpringMvc之参数绑定注解详解之一
  12. (转)“领导者”沈南鹏:逼着自己不断思考,作为VC没有选择
  13. 天勤2022数据结构(一)线性表
  14. 懂中文就能学会的高薪职位,你猜对了吗?
  15. web项目上云_联想Filez携手浙江中烟,发力“云”端,打造“烟草上云”新势能...
  16. 东南大学计算机科学与网络,顾冠群
  17. 中职计算机英语教师教学总结,中职教师工作总结
  18. IllegalStateException: Failure saving state: active Fragment has cleared
  19. Excel导入sql server 2005错误0xc00470fe数据流任务产品级别对于组件源-Sheet1$(1)而言不足
  20. Facebook 登报反苹果,他反的到底是什么?

热门文章

  1. python爬微信头像_itchat+pillow实现微信好友头像爬取和拼接
  2. python是一门面向_如何系统地自学一门Python 语言
  3. 基于springboot的养老院系统(完美运行,数据库源代码,可远程调试)
  4. 关于php说法错误的是,下列关于 PHP 的叙述哪一个是错误的?
  5. 深入探索Android稳定性优化
  6. 工程材料知识点总结(全)
  7. 【算法leetcode】2331. 计算布尔二叉树的值(多语言实现)
  8. 屋顶建模(中国古代建筑)
  9. 好网角怎么发帖?文章发不出去怎么解决?
  10. 透过敦煌网的IPO,看跨境电商热潮里新老玩家的新一轮交互