上次我们已经见识了inlineIL的强大功能,现在我们继续挖掘它的应用。大家都知道VB.NET禁止使用不安全代码,这对于VB的定位来说没什么。可是总有一些从VB6过来的人,他们对VarPtr这种超级不安全的指针念念不忘,而这也确实是突破旧VB语言限制,转变为VB黑客级人物的利器。为了让这些人在VB.NET上继续他们的黑客事业,我们也只有再次动用嵌入IL这一“斧头”。

绝大部分VB6使用指针的功能,都能利用System.Runtime.InteropServices.Marshal类来完成,比如Copy内存,在堆里分配内存或者读写指针等等。现在我们缺少的只是获取变量的指针。现在有一个类Class2,有一个字段i,现在假设我们要获取i的地址,并做一些和非托管程序的互操作。变量i是Class的字段,分配在托管堆中,它的地址是可以被CLR挪动的。我们要获取他的指针,必须将它定在内存中。好在.NET提供了这类方法,虽然不及C++/CLI的pin_ptr那样强大好用……现在嵌入IL

Class Class2 Public Shared Sub Main() Dim o As New Class2 o.Test() End Sub Private i As Integer = 1 Public Sub Test() Dim addr As IntPtr #If IL Then .locals(int32& pinned p) ldarg.0 ldflda int32 Class2::i stloc p ldloc p conv.i call native int native int::op_Explicit(void*) stloc addr #End If Marshal.WriteInt32(addr, 100) Console.WriteLine(i.ToString()) End Sub End Class

注意代码中的pinned,他定义的指针指向一个变量后,就会将变量的地址定在托管堆中,直到这个指针的生命周期结束。在C#中,我们是用fixed来做这件事的。如果是获取局部变量的地址,那么无需使用pinned,因为栈中的值类型变量是不会被移动的。接下来我们将指针转换为无类型信息的IntPtr。因为嵌入IL功能的限制,我们无法在VB代码部分使用嵌入IL声明的变量,所以只有用一个IntPtr作桥。好了,看看结果吧,还不算太难哈。