VB.NET 内存指针和非托管内存的应用
介绍
Visual Basic 从来不像在C或C++里一样灵活的操纵指针和原始内存。然而利用.NET框架中的structures 和 classes,可以做许多类似的事情。它们包括 IntPtr, Marshal 以及 GCHandle。 这些structures(结构) 和classes(类) 允许你在托管和非托管环境中进行交互。本文中将向您展示如何使用这些structures 和 classes 去完成指针和内存的操作。
关于 IntPtr 结构
IntPtr 结构的行为像一个整型指针以便能应用到专门的平台。这个结构可以应用到支持或不支持指针的语言中。 .NET 文件 IO 类使用这个 结构扩展操作文件句柄。这个 IntPtr 结构形如整型指针的行为,但是它没有写或读相应位置内存的能力。这时你就需要System.Runtime.InteropServices 命名空间中的 Marshal 类。
关于 Marshal 类
这个类提供了全部分配非托管内存、拷贝非托管内存块,以及转换托管到非托管类型的方法。要保证你的代码导入了 System.Runtime.InteropServices 。
关于 GCHandle 结构
GCHandle 结构提供了从非托管内存中处理托管对象的方法。在非托管代码使用它时,可以控制垃圾碎片收集。
整数值的(读) 与 (写)
在我们的第一个例子中,使用 Marshal 类在内存中储存一个整数,得到一个内存指针去指向它在内存中的位置,并存到一个 IntPtr 结构中。最终我们将使用 Marshal 类读回这个值。
这里我们声明一个IntPtr类型,并且使用Marshal类中的AllocHGlobal 共享方法从全局堆中分配一个4字节的内存,并返回它的地址,存储在 IntPtr 变量中。接下来我们使用Marshal类中的WriteInt32 方法将整数值写到内存中。
Dim ptr As IntPtr : Dim n As Integer = 123
tr = Marshal.AllocHGlobal(4) '在进程的非托管内存中划分 4 字节大小的内存空间,并返回内存的指针地址
Marshal.WriteInt32(ptr, n) '将32位有符号的整数写入划分的内存空间中
Marshal.AllocHGlobal 方法的返回值为:指向新分配的内存的指针。
用完后必须使用 Marshal.FreeHGlobal 方法释放划分的内存
用下面的方法,你可以读指定位置的内存中的数。
Dim myInt As Integer = Marshal.ReadInt32(ptr) '参数 ptr 为内存空间指针
Marshal.ReadInt32 方法的返回值为:内存空间中的数据值。
字符串的(读) 与 (写)
在这个例子中,我们写一个托管字符串到堆中,并且随后读出来。
Dim str As String = "hello world" '字符串为引用类型,所以存储在非托管堆上
Dim ptr As IntPtr = Marshal.StringToHGlobalAuto(str) '向非托管内存中复制托管内容,并转换为ANSI格式
这里,我们使用了 Marshal类 中的 StringToHGlobalAuto() 方法拷贝托管字符串到非托管内存中。将内存指针地址保存在 IntPtr 结构中。为了从指定内存位置读回字符串,我们使用 Marshal 类中的 PtrToStringAuto() 方法 。
Dim mystring As String = Marshal.PtrToStringAuto(ptr) '从存储在非托管内存中的字符串复制等数量的字符到托管内存中
结构的(读) 与 (写)
在下一个例子中,我们将看到如何使用 Marshal 类和 IntPtr 结构,进行结构的读与写。
Public Class A
Public Structure PointA
Dim x As Integer
Dim y As Integer
'Public Sub New(ByVal _x As Integer, ByVal _y As Integer)
' x = _x
' y = _y
'End Sub
End Structure
Private Sub RWMemory()
Dim myStruct As PointA '其实.net框架自带 Point 坐标抽x,y类型(这里我们用自定义的结构类)
myStruct.x = 100 : myStruct.y = 200
'通过指定的字节数,在非托管堆划分内存空间(这里的字节数是结构对象,它又包含了x,y坐标值,所以字节数就是x,y坐标)
Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(myStruct)) '返回内存指针地址
Marshal.StructureToPtr(myStruct, ptr, True) '将数据从托管中(堆栈)封送到非托管(托管堆)的内存块中
Marshal.PtrToStructure(ptr, myStruct.GetType) '将数据从非托管(托管堆)的内存块中封送到托管(堆栈)
End Sub
End Class
这里我们使用一个名为 PointA 的结构。这个代码,要注意,我们使用了 Marshal 类中的 SizeOf 方法,它返回结构变量的尺寸。 而StructureToPtr 接收要被写的结构变量,将数据从托管中(堆栈)封送到非托管(托管堆)的内存块中。一个IntPtr 实例将被返回,同时标明是否Marshal类应该调用。
DestroyStructure 方法,建议标记为 True,如果标记为 false 可能导致内存泄漏。最后我们使用 Marshal 类的 PtrToStructure 方法反封送结构的值,展示了两个方法很好的实现了交互式功能。
对象的(读) 与 (写)
现在我们了解了如何用 Marshal 和 IntPtr 类读写值类型。
而托管对象的处理稍微不同。需要使用 GCHandle 来读写这些对象,
下面的代码显示如何做:
Public Class Employee
Public Name As String
Public Salary As Decimal
End Class
Private Sub wp()
Dim gh As GCHandle 'GCHandle类提供用于从非托管内存访问托管对象的方法
Dim emp As New Employee
emp.Name = "John"
emp.Salary = 12345.67
gh = GCHandle.Alloc(emp) '从非托管内存中访问托管对象,Alloc 方法能指定的对象分配句柄
Dim emp2 As Employee = gh.Target '设置该句柄表示的对象
gh.Free() '释放GCHandles句柄
End Sub
这里,对于自定义的 Employee 类我们使用 GCHandle (碎片手机句柄) 类进行分配和释放内存。共享(静态)的 Alloc 方法接受对象并存储在内存中并一个 GCHandle 类的实例。
我们可以使用这个对象的Target属性指向对象的引用。我们可以使用 GCHandle 实例的 Free() 方法来销毁相应的内存。
总结
在VB.NET中使用指针和非托管操作并不是易事,然而 IntPtr ,GCHandle 结构 和 Marshal 类可以让你完成类似的事情。
作者 | |||
|
VB.NET 内存指针和非托管内存的应用的更多相关文章
- 如何让IntPtr指向一块内存,以及托管内存与非托管内存的相互转化
IntPtr idp= IntPtr.Zero; StringBuilder idata = new StringBuilder("000000"); string idata = ...
- C# 托管内存与非托管内存之间的转换
c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工作于c#环境中,都是在使用托管内存 ...
- C# 中托管内存与非托管内存之间的转换
c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工作于c#环境中,都是在使用托管内存 ...
- 记一次 .NET 某智慧水厂API 非托管内存泄漏分析
一:背景 1. 讲故事 七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下: 从求助内容看,这位朋友真的太客气了,动不动就谈钱,真伤感情, ...
- 记一次 .NET 某打印服务 非托管内存泄漏分析
一:背景 1. 讲故事 前段时间有位朋友在微信上找到我,说他的程序出现了内存泄漏,能不能帮他看一下,这个问题还是比较经典的,加上好久没上非托管方面的东西了,这篇就和大家分享一下,话不多说,上 WinD ...
- [JVM教程与调优] 了解JVM 堆内存溢出以及非堆内存溢出
在上一章中我们介绍了JVM运行时参数以及jstat指令相关内容:[JVM教程与调优] 什么是JVM运行时参数?.下面我们来介绍一下jmap+MAT内存溢出. 首先我们来介绍一下下JVM的内存结构. J ...
- 记一次 .NET 某桌面奇侠游戏 非托管内存泄漏分析
一:背景 1. 讲故事 说实话,这篇dump我本来是不准备上一篇文章来解读的,但它有两点深深的感动了我. 无数次的听说用 Unity 可做游戏开发,但百闻不如一见. 游戏中有很多金庸武侠小说才有的名字 ...
- 在内存中观察CRL托管内存及GC行为
虽然看了一些书,还网络上的一些博文,不过对CRL托管内存的介绍都不是十分清楚,大部分都是一样的,如果再要了解细节就十分困难了. 所以借助winhex直接查看内存以证实书上的描述或更进一步揣摩CRL托管 ...
- Java堆内存Heap与非堆内存Non-Heap
堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.”“在 ...
随机推荐
- Javascript基础学习笔记
什么是变量?变量是用于存储信息的容器变量的声明语法: var 变量名 变量名 = 值;变量要先声明再赋值变量可以重复赋值变量的命名规则变量必须以字母开头:变量也能以$和_符号开头(不过我们不推荐这么做 ...
- json 去空值与缩进
var jSetting = new Newtonsoft.Json.JsonSerializerSettings(); //忽略值为null的 jSetting.NullValueHandling ...
- [STOI2014]舞伴(dp)
STOI是汕头OI...无聊翻到了去年的比赛题目,就写然后自己测了一下. 其实我很想吐槽为什么题目名是perm,perm好像和舞伴完全无关.. dp(x,s)=∑dp(x-1,s-{i}))(0< ...
- linux中BASH_SOURCE[0]
在C/C++中,__FUNCTION__常量记录当前函数的名称.有时候,在日志输出的时候包含这些信息是非常有用的.而在Bash中,同样有这样一个常量FUNCNAME,但是有一点区别是,它是一个数组而非 ...
- IE8的项目在IE11下 一些功能无法实现的解决方案
最近改了一些IE11下一些功能无法实现的项目,发现了有一些IE8下的方法 ,在IE11下被取消或者替代了,如下: 1.JavaScript 运行时错误: 对象不支持“attachEvent”属性或方法 ...
- node 通过指令创建一个package.json文件及npm安装package.json
描述包的文件是package.json文件. 一个这样的文件,里面的信息还是挺大的.我们可以放弃手动建立.为了练手我们有命令行来建一个这样的包; 完成name,varsion....license项的 ...
- express框架目录结构
. ├── app.js ├── bin │ └── www ├── node_modules │ ├── body-parser │ ├── cookie-parser │ ├── ...
- android 测量控件视图的方法
在实际项目中经常要用到 测量一个控件或者视图的高,宽.然后根据这个高宽进行一些逻辑. 计算视图宽高有几种方式先简单的了解下android 视图的绘制过程会促进理解. 一.android View绘制过 ...
- 为openwrt编译xd-h3c
西电老校区,openwrt上用的认证软件. 在package下新建一目录"xd-h3c",在里面新建一个Makefile,内容如下: include $(TOPDIR)/rules ...
- CKEditor扩展插件:自动排版功能
CKEditor是新一代的FCKeditor,是一个重新开发的版本.CKEditor是全球最优秀的网页在线文字编辑器之一,因其惊人的性能与可扩展性而广泛的被运用于各大网站. 如果还没接触过的可以看看, ...