摘要:最近灵感之源大哥常常在MSN中向我倾诉移植VB6代码的痛苦过程。我一看他的代码——好家伙,这都是原先在VB6种最高深的用法。什么不安全的指针啊,复制内存啊,一应俱全。调用平台的时候指针是少不了的,而VB.NET又不支持,所以麻烦常来,确实让人感到痛苦。我建议他用C#或者MC++,可是他愿意更纯的VB,那只好我来做了。为了能够让指针操作得以在VB中进行,必须对他们进行封装,泛型在这里是个很好的选择。而C#根本不允许对泛型的类型参数使用指针,那我们只有C++/CLI了。
C++/CLI支持两种托管的指针:内部指针和顶指针。内部指针(stdcli::language::interior_ptr)是一个指向托管堆中对象的动态指针。当托管堆中对象发生移动时,内部指针可以同步得到更新。内部指针具有很到的灵活性,他可以和C++本地指针进行类型转换,还可以进行指针运算。定指针(stdcli::language::pin_ptr)也是在一种指向托管堆对象的指针,被定指针所指的对象将被“定”在内存中,CLR不能移动它在内存中的位置,因此定指针可以用来向非托管的代码传递托管对象的指针,它能确保非托管代码操作的时候对象不会移动。最令人欣喜的就是,无论是定指针还是内部指针,他们都能对泛型的类型参数进行操作。所以我们可以封装一段代码来操作指针:
ref class PointerHelper sealed{ private: PointerHelper(void) { } public: generic<typename T> where T : System::ValueType static T GetValue(IntPtr address) { return *(interior_ptr<T>)(void *)address; } generic<typename T> where T : System::ValueType static void UnsafeSetValue(IntPtr address, T value) { interior_ptr<T> ptr = (interior_ptr<T>)(void*)address; *ptr = value; } generic<typename T>where T : System::ValueType static IntPtr UnsafeGetAddress(T %value) { pin_ptr<T> pp = &value; return IntPtr((void*)pp); }};
我也不太确定这段代码会是什么效果,来试验一下。这是一段VB的代码:
Dim x As IntegerDim p As IntPtr = PointerHelper.UnsafeGetAddress(x)PointerHelper.UnsafeSetValue(p, 123)MsgBox(x.ToString)
运行下看x的值,真的变了。相信有了这个,灵感之源所遇到的痛苦应该可以减弱一点了吧。至少从指针里获取指的操作不需要复制内存来做了。以前无法获取变量的地址现在也可以做到了。灵感之源只需要耐心等到VS2005发布,就可以轻松使用这段代码,逃离苦海了。
不过……这样使用终归是不安全的。VB对指针的类型没有任何检查和约束,我也没办法把这个东西做成强类型的。如果有人愿意,他完全可以这样写
Dim x As LongDim p As IntPtr = PointerHelper.UnsafeGetAddress(x)PointerHelper.UnsafeSetValue(p, 123.0F)MsgBox(PointerHelper.GetValue(Of Guid)(p).ToString)
这完全是可以运行的,把123.0F的浮点数塞到整型变量的地址里,然后按照Guid取出……不难想象若这种东西滥用起来,就会再次恢复到VB6那种“大师级”无法阅读、维护的代码了。...[
阅读全文]