VB.NET中指针和非托管内存的应用

介绍

Visual Basic 从来不像在C或C++里一样灵活的操纵指针和原始内存。然而利用.NET框架中的structures 和 classes,可以做许多类似的事情。它们包括 IntPtr, Marshal 以及 GCHandle。 这些structures 和classes 允许你在托管和非托管环境中进行交互。本文中,我 将向您展示如何使用这些structures 和 classes 去完成指针和内存的操作。尽管是VB.NET的代码,转换成 C# 应该也不会有问题。同时注意,为了简明,我没有提供实际的非托管代码,我的目标是讲解如何让它们应用到每一个独立的需求中。

关于 IntPtr structure

IntPtr structure 的行为像一个整型指针以便能应用到专门的平台。这个 structure 可以应用到支持或不支持指针的语言中。 .NET 文件 IO 类使用这个 structure 扩展操作文件句柄。

关于 Marshal 类

这个 IntPtr structure 形如整型指针的行为,但是它没有写或读相应位置内存的能力。这时你就需要System.Runtime.InteropServices 命名空间中的 Marshal 类。这个类提供了全部分配非托管内存、拷贝非托管内存块,以及转换托管到非托管类型的方法。要保证你的代码导入了 System.Runtime.InteropServices 。

关于 GCHandle structure

GCHandle structure 提供了从非托管内存中处理托管对象的方法。在非托管代码使用它时,可以控制垃圾碎片收集。

整数值的读写

在我们的第一个例子中,使用Marshal类在内存中储存一个整数,得到一个指针去指向它,并存到一个 IntPtr 结构中。最终我们将使用Marshal 类读回这个值。

[vb]  view plain copy
  1. Dim ptr As IntPtr  
  2. Dim n as Integer=123  
  3. ptr = Marshal.AllocHGlobal(4)  
  4. Marshal.WriteInt32(ptr, n)  

这里我们声明一个IntPtr类型,并且使用Marshal类中的AllocHGlobal 共享方法从全局堆中分配一个4字节的内存,并返回它的地址,存储在 IntPtr 变量中。接下来我们使用Marshal类中的WriteInt32 方法将整数值写到内存中。

用下面的方法,你可以读指定位置的内存。

[vb]  view plain copy
  1. Dim myint as Integer=Marshal.ReadInt32(ptr)  

字符串的读写

在这个例子中,我们写一个托管字符串到堆中,并且随后读出来。

[vb]  view plain copy
  1. Dim str as String = "hello world"    
  2. Dim ptr As IntPtr = Marshal.StringToHGlobalAuto(str)    
  3. Dim mystring As String = Marshal.PtrToStringAuto(ptr)   

这里,我们使用了Marshal类中的StringToHGlobalAuto()方法拷贝托管字符串到非托管内存中。我们将内存地址保存在 IntPtr 结构中。为了从指定内存位置读回字符串,我们使用Marshal类中的PtrToStringAuto()方法 。

结构的读写

在下一个例子中,我们将看到如何使用Marshal 类和 IntPtr 结构,进行structure的读写。 

[vb]  view plain copy
  1. Public Structure Point  
  2.      Dim x As Integer  
  3.      Dim y As Integer  
  4. End Structure  
  5. Dim mystruct1,mystruct2 As Point    
  6. mystructure.x=100    
  7. mystructure.y=200    
  8. Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(mystruct))    
  9. Marshal.StructureToPtr(mystruct, ptr, True)    
  10. mystruct2 = Marshal.PtrToStructure(ptr, New Point().GetType)   

这里我们使用一个名为Point的结构。这个代码和例子1中的相似,但要注意,我们使用了Marshal类中SizeOf method方法返回结构变量的尺寸。 方法StructureToPtr 接收要被写的结构变量,一个IntPtr 实例将被返回,同时标明是否Marshal类应该调用 DestroyStructure 方法,建议标记为 True,如果标记为 false 可能导致内存泄漏。最后我们使用Marshal累得PtrToStructure方法对会结构的值。

对象的读写

现在我们了解了如何用Marshal和IntPtr类读写值类型。托管对象的处理稍微不同。需要使用 GCHandle 来读写这些对象,下面的代码显示如何做: 

[vb]  view plain copy
  1. Public Class Employee        
  2.     Public Name As String        
  3.     Public Salary As Decimal    
  4. End Class      
  5. Dim gh As GCHandle    
  6. Dim emp As New Employee    
  7. emp.Name = "John"    
  8. emp.Salary = 12345.67    
  9. gh = GCHandle.Alloc(emp)    
  10. Dim emp2 As Employee = gh.Target    
  11. gh.Free()    

这里,对于employee类我们使用GCHandle (碎片手机句柄) 类进行分配和释放内存。共享(静态)的 Alloc 方法接受对象并存储在内存中并一个 GCHandle 类的实例。我们可以使用这个对象的Target属性指向对象的引用。我们可以使用GCHandle实例的Free方法来销毁相应的内存。

总结

在VB.NET中使用指针和非托管操作并不是易事,然而 IntPtr ,GCHandle 结构 和 Marshal 类可以让你完成类似的事情。

作者
Bipin Joshi
Bipin Joshi BinaryIntellect Consulting 的所有者,在那里他提供了许多关于 .NET 技术的训练程序。

转载链接:http://blog.csdn.net/suprman/article/details/5031719

你可能感兴趣的:(marshal,intptr,非托管内存,vb.net读写指针,GCHandle)