数学上不是有斜二测画法,算好坐标即可画出
成都创新互联是一家专注于成都网站建设、成都做网站与策划设计,贡嘎网站建设哪家好?成都创新互联做网站,专注于网站建设十多年,网设计领域的专业建站公司;建站业务涵盖:贡嘎等地区。贡嘎做网站价格咨询:028-86922220
或者用AnyCAD的.Net图形控件
也可以调用matlab 实现
VB.NET和VB6.0有什么区别
Visual Basic .NET是Microsoft Visual Studio .NET套件中主要组成部分之一。.NET版本的Visual Basic增加了更多特性,而且演化为完全面向对象(就像C++)的编程语言。本文将介绍VB.NET的新特性,并比较VB6.0/VB.NET之间的区别,阐述如何利用VB.NET编写简单的应用程序。
1.1 什么是 VB.NET? Microsoft推出全新的编程和操作系统Framework——.NET,支持多种语言利用公共.NET库开发应用程序,这些应用程序在.NET Framework上运行。使用Visual Basic在.NET Framework上编程,这就是VB.NET。
首先,让我演示在VB.NET中写最简单的控制台程序:Hello World。
1.2 Hello, World!“Hello World!”是初学者学习Windows编程的代表性程序。我们的第一个程序就叫做“Hello VB.NET World!”。该程序在控制台输出一句话:“Hello VB.NET World!”,代码如下所示:
代码 1.1: Hello VB.NET World例子Imports System
Module Module1
Sub Main()
System.Console.WriteLine("Hello VB.NET World!")
End Sub
End Module
1.3 VB.NET 编辑器和编译器你可以在记事本或VS.NET IDE等任意文本编辑器中撰写上述代码,然后保存为HelloWorld.vb。 代码编写完成之后,要么在命令行、要么在VS.NET IDE中编译它。在Microsoft .NET Framework SDK中已经包括VB.NET编译器vbc.exe[][1],从IDE或是命令行都可以调用。要从命令行编译HelloWorld.vb,请在命令行窗口输入
vbc HelloWorld.vb /out:HelloWorld.exe /t:exe
编译结束后,HelloWorld.exe被创建到当前目录下。在资源管理其中双击图标或在命令行执行,程序正确地运行了。祝贺你进入VB.NET开发者的行列。
Imports 语句
如你所知,大部分的.NET类型都在名字空间(namespace)中定义。Namespace是定义和管理类别的范畴。察看.NET Framework Class Library,可以看到数以百计的namespace。例如,System namespace就包括了Console、Object等类型定义。如果想使用Console类,需要用Imports指令导入System namespace。如下所示:
Imports System甚至可以明确地调用namespace而无需用Import导入。下面的例子展示了不用Import的“Hello World!”程序:
代码1.2: Hello VB.NET World例子Module Module1
Sub Main()
System.Console.WriteLine("Hello VB.NET World!")
End SubEnd Module1.4 解析 "Hello VB.NET World!"程序第一行是:
Imports System; System namespace定义了Console类,该类用于读写控制台(命令行窗口)。然后你定义了一个module:Module Module1
…End Module所有的VB程序都包括一个Main()方法,即应用程序入口点。在例子程序中,我们调用Console.WriteLine()向控制台写入“Hello VB.NET World!”:
Sub Main()
Console.WriteLine(“Hello VB.NET World!”) End SubWriteLine()方法归属于Console类,它负责向控制台写一个带有行结束符的字符串。如前所述,Console类定义于System namespace,你通过直接引用来控制类成员。
Console类负责读写系统控制台。读控制台输入用Read和ReadLine方法,向控制台输出用WriteLine方法。
表1.1 Console类定义的方法
方法 用途 例子
Read 读入单个字符 int i = Console.Read();
ReadLine 读入一行 string str = Console.ReadLine();
Write 写一行 Console.Write("Write: 1");
WriteLine 写一行,并带上行结束符
Console.WriteLine("Test Output Data with Line");
1.5 VB.NET有什么新特点? VB.NET比 VB6.0更加稳定,而且完全面向对象。也许你还记得,VB6.0不支持继承、重载和接口,所以不是真正面向对象的。而VB.NET则支持这些面向对象特性。VB6.0有两个薄弱环节——多线程和异常处理。在VB.NET中,开发多线程应用和使用C++/C#别无二致,结构化异常处理也得到支持。稍后我们会详细解释这些特性。
下面是VB.NET的特性列表——
·面向对象的编程语言。支持继承、重载、接口、共享成员和构造器。·支持所有的CLS特性,如存取控制.NET类、与其它.NET语言交互、元数据、公共数据类型、委托等等。·多线程支持。·结构化异常处理。 1.6 名字空间与集合 前面讨论了我们的第一个VB.NET程序。该程序中首先引人注意的是名字空间(namespace)。在.NET参考文档中,你会发现每个类都归属于某个namespace。那么,namespace到底是什么?
一个namespace是类和组件的逻辑组合,其目的在于将.NET class按类别定义。微软借用了C++ class packaging概念:namespace来描述这种组合。.NET Framework中的组件被称为集合(assembly)。全部.NET代码在数百个库文件(DLL)中定义。Namespace把assembly中定义的类组织起来。一个namespace可以包括多个assembly,一个assembly也可以在多个namespace中定义。 namespace树的根节点是System namespace。在.NET Library中,每个class都在一组相似的类别中定义。例如,System.Data namespace只包括数据相关类。同样,System.Multithreading只包括多线程类。
在使用.NET支持的语言(如C#、VB.NET、C++.NET等)创建新应用程序时,你会注意到每个应用程序都被定义为一个namespace,而所有的class都归属于这个namespace。通过引用这个namespace,其它应用程序就能存取这些class。 在.NET中,代码被编译为中间语言(Intermediate Language,IL),assembly中存储了IL代码、元数据和其它资源文件。同一个assembly可以附属于一个或多个Exe/DLL。所有的.NET库都存储在assembly中。
1.7 VB.NET: 完全面向对象的编程语言抽象、封装、多态、继承是面向对象语言的四个基本属性。VB6.0不支持继承,而VB.NET则不然。所以,和C++一样,VB.NET也是完全面向对象的编程语言。
Class 和 ModuleVB.NET用Class...End Class语句对创建class。每个VB.NET至少包括一个Module(模块)。Module在Module…End Module语句对中实现。应用程序的主要模块是Main方法,亦即应用程序入口点。
和VB6.0相似的地方是,都可以使用Function/Sub关键字定义方法。下面的例子显示了如何创建class、添加方法,并从主程序调用方法: Imports System
Module Module1
Sub Main()
Dim cls As TestClass = New TestClass
Console.WriteLine(cls.MyMethod)
End Sub
Class TestClass
Function MyMethod() As String
Return "Test Method"
End Function
End Class
End ModuleProperty属性(Property)是类变量的公共描述。Property…End Property语句用以创建property。属性的Get/Set方法分别用于取得和设置属性值。下面的例子中,Data是TestClass的属性。
Imports System
Imports System.Console
Module Module1
Sub Main()
Dim cls As TestClass = New TestClass
WriteLine(cls.MyMethod)
WriteLine(cls.Data)
cls.Data = "New Data"
WriteLine(cls.Data)
End Sub
End Module
Class TestClass
Private strData As String = "Some Data"
Function MyMethod() As String
Return "Test Method!"
End Function
' Adding Data property to the class
Public Property Data() As String
Get
Return strData
End Get
Set(ByVal Value As String)
strData = Value
End Set
End Property
重载VB.NET通过overload关键字支持方法重载。使用这个关键字,你可以定义同名但不同参数的方法。
类成员访问域
除了原有的Private和Public,VB.NET引入了几个新关键字。全部访问域关键字列表如下:
关键字 作用域
Private 限于class内部
Public 可以从class外访问
Friend 限于class所属的应用程序内
Protected 只能被class和其派生类访问
Protected Friend 能被class、应用程序和派生类访问
继承继承是面向对象编程语言中最常用的技术。继承让你能够重用类代码和功能。
VB.NET支持继承,而VB6.0则不支持。继承的好处在于你能使用任何人编写的类,从这些类派生自己的类,然后在自己的类中调用父类功能。在下面的例子中,Class B派生自Class A,我们将从Class B中调用Class A的方法MethodA。
Imports System
Imports System.Console
Module Module1
Sub Main()
Dim bObj As B = New B
WriteLine(bObj.MethodA())
End Sub
End Module
' Class A defined
Public Class A
Function MethodA() As String
Return "Method A is called."
End Function
End Class
'Class B, inherited from Class A. All members (Public and Protected)
' can be access via B now.
Public Class B
Inherits A
Function MethodB() As String
Return "Method B is called."
End Function
End Class
可以从一个class中派生多个自定义class,也可以从多个class派生一个自定义class。
共享的成员类的共享成员被类的所有实体共享。共享成员可能是属性、方法或字段/值域。在你不想让用户全面控制自己的类时,共享成员相当有用。例如,你可以开发一个类库,让用户通过共享成员使用其中的部分功能。
可以通过class本身引用共享成员,而无需通过类的实体。例如:Module Module1
Sub Main()
WriteLine(A.MethodA())
End Sub
End Module
' Class A defined
Public Class A
Shared Function MethodA() As String
Return "Method A is called."
End Function
End Class
多线程VB语言的一大弱点就是缺乏编写自由线程(free-threaded)程序的能力。在.NET Framework中,所有语言共享CRL(Common Runtime Library,公共运行库),也就是说,你可以用VB.NET、C#或其它.NET语言编写同样的程序。
System.Threading namespace定义了线程类。我们只需要引入System.Threading namespace,即可使用线程类。
System.Threading.Thread类提供线程对象,可以使用Thread类创建或破坏线程。
创建线程使用Thread类的实体创建一个新线程,然后用Thread.Start方法开始执行线程。线程构造器接受一个参数,该参数指明你要在线程中执行的procedure。在下例中,我想在oThread1(Thread类的一个实体)的第二线程中执行SecondThread过程:
oThread1 = New Thread(AddressOf SecondThread)
SecondThread procedure looks like below:
Public Sub SecondThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine(i.ToString())
Next
End Sub
然后,调用Thread.Start()开始线程:
oThread1.Start()
下面的代码创建两个第二线程:
Imports System
Imports System.Threading
Module Module1
Public oThread1 As Thread
Public oThread2 As Thread
Sub Main()
oThread1 = New Thread(AddressOf SecondThread)
oThread2 = New Thread(AddressOf ThirdThread)
oThread1.Start()
oThread2.Start()
End Sub
Public Sub SecondThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine(i.ToString())
Next
End Sub
Public Sub ThirdThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine("A" + i.ToString())
Next
End Sub
End Module
破坏线程 调用Abort方法来破坏(中止)一个线程。在调用Abort之前,确保用IsAlive判断线程处于活动状态。
If oThread1.IsAlive Then
oThread1.Abort()
End If
暂停线程可以使用Sleep方法来暂停线程执行。Sleep方法接受一个以毫秒为单位的参数,指明线程应当暂停多长时间。
下面的例子让线程暂停1秒钟:
oThread2.Sleep(1000)你也可以使用Suspend和Resume方法来挂起和继续线程执行。
设定线程优先级Thread类的Priority属性用于设定线程优先级。该属性可以设置为Normal,AboveNormal,BelowNormal,Highest和Lowest。如:
oThread2.Priority = ThreadPriority.Highest线程与Apartment使用ApartmentState属性设置线程的apartment类型,该属性值可以为STA,MTA或是Unknown[][2]:
oThread.ApartmentState = ApartmentState.MTAMTS意味着可以使用多线程模式,而STA则只能是单线程执行。
Public Enum ApartmentState
{
STA = 0,
MTA = 1,
Unknown = 2,
}
1.8 结构化异常处理异常处理也被称之为错误处理。作为VB程序员,你一定对On Error Goto和On Error Resume Next这些VB6.0错误处理语句耳熟能详。这种类型的错误处理被称为非结构化异常处理(Unstructured Exception Handling)。而在VB.NET中,Microsoft推出了结构化异常处理机制。VB.NET支持类似C++的TryCatch..Finally控制。Try..Catch..Finally结构如下: Try
' 可能导致异常的代码
Catch
' 当异常发生时处理异常的代码
Finally
' 清理现场
End Try
Try语句块用以抛出异常。如果异常发生,在Catch语句块中处理。Finally语句块是可选的,在需要释放资源时特别有用。
1.9 VB6.0与VB.NET的不同之处除了上面谈到的语言进化,还有一些语法上的变化。所有这些语言和语法的变化在MSDN中均可查到,本文只作简单介绍。
数据类型(Data Type)的改变VB.NET中有些数据类型得到改进。下面是变化对照表。
数据类型 VB6.0 VB.NET
Integer 16 bit size 32 bit size
Long 32 bit size 64 bit size
Currency 用于存储大浮点数 被decimal替代,支持更高精度
Variant 可以存储任意类型数据 被Object类型替代,也可以存储任意类型数据,但结果更好
Date Date类型被存储为double 引入DateTime类型,用于存储不同格式的日期
在VB.NET中,Short数据类型是16 bit的。Short,Integer和Long都等同于CLR的System.Int16、System.Int32和System.Int64类型。 变量声明的变化在VB6.0中,变量声明有许多限制。其中之一就是不能同行声明多个变量。如果一定要在一行中声明多个变量,就一定得指明每个变量的类型,否则将被默认为Variant类型。
Dim a1, a2 As Integer Dim a3 As Integer, a4 As Integer 第一行中的a1是Variant类型,a2是Integer类型。第二行中两个变量都是Integer类型。VB.NET支持同行声明多个变量,举例如下:
Dim a1, a2, a3 As Integer 变量初始化是另一个问题。在VB6.0中不能同时声明和初始化变量,而VB.NET则支持这个特性。
Dim name As String = "Mahesh"System.Console.Write(name) 声明常量也可以照此办理:Const DT_COUNT As Integer = 23 New关键字。在VB.NET中,New关键字用于创建对象。由于数据类型是对象,所以New关键字用以创建一个数据类型对象。
Dim i As Integer = New Integer()i = 10System.Console.WriteLine(i.ToString()) 代码块级别支持。像C++一样,VB.NET支持代码块级别的作用域检查。在语句块中声明的变量只在块内有效。
For i = 1 To 10Dim p As LongSystem.Console.WriteLine(i.ToString())NextSystem.Console.WriteLine(p.ToString()) 这段代码在VB.NET中会得到一个编译错误,因为p在For..Next语句块之外不可访问。在VB6.0中这段代码可以通过。
改进了的类型安全
在VB6.0中,当你声明一个对外部过程的引用时,可以指定任意类型的参数为As Any。Any关键字禁止了类型检查,允许任意数据类型传入和返回。
VB.NET不支持Any关键字。你必须指定每个参数和返回值的数据类型。数组VB.NET对数组作了明显的改动。
数组范围。在VB.NET中,你需要格外留意数组范围问题。VB6.0默认数组下界为0,故数组中的元素数量等与数组上界值加一。下面的数组界限从A(0)到A(10),共有11个元素:
Dim A(10) As Single可以使用Option Base改变下界值为1。在VB.NET中,数组和C++一样,下界值为0,不支持Option Base。注意:MSDN文档指出数组只能包括与其尺寸相等的元素数量,例如:Dim A(10) As Integer 只能包括10个元素(从A(0)到A(9)),但在编译下面这段代码时我发现它运行良好,看起来数组中容纳了11个元素。
Dim A(10) As Integer A(0) = 12 A(2) = 24 A(10) = 23 System.Console.WriteLine(A(0).ToString()) System.Console.WriteLine(A(10).ToString())System.Console.WriteLine(UBound(A).ToString()) System.Console.WriteLine(LBound(A).ToString()) Lbound和Ubound分别返回 0与10。ReDim和Fixed Array。你可以在VB6.0中指定固定长度的数组。
Dim ArrWeekDays(0 To 6) As Integer
这里的ArrWeekDays数组是固定长度的,不能用ReDim语句改变长度。VB.NET不支持固定长度数组,所以ReDim总是有效。
可以用下面两种方式声明数组: Dim ArrWeekDays(6) As IntegerDim ArrWeekDays() As Integer = {1, 2, 3, 4, 5, 6} ReDim语句。在VB6.0中,ReDim用于初始化动态数组。在VB.NET中你不能把它当作声明用。ReDim只能用于改变数组长度,不过不能改变数组维度。
Variant对阵ObjectVB6.0中的Variant数据类型能存储任意类型变量,VB.NET中Object具备相同能力。
算术操作符VB.NET支持类似C++的快捷方式。下面的表格显示了常规操作与快捷操作的不同之处。快捷方式也可用于*、/、|、等操作符。
操作符 常规语法 快捷方式加法 A = A+5 A +=5 减法 A = A – 5 A -+ 5固定长度字符串
在VB6.0中,可以在声明字符串时指定其长度。VB.NET不支持固定长度字符串。
布尔操作符VB6.0中的And、Or或是Xor语句是按位操作符。而在VB.NET中,它们是布尔操作符。执行这些操作将返回true或false。VB.NET引入新操作符来完成按位操作。
操作符 描述 BitAnd 按位AndBitOr 按位OrBitXor 按位XorBitNot 按位Not结构与自定义类型在VB6.0中,你使用Type…End Type语句块创建结构或自定义类型。例如:
Type StdRec
StdId As Integer
StdName As String End Type
VB.NET引入新的语法:Structure。Type…End Type不再被支持。Structure…End Structure与C++用法相同。可以指定结构中每个元素的可访问域,如Public、Protected、Friend、Protected Friend、Private等。例如:
Structure StdRec
Public StdId As Integer Public StdName As String
Private StdInternal As String End StructureVB.NET中的Structures就像类一样,也可以拥有方法和属性。New和Nothing关键字VB6.0中,AS New和Nothing关键字用于声明一个对象并初始化它。 VB.NET不支持隐式创建对象。如前所言,甚至连数据类型都是对象。你可以采用以下两种方法创建数据类型或对象: Dim i As Integer Dim i As Integer = New Integer() // Do something if i = Nothing Then End If 不支持Set语句VB6.0使用Set语句指派对象。例如:Set myObj = new MyObjectSet a = b在VB.NET中,不需要使用Set指派对象。例如:myObj = new MyObj()a = b过程(procedure)语法的变化在VB.NET中过程语法有了很多变化。例如类似C++的过程调用方式、ByVal(传值)为默认类型、Optional关键字、return语句等等。类似C++的过程调用方式 VB6.0允许不用加圆括号调用过程(sub)。不过,用Call语句调用函数或sub时,一定要使用圆括号。例如:Dim I as IntegerCall EvaluateData(2, i) EvaluateData 2, i 在VB.NET中,所有的方法调用都需要圆括号,而Call语句则是可选的。 ByVal是默认参数类型在VB6.0中,在调用函数或sub时ByRef(传址)是默认类型。那意味着所有改变将反映到传入的变量。VB.NET改变了这种方式。现在,默认的参数类型是ByVal(传值)。 Optional关键字VB6.0使用Optional关键字可用来让用户决定传入一个默认值,之后在调用IsMissing函数判断参数是否有效。 而在VB.NET中,每个可选参数必须声明其默认值,无需调用IsMissing函数。例如:Sub MyMethod(Optional ByVal i As Integer = 3)
Return语句VB.NET的Return语句与C++相似。使用Return语句把控制权从过程返还给调用者。在VB6.0中,Return语句与GoSub语句一起使用。VB.NET不再支持GoSub语句。流程控制的改变下面是VB.NET对流程控制语句的修改:1. GoSub不再受到支持。2. Call、Function和Sub语句均可用于调用过程。3. On ... GoSub和On ... GoTo语句不再受支持。可以使用Select Case语句来替代。4. While ... Wend语句现在改为While…End While语句。不再支持Wend关键字。小结 Visual Basic .NET是.NET版本的Visual Basic,已经从根本发生了变化!通过本文你了解到VB6.0和VB.NET的区别是很大的,可以说根本就是两种不同的语言,因为它们的内核发生了变化,VB6.0是基于COM而vb点虐 是基于点虐 框架的,因为这个变化,所以在构造类时也发生了根本性的变化。
请参考:
当我把一些旧的VB6项目转变成VS2008时会出现, “Declare”语句中不支持“As Any”的错误说明, 例如在National Instrument中有一个VBib-32.vb中有大量的这样一类的语句:
Declare Function ibcmda32 Lib "Gpib-32.dll" Alias "ibcmda" (ByVal ud AsInteger, ByRef sstr As Any, ByVal cnt As Integer) As Integer
如何办?
有两种办法:
一, 使用具体的参数
例如字符串, 就用string, 那么上面的ByRef as Any, 就写成ByRef as string, 或者
二, 使用特殊说明MarshalAsAttribute
System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny)
把这段语句放在相应之处, 然后把Any改成Object, 于是最上面的说明语句写成
Declare Function ibcmda32 Lib "Gpib-32.dll" Alias "ibcmda" (ByVal ud AsInteger, System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny) ByRef sstr As Object, ByVal cnt As Integer) As Integer
对于ByVal也是一样
Public Declare Function GetPrivateProfileString Lib "kernel32" Alias"GetPrivateProfileStringA" (ByVal lpApplicationName As String, System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny) ByVal lpKeyName As Object, ByVal lpDefault As String,ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName AsString) As Integer
MSDN官方说明, 可以简写成:
Declare Sub SetData Lib "..\LIB\UnmgdLib.dll" ( ByVal x As Short, MarshalAsAttribute(UnmanagedType.AsAny)ByVal o As Object)
这种方法本人实际使用, 可行.
VB真想不到之六:有用的技术和没用的指针
作者: AdamBear 来源:CSDN
关键字:VB、SafeArray、数值类型指针
难度:中级
参考文章:
1、2000年7月VBPJ Black Belt专栏文章《Modify a Varialbe's Pointer》
作者:Bill McCarthy
引言:
这真的是指针专题的最后一篇了(当然,以后肯定还会提到指针)。主要是来谈谈Bill McCarthy的文章《Modify a Varialbe's Pointer》的精华。关于这篇文章的东西,在我的《VB指针葵花宝典之SafeArray》里曾谈到过,但那篇文章实际上没有写出SafeArray的精华,用SafeArray最妙的地方在于可以将一个变量建在指定的内存处,就象上一篇文章给出的那个字串类一样。
正文:
Bill McCarthy在那篇《Modify a Varialbe's Pointer》里用SafeArray实现多进程的数组共享内存,他考虑了数组变量的类型,因此可以兼容大部分数值类型的数组,是一个非常不错的东西。我这里不讲它实现的具体方法,只是想和大家一起看看SafeArray还能做什么。
修改SafeArray结构的pvData指针却是一个非常有用的技术,通过修改pvData,就能够通过数组直接访问指定的内存。
和上一篇文章包装字串指针类一样,通过修改pvData,我们也可以包装一些普通数值类型变量的指针类。
我在指针的第一篇文章里说过,要想实现C语言里'*'这个取指针所指变量值功能,必须要用CopyMemory。实际上,我说错了,我们完全可以实现和C里一样的指针,如下:
//C语言
Long L;
Long* pL = L;
*pL = 12;
printf("L = %d *pL = %d", l, *pl);
'VB里
Dim pL As New pLong, L As Long
pL.Attach L
'也可以 pL.Ptr = VarPtr(L)
pL = 12
Debug.Print "L ="; L; " *pL ="; pL
结果都能够通过修改pL指针,达到修改变量L的目的。
上面VB代码里的pLong就是一个包装好的Long型变量的指针类,下面看看如何来实现它:
Option Explicit
'********************************************************
'pLong.cls
'包装一个Long型指针的类
'作者: 熊超 ID: AdamBear 2002年3月18日
'
' 你可以自由使用本类模块,不过请保留本声明
'********************************************************
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private m_Arr(0) As Long
'缺省属性
Public Property Get Data() As Long
Data = m_Arr(0)
End Property
Public Property Let Data(ByVal Value As Long)
m_Arr(0) = Value
End Property
Public Sub Attach(Target As Long)
Ptr = VarPtr(Target)
End Sub
Public Property Let Ptr(ByVal Target As Long)
Dim pSA As Long
'得到SafeArray结构指针pSA
CopyMemory pSA, ByVal VarPtrArray(m_Arr), 4
'这个指针偏移12个字节后就是pvData指针
CopyMemory ByVal (pSA + 12), Target, 4
End Property
Public Property Get Ptr() As Long
Ptr = m_SA.pvData
End Property
Private Sub Class_Terminate()
CopyMemory ByVal VarPtrArray(m_Arr), 0, 4
End Sub
要将它改成Byte的指针类,只需要将上面的代码中m_Arr数组的类型,Data属性和Attach方法中的参数类型改为Byte型即可。
当我们这样做出pLong、pByte、pInteger后,我们就能够玩点和C里一样的花样了。
Sub Main()
Dim pB As New pByte, B As Byte
Dim pI As New pInteger, I As Integer
Dim pL As New pLong, L As Long
'用Attach方法将经过类型检查,直接用Ptr属性则可以绕过类型检查
pB.Attach B
pI.Attach I
pL.Attach L
'试试指针
B = 1
Debug.Print "B ="; B; " *pB ="; pB
pB = 1
Debug.Print "B ="; B; " *pB ="; pB
I = 1000
Debug.Print "I ="; I; " *pI ="; pI
pI = 2000
Debug.Print "I ="; I; " *pI ="; pI
L = 40000
Debug.Print "L ="; L; " *pL ="; pL
pL = 60000
Debug.Print "L ="; L; " *pL ="; pL
'试试C里的类型转换
'用Integer指针访问Long型变量
pI.Ptr = VarPtr(L)
Debug.Print "*pI ="; pI
End Sub
搞出这几种普通数值类型的指针类有什么用?基本上没有什么大用。不过是证明一种方法的可行性,和演示技术。这种技术还有什么用,需要的时候还会再谈。
后记:
本文的东西,可见CSDN共享软件上的《内存共享和指针》,
指针的专题就到这儿了,下一篇准备开始着手写VB和COM的一个系列文章,其间我准备翻译一下《VB Design Patterns》,这是一本不错的书。
9:43 添加评论 固定链接 引用通告 (0) 记录它 VisualBASIC
固定链接
添加评论VB真想不到系列五
VB真想不到之五:高效字串指针类
作者:AdamBear 来源:CSDN
关键字:VB、HCAK、字串指针、BSTR、效率、内存共享
难度:中级或高级
参考文章:
1、2000年7月VBPJ Black Belt专栏文章《Modify a Varialbe's Pointer》
作者:Bill McCarthy
2、1998年4月VBPJ Black Belt专栏文章《Play VB's Strings》
作者:Francesco Balena
引言:
本想以内存共享做为VB指针专题的最后一篇,写着写着发现字串的问题应该单独谈谈。在内存共享的问题上,我尤其关心的是字串的共享,因为在我一个多月前发布的源码里用的是《HardCore VB》里Bruce Mckinney提供的CShareStr类,它实现了字串的内存共享。但是Bruce也没有突破局限,对字串的处理依然是CopyMemory的乾坤大挪移,尤其是还要进行讨厌的ANSI/DBCS和Unicode的转换。我在readme里说过它效率极低,应该采用Variant或Byte数组来实现,才能避免转换。后来又想到可以用StrPtr来做,并在VC里用DLL共享节实现了可以不进行转换的字串内存共享。不过在VC里我仍然需要用SysAllocString来建立VB能使用的BSTR。这都不是我想要的,我想要的东西要象VC里的CString的一样,只要字串够大,对其赋值就不用重新分配内存,还要象VC里CComBSTR类一样可以Attach到一个特定BSTR。
知道该怎么做,是在看了VBPJ上Bill McCarthy和Francesco Balena的两篇文章之后。Bill用修改SafeArray描述结构实现了数组的内存共享,而Francesco则对字串指针进行深入的探讨。但是Bill和Francesco的东西都没有实现我想要的字串类。
方法知道了,实现并不难,所以我决定自己来包装一个这样的东西。
正文:
使用VB里的字串类型String有两大不足:第一、它的分配是由VB运行时控制,我们不能将其分配在指定内存处;第二,任何一次对字串的赋值操作都要进行内存重新分配。要实现高效、灵活的字串处理,我们必须克服这两大不足。
对于第一个问题,通过修改String变量里放着的BSTR描述符指针可以实现;对于第二个问题,可以用Mid语句(注意是语句而不是函数)来赋值。不详细讲了,直接看下面的这个类:
Option Explicit
'********************************************************
'clsBSTR.cls
'作者: 熊超 ID: AdamBear 2002年3月18日
'
' 你可以自由使用本类模块,不过请保留本声明
'********************************************************
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
'不要直接对sString赋值(可以用MID语句),将其设为公有仅为提高效率。
Public sString As String 'BSTR描述符指针
Private pStr As Long 'BSTR地址
Private nMaxLen As Long 'BSTR最大字节数
'让本字串指向特定地址
Public Sub Attach(Addr As Long, Optional nLen As Long)
pStr = Addr
'修改BSTR描述符指针,使其指向Addr
CopyMemory ByVal VarPtr(sString), Addr, 4
If IsMissing(nLen) Then Exit Sub
'设定最大字串字节数
nMaxLen = nLen
End Sub
'还原本字串原BSTR描述符
Public Sub Detach()
CopyMemory ByVal VarPtr(sString), 0, 4
End Sub
'让本字串指向源字串
Public Sub AttachStr(sStr As String)
Attach StrPtr(sStr), LenB(sStr)
End Sub
'data为缺省属性
Public Property Let data(sVal As String)
Dim c As Long
c = LenB(sVal)
'超过最大字串数,抛出错误。
If c nMaxLen Then Err.Raise vbObjectError + 3000, _
"CString::Let Data", "溢出"
'写字串长度
CopyMemory ByVal (pStr - 4), c, 4
'写字串
Mid(sString, 1) = sVal
End Property
'可以通过公有变量sString来读字串,效率更高
Public Property Get data() As String
data = sString
End Property
Private Sub Class_Terminate()
Call Detach
End Sub
用法如下,假设我们已通过VitualAlloc,HeapAlloc,MapViewOfFile这样的内存管理API得到了一个4k个字节的可读写的内存地址baseAddr:
Dim sShare As New clsBSTR
'留下前4个字节用于BSTR保存字串字节数
sShare.Attach(baseAddr+4, 4096-4)
'下面的字串"Test"会直接写到baseAddr+4字节处
sShare = "Test"
Dim y As String
'读字串时可以用sString属性或缺省属性
y = sShare.sString
'用AttachStr方法Attach到一个字串。
'必须要先Detach
sShare.Detach
sShare.AttachStr(y)
sShare = "Hahaha"
Debug.Print y
'一旦AttachStr到字串y后,对sShare的修改就相当于对y的修改。
'并且以后对y的修改也只能用Mid语句
Mid(y, 1) = "xxxxx"
'不能直接赋值,这样VB会将原来y所指(也是sShare所指)内存释放,
' 重新分配y。这样在访问sShare时会出错。
'y = "Test"
我也不在这里讲这个类的详细原理,可以参考我前面说的两篇文章。
使用这个类有几个需要注意的地方。
1、读字串时可以用sString属性来读,更快。
读sShare有两种方法,一种是用缺省属性Data来读,一种是直接用sString属性来读。用sString属性不重新分配内存,要快得多。
2、不要直接给sString赋值,应使用缺省的data属性来赋值。
之所以把sString属性暴露出来,是为了效率和方便。我们可以用Mid语句对其进行修改,但不要直接用"="来赋值。
3、注意Attach的第二个参数,表示字串的最大字节数,不要让它超过已经分配的内存。
4、用AttachStr将本字串对象Attach到某个字串(比如上面的y)上后,不能再对这个字串y重新赋值,也不能将其传递到会对其重新赋值的过程。
哇,这么多需要注意的问题,用起来岂不是更不方便。的确,用它的之前要考虑是不是必须的。因为建立这个类也一样有开销。所以还有一个需要注意的问题:
5、它主要的应用还是在于将字串安放在指定内存处。虽然它也可以让同一个进程内几个的字串达到共享的目的,但是如果只是两三个很小的字串这样时做反而慢了。
后计:
数组指针和字串指针我们已经谈过了,对于普通的数值类型变量的指针没有什么Hack的必要,但是它关系到一个有用的技术,下篇文章再谈。
本文和下篇文章的代码,以及用这个类来实现的共享内存的代码,我会发布到CSDN共享软件上,名字是《内存共享和指针》。
9:38 添加评论 固定链接 引用通告 (0) 记录它 VisualBASIC
固定链接
添加评论VB真想不到系列四
VB真是想不到系列之四:VB指针葵花宝典之SafeArray
作者:AdamBear 来源:CSDN