谁能帮我做一个vb编程教程,要比较复杂一点的,必须自己做的哦,亲

做一个JAVA 桌面程序,但要和VB合作,给点建议把
- ITeye问答
我是写WEB代码的,可最近突然有个外行朋友让我帮忙做个桌面的小程序,实话:1,没干过。2.应下来了。
想请教个问题,java 怎么跟VB打交道呢,比如,VB算了个1+1,结果怎么给JAVA呢。。。
直接让 VB写的代码成DLL,然后JNI调用。
使用WebService和socket都可以实现。WebService是系统对外的接口,一般都是对外发布数据,如天气预报那种在线服务,只有一个程序请求它的服务就可以拿到数据。至于socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。我之前在一家游戏公司工作的时候,项目就是用java socket与底层的C++ 程序通讯的。你要做java桌面程序的话,可以用swing和awt做程序,用java socket 做后台服务程序,这个既可以完成桌面客户端与后台服务的通讯也可以完成与其他程序之间的通讯。学习的时候做过一个模拟股票分析的小程序就是用的这两项技术。
你可以看一下webservice
既然用vb了,肯定运行在windows上了,就可以放心大胆的掉API了
一般FindWindow+PostMessage就行了
跟 @blogzhoubo 思路一样
核心功能写成jar包, 上层采用commandline调用. VB同理.
Linux 就是采用这种逻辑. 如果一定要用JAVA和VB通讯用Socket发二进制数也可以.
1.VB程序通过命令行接收参数,然后把执行结果通过屏幕输出返回。
2.java程序中用Runtime.getRuntime().exec(),调用vb生成的可执行文件,同时把参数也一起传给vb可执行程序。
3.从返回的Process中获得inputstream,从输入流中取得vb的返回结果
public Process exec(String[] cmdarray,
&&& String[] envp)
详细用法可以参照下面的例子:
webservice?
已解决问题
未解决问题谁能给我写一个vb小程序,编写一个函数,求1-N(正整数)的所有正整数之和,
谁能给我写一个vb小程序,编写一个函数,求1-N(正整数)的所有正整数之和,用按钮和编辑框形式表达出来,
Private Sub Command1_Click() n = Val(Text1.Text) For i = 1 To n s = s + i Next i Text2.Text = sEnd Su
我有更好的回答:
剩余:2000字
与《谁能给我写一个vb小程序,编写一个函数,求1-N(正整数)的所有正整数之和,》相关的作业问题
是VB,就做界面.先做一个窗体,其上有四个文本框,text1、text2、text3、text4依次输入边长a、b、角A的值,在text4中输出面积,你可以在前面加上标签说明再加一个命令按钮command1,点击过程中写下程序如下:sub command1_click()text4.text=1/2*text1.tex
这回完全符合你的题意了.结果确实如你所给新建一个exe工程,画一个text文本框,一个按钮.text文本框的multiline=true代码如下'Option ExplicitPrivate Sub Command1_Click()On Error GoTo writeFile:Dim CacheA As String
你可以试一下Public&access&As&New&ADODB.ConnectionPublic&res&As&New&ADODB.RecordsetPrivate&Sub&Command1_Click()Dim&nb
Private Sub Command1_Click()Dim i As LongDim s As StringDim r As StringDim F As BooleanFor i = 1 To Len(Text1.Text)s = Mid(Text1.Text,i,1)If s = ":" ThenF = Tru
I have a full weekend plan.Saturday morning, I will read some books that I like.And then after lunch I'll watch the animal world.On Sunday I'll go swimming with
Preparation for visiting my former instructor This weekend will be my instructor's birthday. My friends and I would like to go to his home to
import&javax.swing.*;import&javax.swing.table.*;import&java.awt.*;import&java.applet.*;import&java.awt.event.ActionLimport&nbsp
a = InputBox("请输入a")b = InputBox("请输入b")c = InputBox("请输入c")If a < b Then t = a a = b b = tEnd IfIf a < c Then t = a a = c c = tEnd IfIf b < c Then t = b b = c
I want to go to Harbinbecause there lot of fun. I will take a plane to next week and play ski, do ice. It‘s well be fun!我想去哈尔滨,因为那里很好玩.我会在下个星期坐飞机去,玩一玩滑雪、做冰雕.这一定
Sub Main() MsgBox Func(100, 2, 5) '按照要求写的例子End Sub Rem 参数(最大值, 被整除的数的数组)Function Func(n&, ParamArray x()) Dim i&, b& For i = 1 To n b = 0 Dim j& For j = LBound(
Private Sub btnstart_Click()'按要求写了If-Then语句,不过个人认为Select-Case比较适合这个.If Me.txbplanet.Text = "Jupiter" Then 'Format格式化结果,保留2位小数. Me.lbltime.Caption = "光从太阳到金星的时间为
sub test()dim ii = textbox1.valueif(i mod 2 = 0) thenlabel1.caption = “偶数”eslelabel1.caption = "奇数"end if end sub textbox1 label1是自己添加的文本框和标签
Private&Sub&Command2_Click()&Dim&x&As&Double,&y&As&Double,&ang&&&&&As&Double&nbs
从前,森林王国里有一只可爱的小兔,叫贝贝.她原来是一只讲卫生的小兔子,但是她现在好像变了,兔妈妈好几次提醒贝贝,她都说:“我知道了,别烦我!”兔妈妈只好生气地走了.时间一点一点地过去了,小兔贝贝也渐渐地长大了,可是她的坏毛病并没改掉.兔妈妈老了,生病了,再也没法提醒她的女儿.贝贝不喜欢走路,所以她就把一袋袋臭臭的垃圾往
My favourite food  I like many food:chicken、 duck 、 fish 、beef 、vegetables……  I like duck,because it's beautiful and delicious.I like peas,pea flowers are white
Dim a As Integer '首项Dim b As Integer '末项Dim c As Integer '项数Dim d As Integer '结果Public Sub Add()a = InputBox("首项","")b = InputBox("末项","")c = InputBox("项数","")d
再过五分钟就是晚上7时了,灯火通明的教室里一个孩子突然问道:“老师,我作业都已经做完了,现在能开始了吗?”这一问得到了大多数同学的附和,也让1、2个正埋头做作业的同学的眼中露出了羡慕的眼神,他们也很想参加,可自己的作业还没完成呀!孩子们是想要在四角拼图、五子棋、24点的游戏中来一次脑力震荡,享受一下动脑的快乐,这些可是
我去过少林寺,龙门石窟,平遥古城,青岛等地,印象最深是平遥, 1.慕名已久,非常向往2.启程顺利到达3.游览古城4.感想请大家帮帮忙,最好是讲详细的,我可是初次接触程序,还有我的英语水平也是一般般,但我很想学。我的VB软件是企业版6。0的。
学习相关信息请先找本VB方面的参考消书如"VISUAL STUDIO 6.0 入门和提高",清华大学出版社出版的.照书中的介绍,一步一个脚印的实践,如有MICROSOFT VISUAL STUDIO 6.0 MSDN文档资料,网上有下载的.下附一个简单的屏保程序源代码供参考.
Pivate Su Fom_KeyDown(KeyCode As Intege, Shift As Intege)
Unload Me '关闭程序
End Su
Pivate Su Fom_KeyPess(KeyAscii As Intege)
Unload Me '关闭程序
End Su
Pivate Su Fom_Load()
Time2.Enaled = Not Time2.Enaled
Lael1.Caption = "IASK"
End Su
Pivate Su Fom_MouseDown(Button As Intege, Shift As Intege, X As Single, Y As Single)
Unload Me '关闭程序
En...
学习相关信息请先找本VB方面的参考消书如"VISUAL STUDIO 6.0 入门和提高",清华大学出版社出版的.照书中的介绍,一步一个脚印的实践,如有MICROSOFT VISUAL STUDIO 6.0 MSDN文档资料,网上有下载的.下附一个简单的屏保程序源代码供参考.
Pivate Su Fom_KeyDown(KeyCode As Intege, Shift As Intege)
Unload Me '关闭程序
End Su
Pivate Su Fom_KeyPess(KeyAscii As Intege)
Unload Me '关闭程序
End Su
Pivate Su Fom_Load()
Time2.Enaled = Not Time2.Enaled
Lael1.Caption = "IASK"
End Su
Pivate Su Fom_MouseDown(Button As Intege, Shift As Intege, X As Single, Y As Single)
Unload Me '关闭程序
End Su
Pivate Su Time1_Time()
Lael1.FontName = "楷体_GB2312"
Lael1.FontSize = Rnd * 48
If Lael1.FontSize
10 Then Lael1.FontSize = 48
Lael1.FoeColo = QBColo(Int(16 * Rnd))
fmSplash.BackColo = QBColo(Int(16 * Rnd * Rnd))
Lael1.BackColo = fmSplash.BackColo
End Su
Pivate Su Time2_Time()
Lael1.Left = Lael1.Left - 15
Lael1.Top = Lael1.Top + 150
If Lael1.Left = 0 Then
Lael1.Left = Width
If Lael1.Top = 8000 Then
Lael1.Top = 0
End If
End Su
其他答案(共1个回答)
编程吧,易语言,
我是计算机系的学生
我学的第一种语言是 C语言
我也建议你学C
语言分很多种:机器语言 汇编语言 高级语言
VB和C 都属于高级语言
VB是面向对象的...
Private Declare Function SetCursorPos Lib &#034;user32&#034; (ByVal x As Long, ByVal y As ...
在VB开发环境中的单击“工程”→“部件”对话框中,添加MediaPlayer控件。如果要播放MP3,则至少要6.01以上版本的MediaPlayer控件(Win...
d = 100 * a + b * 10 + c,是C 不是D
Option Explicit
Dim Op1, Op2
&#039; 预先输入操作数。
Dim DecimalFlag As Intege...
答: richtext控件可以实现的
text 是没有这个功能的!
答: 还是买本参考资料看看吧,如果三言两语能描述清楚,种地的也编程了。
答: '不需要任何控件,需要的是如下代码:
'因为您的分值为0,并且考虑到您并不一定会采纳,所以请原谅小的不写注释.以下代码只帖出两天,若您觉得没用,那小的自会删除,...
大家还关注
Copyright &
Corporation, All Rights Reserved
确定举报此问题
举报原因(必选):
广告或垃圾信息
激进时政或意识形态话题
不雅词句或人身攻击
侵犯他人隐私
其它违法和不良信息
报告,这不是个问题
报告原因(必选):
这不是个问题
这个问题分类似乎错了
这个不是我熟悉的地区
相关问答:123456789101112131415《VB真是想不到系列》
每次看大师的东西到了精彩之处,我就会拍案叫绝:"哇噻,真是想不到!"。在经过很多次这种感慨之后,我发现只要我们动了脑筋,我们自己也能有让别人想不到的东西。于是想到要把这些想不到的东拿出来和大家一起分享,希望抛砖引玉,能引出更多让人想不到的东西。 本系列文章可见:
http://www.csdn.net/develop/list_article.asp?author=AdamBear
VB真是想不到系列之三:VB指针葵花宝典之函数指针 关键字:VB、HCAK、指针、函数指针、效率、数组、对象、排序 难度:中级 要求:熟悉VB,了解基本的排序算法,会用VC更好。 引言:
不知大家在修习过本系列第二篇《VB指针葵花宝典》后有什么感想,是不是觉得宝典过于偏重内功心法,而少了厉害的招式。所以,今天本文将少讲道理,多讲招式。不过,还是请大家从名门正派的内功心法开始学起,否则会把九阴真经练成九阴白骨爪。
今天,我们重点来谈谈函数指针的实际应用。
接着上一篇文章,关于字串的问题,听CSDN上各位网友的建议,我不准备写什么《VB字符串全攻略》了,关于BSTR的结构,关于调用API时字串在UNICODE和ANSI之间的转换问题,请参考MSDN的Partial Books里的《Win32 API Programming with Visual Basic》里的第六章《Strings》。今天就让我们先忘掉字符串,专注于函数指针的处理上来。
一、函数指针
AddressOf得到一个VB内部的函数指针,我们可以将这个函数指针传递给需要回调这个函数的API,它的作用就是让外部的程序可以调用VB内部的函数。
但是VB里函数指针的应用,远不象C里应用那么广泛,因为VB文档里仅介绍了如何将函数指针传递给API以实现回调,并没指出函数指针诸多神奇的功能,因为VB是不鼓励使用指针的,函数指针也不例外。
首先让我们对函数指针的使用方式来分个类。
1、回调。这是最基本也是最重要的功能。比如VB文档里介绍过的子类派生技术,它的核心就是两个API:SetWindowLong和CallWindowProc。
我们可以使SetWindowLong这个API来将原来的窗口函数指针换成自己的函数指针,并将原来的窗口函数指针保存下来。这样窗口消息就可以发到我们自己的函数里来,并且我们随时可以用CallWindowProc来调用前面保存下来的窗口指针,以调用原来的窗口函数。这样,我们可以在不破坏原有窗口功能的前提下处理钩入的消息。
具体的处理,我们应该很熟悉了,VB文档也讲得很清楚了。这里需要注意的就是CallWindowProc这个API,在后面我们将看到它的妙用。
在这里我们称回调为让"外部调用内部的函数指针"。
2、程序内部使用。比如在C里我们可以将C函数指针作为参数传递给一个需要函数指针的C函数,如后面还要讲到的C库函数qsort,它的声明如下:
#define int (__cdecl *COMPARE)(const void *elem1, const void *elem2)
void qsort(void *base, size_t num, size_t width,
COMPARE pfnCompare); 它需要一个COMPARE类型函数指针,用来比较两个变量大小的,这样排序函数可以调用这个函数指针来比较不同类型的变量,所以qsort可以对不同类型的变量数组进行排序。
我们姑且称这种应用为"从内部调用内部的函数指针"。
3、调用外部的函数
也许你会问,用API不就是调用外部的函数吗?是的,但有时候我们还是需要直接获取外部函数的指针。比如通过LoadLibrary动态加载DLL,然后再通过GetProcAddress得到我们需要的函数入口指针,然后再通过这个函数指针来调用外部的函数,这种动态载入DLL的技术可以让我们更灵活的调用外部函数。
我们称这种方式为"从内部调用外部的函数指针"
4、不用说,就是我们也可控制"从外部调用外部的函数指针"。不是没有,比如我们可以加载多个DLL,将其中一个DLL中的函数指针传到另一个DLL里的函数内。
上面所分的"内"和"外"都是相对而言(DLL实际上还是在进程内),这样分类有助于以后我们谈问题,请记住我上面的分类,因为以后的文章也会用到这个分类来分析问题。
函数指针的使用不外乎上面四种方式。但在实际使用中却是灵活多变的。比如在C++里继承和多态,在COM里的接口,都是一种叫vTable的函数指针表的巧妙应用。使用函数指针,可以使程序的处理方式更加高效、灵活。
VB文档里除了介绍过第一方式外,对其它方式都没有介绍,并且还明确指出不支持“Basic 到 Basic”的函数指针(也就是上面说的第二种方式),实际上,通过一定的HACK,上面四种方式均可以实现。今天,我们就来看看如何来实现第二种方式,因为实现它相对来说比较简单,我们先从简单的入手。至于如何在VB内调用外部的函数指针,如何在VB里通过处理vTable接口函数指针跳转表来实现各种函数指针的巧妙应用,由于这将涉及COM内部原理,我将另文详述。
其实VB的文档并没有说错,VB的确不支持“Basic 到 Basic”的函数指针,但是我们可以绕个弯子来实现,那就是先从"Basic到API",然后再用第一种方式"外部调用内部的函数指针"来从"API到BASIC",这样就达到了第二种方式从"Basic 到 Basic"的目的,这种技术我们可以称之为"强制回调",只有VB里才会有这种古怪的技术。
说得有点绕口,但是仔细想想窗口子类派生技术里CallWindowProc,我们可以用CallWindowProc来强制外部的操作系统调用我们原来的保存的窗口函数指针,同样我们也完全可以用它来强制调用我们内部的函数指针。
呵呵,前面说过要少讲原理多讲招式,现在我们就来开始学习招式吧!
考虑我们在VB里来实现和C里一样支持多关键字比较的qsort。完整的源代码见本文配套代码,此处仅给出函数指针应用相关的代码。
'当然少不了的CopyMemory,不用ANY的版本。
Declare Sub CopyMemory Lib "kernel32" Alias _ "RtlMoveMemory" (ByVal dest As Long, ByVal source As Long, _
ByVal numBytes As Long)
'嘿嘿,看下面是如何将CallWindowProc的声明做成Compare声明的。
Declare Function Compare Lib "user32" Alias _ "CallWindowProcA" (ByVal pfnCompare As Long, ByVal pElem1 As Long, _
ByVal pElem2 As Long, ByVal unused1 As Long, _
ByVal unused2 As Long) As Integer '注:ByVal xxxxx As Long ,还记得吧!这是标准的指针声明方法。
'声明需要比较的数组元素的结构
Public Type TEmployee
Name As String
Salary As Currency
'再来看看我们的比较函数
'先按薪水比较,再按姓名比较
Function CompareSalaryName(Elem1 As TEmployee, _
Elem2 As TEmployee, _
unused1 As Long, _
unused2 As Long) As Integer
Dim Ret As Integer
Ret = Sgn(Elem1.Salary - Elem2.Salary)
If Ret = 0 Then
Ret = StrComp(Elem1.Name, Elem2.Name, vbTextCompare)
CompareSalaryName = Ret
End Function
'先按姓名比较,再按薪水比较
Function CompareNameSalary(Elem1 As TEmployee, _
Elem2 As TEmployee, _
unused1 As Long, _
unused2 As Long) As Integer
Dim Ret As Integer
Ret = StrComp(Elem1.Name, Elem2.Name, vbTextCompare)
If Ret = 0 Then
Ret = Sgn(Elem1.Salary - Elem2.Salary)
CompareNameSalary = Ret
End Function
最后再看看我们来看看我们最终的qsort的声明。
Sub qsort(ByVal ArrayPtr As Long, ByVal nCount As Long, _
ByVal nElemSize As Integer, ByVal pfnCompare As Long)
上面的ArrayPtr是需要排序数组的第一个元素的指针,nCount是数组的元素个数,nElemSize是每个元素大小,pfnCompare就是我们的比较函数指针。这个声明和C库函数里的qsort是极为相似的。
和C一样,我们完全可以将Basic的函数指针传递给Basic的qsort函数。
使用方式如下:
Dim Employees(1 To 10000) As TEmployee
'假设下面的调用对Employees数组进行了赋值初始化。
Call InitArray()
'现在就可以调用我们的qsort来进行排序了。
Call qsort(VarPtr(Employees(1)), UBound(Employees), _
LenB(Employees(1)), AddressOf CompareSalaryName)
'或者先按姓名排,再按薪水排
Call qsort(VarPtr(Employees(1)), UBound(Employees), _
LenB(Employees(1)), AddressOf CompareNameSalary)
聪明的朋友们,你们是不是已经看出这里的奥妙了呢?作为一个测验,你能现在就给出在qsort里使用函数指针的方法吗?比如现在我们要通过调用函数指针来比较数组的第i个元素和第j个元素的大小。
没错,当然要使用前面声明的Compare(其实就是CallWindowProc)这个API来进行强制回调。
具体的实现如下:
Sub qsort(ByVal ArrayPtr As Long, ByVal nCount As Long, _
ByVal nElemSize As Integer, ByVal pfnCompare As Long)
Dim i As Long, j As Long
'这里省略快速排序算法的具体实现,仅给出比较两个元素的方法。
If Compare(pfnCompare, ArrayPtr + (i - 1) * nElemSize, _
ArrayPtr + (j - 1) * nElemSize, 0, 0) & 0 Then
'如果第i个元素比第j个元素大则用CopyMemory来交换这两个元素。
招式介绍完了,明白了吗?我再来简单地讲解一下上面Compare的意思,它非常巧妙地利用了CallWindowProc这个API。这个API需要五个参数,第一个参数就是一个普通的函数指针,这个API能够强马上回调这个函数指针,并将这个API的后四个Long型的参数传递给这个函数指针所指向的函数。这就是为什么我们的比较函数必须要有四个参数的原因,因为CallWindowProc这个API要求传递给的函数指针必须符合WndProc函数原形,WndProc的原形如下:
LRESULT (CALLBACK* WNDPROC) (HWND, UINT, WPARAM, LPARAM);
上面的LRESULT、HWND、UINT、WPARAM、LPARAM都可以对应于VB里的Long型,这真是太好了,因为Long型可以用来作指针嘛!
再来看看工作流程,当我们用AddressOf CompareSalaryName做为函数指针参数来调用qsort时,qsort的形参pfnCompare被赋值成了实参CompareSalaryName的函数指针。这时,调用Compare来强制回调pfnCompare,就相当于调用了如下的VB语句:
Call CompareSalaryName(ArrayPtr + (i - 1) * nElemSize, _
ArrayPtr + (j - 1) * nElemSize, 0, 0)
这不会引起参数类型不符错误吗?CompareSalaryName的前两个参数不是TEmployee类型吗?的确,在VB里这样调用是不行的,因为VB的类型检查不会允许这样的调用。但是,实际上这个调用是API进行的回调,而VB不可能去检查API回调的函数的参数类型是一个普通的Long数值类型还是一个结构指针,所以也可以说我们绕过了VB对函数参数的类型检查,我们可以将这个Long型参数声明成任何类型的指针,我们声明成什么,VB就认为是什么。所以,我们要小心地使用这种技术,如上面最终会传递给CompareSalaryName函数的参数"ArrayPtr + (i - 1) * nElemSize"只不过是一个地址,VB不会对这个地址进行检查,它总是将这个地址当做一个TEmployee类型的指针,如果不小心用成了"ArrayPtr + i * nElemSize",那么当i是最后一个元素时,我们就会引起内存越权访问错误,所以我们要和在C里处理指针一样注意边界问题。
函数指针的巧妙应用这里已经可见一斑了,但是这里介绍的方法还有很大的局限性,我们的函数必须要有四个参数,更干净的做法还是在VC或Delphi里写一个DLL,做出更加符合要求的API来实现和CallWindowProc相似的功能。我跟踪过CallWindowProc的内部实现,它要做许多和窗口消息相关的工作,这些工作在我们这个应用中是多余的。其实实现强制回调API只需要将后几个参数压栈,再call第一个参数就行了,不过几条汇编指令而已。
正是因为CallWindowProc的局限性,我们不能够用它来调用外部的函数指针,以实现上面说的第三种函数指针调用方式。要实现第三种方式,Matt Curland大师提供了一个噩梦一般的HACK方式,我们要在VB里凭空构造一个IUnknown接口,在IUnknown接口的vTable原有的三个入口后再加入一个新入口,在新入口里插入机器代码,这个机器代码要处理掉this指针,最后才能调用到我们给的函数指针,这个函数指针无论是内部的还是外部的都一样没问题。在我们深入讨论COM内部原理时我会再来谈这个方法。
另外,排序算法是个见仁见智的问题,我本来想,在本文提供一个最通用性能最好的算法,这种想法虽好,但是不可能有在任何情况下都“最好”的算法。本文提供的用各种指针技术来实现的快速排序方法,应该比用对象技术来实现同样功能快不少,内存占用也少得多。可是就是这个已经经过了我不少优化的快速排序算法,还是比不了ShellSort,因为ShellSort实现上简单。从算法的理论上来讲qsort应该比ShellSort平均性能好,但是在VB里这不一定(可见本文配套代码,里面也提供了VBPJ一篇专栏的配套代码ShellSort,非常得棒,本文的思想就取自这个ShellSort)。
但是应当指出无论是这里的快速排序还是ShellSort,都还可以大大改进, 因为它们在实现上需要大量使用CopyMemroy来拷贝数据(这是VB里使用指针的缺点之一)。其实,我们还有更好的方法,那就是Hack一下VB的数组结构,也就是COM自动化里的SafeArray,我们可以一次性的将SafeArray里的各个数组元素的指针放到一个long型数组里,我们无需CopyMemroy,我们仅需交换Long型数组里的元素就可以达到实时地交换SafeArray数组元素指针的目的,数据并没有移动,移动的仅仅是指针,可以想象这有快多。在下一篇文章《VB指针葵花宝典之数组指针》中我会来介绍这种方法。
我学习所以我快乐。 VB真是想不到系列之四:VB指针葵花宝典之SafeArray 关键字:VB、HCAK、指针、SafeArray、数组指针、效率、数组、排序 难度:中级或高级 要求:熟悉VB,了解基本的排序算法,会用VC更好。 引言:
上回说到,虽然指针的运用让我们的数组排序在性能上有了大大的提高,但是CopyMemory始终是我们心里一个挥之不去的阴影,因为它还是太慢。在C里我们用指针,从来都是来去自如,随心所欲,四两拨千斤;而在VB里,我们用指针却要瞻前顾后,哪怕一个字节都要用到CopyMemory乾坤大挪移,真累。今天我们就来看看,能不能让VB里的指针也能指哪儿打哪儿,学学VB指针的凌波微步。
各位看官,您把茶端好了。
一、帮VB做点COM家务事
本系列开张第一篇里,我就曾说过VB的成功有一半的功劳要记到COM开发小组身上,COM可是M$公司打的一手好牌,从OLE到COM+,COM是近十年来M$最成功技术之一,所以有必要再吹它几句。
COM组件对象模型就是VB的基础,Varinat、String、Current、Date这些数据类型都是COM的,我们用的CStr、CInt、CSng等Cxxx函数根本就是COM开发小组写的,甚至我们在VB里用的数学函数,COM里都有对应的VarxxxDiv、VarxxxAdd,VarxxxAbs。嘿嘿,VB开发小组非常聪明。我们也可以说COM的成功也有VB开发小组和天下无数VB程序员的功劳,Bill大叔英明地将COM和VB捆绑在一起了。
所以说,学VB而不需要了解COM,你是幸福的,你享受着VB带给你的轻松写意,她把那些琐碎的家务事都干了,但同时你又是不幸的,因为你从来都不曾了解你爱的VB,若有一天VB对你发了脾气,你甚至不知该如何去安慰她。所以,本系列文章将拿出几大篇来教大家如何帮VB做点COM方面的家务事,以备不时之需。
想一口气学会所有COM家务事,不容易,今天我们先拿数组来开个头,更多的技术我以后再一一道来。
二、COM自动化里的SafeArray
就象洗衣机、电饭堡、吹尘器,VB洗衣服、做饭、打扫卫生都会用到COM自动化。它包含了一切COM里通用的东西,所有的女人都能用COM自动化来干家务,无论是犀利的VC、温柔的VB、还是小巧的VBScript,她们都能用COM自动化,还能通过COM自动化闲话家常、交流感情。这是因为COM自动化提供了一种通用的数据结构和数据转换传递的方式。而VB的数据结构基本上就是COM自动化的数据结构,比如VB里的数组,在COM里叫做SafeArray。所以在VB里处理数组时我们要清楚的知道我们是在处理SafeArray,COM里的一种安全的数组。
准备下厨,来做一道数组指针排序的菜,在看主料SafeArray的真实结构这前,先让我们来了解一下C里的数组。
在C和C++里一个数组指针和数组第一个元素的指针是一回事,如对下:
#include &iostream&
int main() {
int a[10];
cout && "a = " && a &&
cout && "&a[0] =" && &a[0] &&
可以看到结果a和&a[0]是相同的,这里的数组是才数据结构里真实意义上的数组,它们在内存里一个接着一个存放,我们通过第一个元素就能访问随后的元素,我们可以称这样的数组为"真数组"。但是它不安全,因为我们无法从这种真数组的指针上得知数组的维数、元素个数等非常重要的信息,所以也无法控制对这种数组的访问。我们可以在C里将一个二维数组当做一维数组来处理,我们还可以通过一个超过数组大小的索引去访问数组外的内存,但这些都是极不安全的,数组边界错误可以说是C里一个非常容易犯却不易发现的错误。
因此就有了COM里的SafeArray安全数组来解决这个问题,在VB里我们传递一个数组时,传递的实际上COM里的SafeAraay结构指构的指针,SafeAraay结构样子如下:
Private Type SAFEARRAY
cDims As Integer
'这个数组有几维?
fFeatures As Integer
'这个数组有什么特性?
cbElements As Long
'数组的每个元素有多大?
cLocks As Long
'这个数组被锁定过几次?
pvData As Long
'这个数组里的数据放在什么地方?
'rgsabound() As SFArrayBOUND
紧接在pvData这后的rgsabound是个真数组,所以不能在上面的结构里用VB数组来声明,记住,在VB里的数组都是SafeArray,在VB里没有声明真数组的方法。
不过这不是问题,因为上面SFArrayBOUND结构的真数组在整个SAFEARRAY结构的位置是不变的,总是在最后,我们可以用指针来访问它。SFArrayBOUND数组的元素个数有cDims个,每一个元素记录着一个数组维数的信息,下面看看它的样子:
Private Type SAFEARRAYBOUND
cElements As Long
'这一维有多少个元素?
lLbound As Long
'它的索引从几开始?
还有一个东西没说清,那就是上面SAFEARRAY结构里的fFeatures,它是一组标志位来表示数组有那些待性,这些特性的标志并不需要仔细的了解,本文用不上这些,后面的文章用到它们时我会再来解释。
看了上面的东西,各位一定很头大,好在本文的还用不了这么多东西,看完本文你就知道其实SafeArray也不难理解。先来看看如下的声明:
Dim MyArr(1 To 8, 2 To 10) As Long
这个数组做为SafeArray在内存里是什么样子呢?如图一: cDims = 2 fFeatures = FADF_AUTO AND FADF_FIXEDSIZE 位置 0 cbElements = 4
LenB(Long) 4 cLocks = 0 8 pvData(指向真数组) 12 rgsabound(0).cElements = 8 16 rgsabound(0).lLbound = 1 18 rgsabound(1).cElements = 9 22 rgsabound(1).lLbound = 2 26 cDims = 2 fFeatures = FADF_AUTO AND FADF_FIXEDSIZE 位置 0 cbElements = 4
LenB(Long) 4 cLocks = 0 8 pvData(指向真数组) 12 rgsabound(0).cElements = 8 16 rgsabound(0).lLbound = 1 18 rgsabound(1).cElements = 9 22 rgsabound(1).lLbound = 2 26 图一 :SafeArray内存结构 cDims表示它是个2维数组,sFeatures表示它是一个在堆栈里分配的固定大小的数组,cbElements表示它的每个元素大小是Long四个字节,pvData指向真的数组(就是上面说的C里的数组),rgsabound这个真数组表明数组二个维的大小和每个维的索引开始位置值。
先来看看从这个上面我们能做些什么,比如要得到一个数组的维数,在VB里没有直接提供这样的方法,有一个变通的方法是通过错误捕获如下:
On Error Goto BoundsError
For I = 1 To 1000
'不会有这么多维数的数组
lTemp = LBound(MyArr, I)
Next BoundErro:
nDims = I - 1
MsgBox "这个数组有" & nDims & "维"
现在我们知道了SafeArray的原理,所以也可以直接得到维数,如下:
'先得到一个指向SafeArray结构的指针的指针,原理是什么,我后面说。
ppMyArr = VarPtrArray(MyArr)
'从这个指针的指针得到SafeArray结构的指针
CopyMemory pMyArr, ByVal ppMyArr, 4
'再从这个指针所指地址的头两个字节取出cDims
CopyMemory nDims, ByVal pMyArr, 2
MsgBox "这个数组有" & nDims & "维"
怎么样,是不是也明白了LBound实际上是SafeArray里的rgsabound的lLbound,而UBound实际上等于lLbound +cElements - 1,现在我提个问,下面iUBound应该等于几?
Dim aEmptyArray() As Long
iUBound = UBound(aEmptyArray)
正确的答案是-1,不奇怪,lLbound -cElements - 1 = 0 - 0 - 1 = -1
所以检查UBound是不是等于-1是一个判断数组是不是空数组的好办法。
还有SafeArray结构里的pvData指向存放实际数据的真数组,它实际就是一个指向真数组第一个元素的指针,也就是说有如下的等式:
pvDate = VarPtr(MyArr(0))
在上一篇文章里,我们传给排序函数的是数组第一个元素的地址VarPtr(xxxx(0)),也就是说我们传的是真数组,我们可以直接在真数组上进行数据的移动、传递。但是要如何得到一个数组SafeArray结构的指针呢?你应该注意到我上面所用的VarPtrArray,它的声明如下:
Declare Function VarPtrArray Lib "msvbvm60.dll" _
Alias "VarPtr" (Var() As Any) As Long
它就是VarPtr,只不过参数声明上用的是VB数组,这时它返回来的就是一个指向数组SafeArray结构的指针的指针。因为VarPtr会将传给它的参数的地址返回,而用ByRef传给它一个VB数组,如前面所说,实际上传递的是一个SafeArray结构的指针,这时VarPtrArray将返回这个指针的指针。所以要访问到SafeArray结构需要,如下三步: 用VarPtrArray返回ppSA,再通过ppSA得到它指向的pSA,pSA才是指向SafeArray结构的指针,我们访问SafeArray结构需要用到就是这个pSA指针。
现在你应该已经了解了SafeArray大概的样子,就这么一点知识,我们就能在VB里对数组进行HACK了。
三、HACK数组字串指针
这已经是第三篇讲指针的东西了,我总在说指针能够让我们怎么样怎么样,不过你是不是觉得除了我说过的几个用途外,你也没觉得它有什么用,其实这是因为我和大家一样急于求成。在讲下去之前,我再来理一理VB里指针应该在什么情况下用。
只对指针类型用指针!废话?我的意思是说,象Integer, Long, Double这样的数值类型它们的数据直接存在变量里,VB处理它们并不慢,没有HACK的必要。但是字串,以及包括字串、数组、对象、结构的Variant,还有包括字串、对象结构的数组它们都是指针,实际数据不放在变量里,变量放的是指针,由于VB不直接支持指针,对他们的操作必须连同数据拷贝一起进行。有时我们并不想赋值,我们只想交换它们指针,或者想让多个指针指向同一个数据,让多个变量对同一处内存操作,要达到这样的目的,在VB里不HACK是不行的。
对数组尤其如此,比如我们今天要做的菜:对一个字串数组进行排序。我们知道,对字串数组进行排序很大一部分时间都用来交换字串元素,在VB里对字串赋值时要先将原字串释放掉,再新建一个字串,再将源字串拷贝过来,非常耗时。用COM里的概念来说,比如字串a、b的操作a=b,要先用SysFreeString(a)释放掉原来的字串a, 再用a = SysAllocString(b)新建和拷贝字串,明白了这一点就知道,在交换字串时不要用赋值的方式去交换,而应该直接去交换字串指针,我在指针葵花宝典第一篇里介绍过这种交换字串的方法,这可以大大提高交换字串的速度。但是这种交换至少也要用两次CopyMemory来将指针写回去,对多个字串进行交换时调用CopyMemory的次数程几何增长,效率有很大的损失。而实际上,指针只是32位整数而已,在C里交换两个指针,只需要进行三次Long型整数赋值就行了。所以我们要想想我们能不能将字串数组里所有字串指针拿出来放到一个Long型指针数组里,我们只交换这个Long型数组里的元素,也就相当于交换了字串指针,排好序后,再将这个Long型指针数组重新写回到字串数组的所有字串指针里,而避免了多次使用CopyMemory来一次次两两交换字串指针。这样我们所有的交换操作都是对一个Long型数组来进行,要知道交换两个Long型整数,在VB里和在C里是一样快的。
现在我们的问题成了如何一次性地将字串数组里的字串指针拿出来,又如何将调整后的字串指针数组写回去。
不用动数组的SafeArray结构,我们用StrPtr也能完成它。我们知道,字串数组元素里放的是实际上是字串指针,也就是BSTR指针,把这些指针放到一个Long型数组里很简单,用下面的方法:
Private Sub GetStrPtrs()
Dim Hi As Long, Lo As Long
Hi = UBound(MyArr)
Lo = LBound(MyArr)
ReDim lStrPtrs(0 To 1, Lo To Hi) As Long
Dim i As Long
For i = Lo To Hi
lStrPtrs(0, i) = StrPtr(MyArr(i))
'BSTR指针数组
lStrPtrs(1, i) = i
'原数组索引
为什么要用2维数组,这是排序的需要,因为当我们交换lStrPtrs里的Long型指针时,原来的字串数组MyArr里的字串指针并没有同时交换,所以用lStrPtrs里的Long型指针访问字串时,必须通过原来的索引,因此必须用2维数组同时记录下每个Long型指针所指字串在原字串数组里的索引。如果只用1维数组,访问字串时就又要用到CopyMemory了,比如访问lStrPtrs第三个元素所指的字串,得用如下方法:
CopyMemory ByVal VarPtr(StrTemp), lStrPtrs(3), 4 虽然只要我们保证StrTemp足够大,再加上一些清理善后的工作,这种做法是可以的,但实际上我们也看到这样还是得多次调用CopyMemory,实际上考虑到原来的字串数组MyArr一直就没变,我们能够通过索引来访问字串,上面同样的功能现在就成了:
StrTemp = MyArr(lStrPtrs(1,3)) '通过原字串数组索引读出字串。
不过,当我们交换lStrPtrs里的两个Long型指针元素时,还要记得同时交换它们的索引,比如交换第0个和第3个元素,如下:
lTemp1 = lStrPtrs(0, 3) : lTemp2 = lStrPtrs(1, 3)
lStrPtrs(0, 3) = lStrPtrs(0, 0) : lStrPtrs(1, 3) = lStrPtrs(1, 0)
lStrPtrs(0, 0) = lTemp1 : lStrPtrs(1, 0) = lTemp2
当我们排好序后,我们还要将这个lStrPtrs里的指针元素写回去,如下:
For i = Lo To Hi
CopyMemory(ByVal VarPtr(MyArr(i)), lStrPtrs(0,i), 4)
我已经不想再把这个方法讲下去,虽然它肯定可行,并且也肯定比用CopyMemory来移动数据要快,因为我们实际上移动的仅仅是Long型的指针元素。但我心里已经知道下面有更好更直接的方法,这种转弯抹角的曲线救国实在不值得浪费文字。
四、HACK数组的BSTR结构,实时处理指针。
最精采的来了,实时处理指针动态交换数据,好一个响亮的说法。
我们看到,上一节中所述方法的不足在于我们的Long型指针数组里的指针是独立的,它没有和字串数组里的字串指针联系在一起,要是能联系在一起,我们就能在交换Long型指针的同时,实时地交换字串元素。
这可能吗?
当然,否则我花那么笔墨去写SafeArray干什么!
在上一节,我们的目的是要把字串数组里的BSTR指针数组拿出来放到一个Long型数组里,而在这一节我们的目的是要让我们Long型指针数组就是字串数组里的BSTR指针数组。拿出来再放回去的方法,我们在上一节看到了,现在我们来看看,不拿出来而直接用的方法。
这个方法还是要从字串数组的SafeArray结构来分析,我们已经知道SafeArray结构里的pvData指向的就是一个放实际数据的真数组,而一个字串数组如MyArr它的pvData指向的是一个包含BSTR指针的真数组。现在让我们想想,如果我们将一个Long型数组lStrPtrs的pvData弄得和字串数组MyArr的pvData一样时会怎样?BSTR指针数组就可以通过Long型数组来访问了,先看如何用代码来实现这一点:
'模块级变量
Private MyArr() As String '要排序的字串数组
Private lStrPtrs() As Long '上面数组的字串指针数组,后面会凭空构造它
Private pSA As Long
'保存lStrPtrs数组的SafeArray结构指针
Private pvDataOld As Long '保存lStrPtrs数组的SafeArray结构的原
pvData指针,以便恢复lStrPtrs
'功能: 将Long型数组lStrPtrs的pvData设成字串数组MyArr的pvData
以使Long指针数组的变更能实时反应到字串数组里
Private Sub SetupStrPtrs()
Dim pvData As Long
' 初始化lStrPtrs,不需要将数组设得和MyArr一样大
我们会在后面构造它
ReDim lStrPtrs(0) As Long
'得到字串数组的pvData
pvData = VarPtr(MyArr(0))
'得到lStrPtrs数组的SafeArray结构指针
CopyMemory pSA, ByVal VarPtrArray(lStrPtrs), 4
'这个指针偏移12个字节后就是pvData指针,将这个指针保存到pvDataOld
以便最后还原lStrPtrs,此处也可以用:
pvDataOld = VarPtr(lStrPtrs(0))
CopyMemory pvDataOld, ByVal pSA + 12, 4
'将MyArr的pvData写到lStrPtrs的pvData里去
CopyMemory ByVal pSA + 12, pvData, 4
'完整构造SafeArray必须要构造它的rgsabound(0).cElements
CopyMemory ByVal pSA + 16, UBound(MyArr) - LBound(MyArr) + 1, 4
'还有rgsabound(0).lLbound
CopyMemory ByVal pSA + 20, LBound(MyArr), 4
看不懂,请结合图一再看看,应该可以看出我们是凭空构造了一个lStrPtrs,使它几乎和MyArr一模一样,唯一的不同就是它们的类型不同。MyArr字串数组里的fFeatures包含FADF_BSTR,而lStrPtrs的fFeatures包含FADF_HAVEVARTYPE,并且它的VARTYPE是VT_I4。不用关心这儿,我们只要知道lStrPtrs和MyArr它们指向同一个真数组,管他是BSTR还是VT_I4,我们把真数组里的元素当成指针来使就行了。
注意,由于lStrPtrs是我们经过了我们很大的改造,所以当程序结束前,我们应该将它还原,以便于VB来释放资源。是的,不释放也不一定会引起问题,因为程序运行结束后,操作系统的确是会回收我们在堆栈里分配了却没有释放的lStrPtrs原来的野指针pvOldData,但当你在IDE中运行时,你有60%的机会让VB的IDE死掉。我们是想帮VB做点家务事,而不是想给VB添乱子,所以请记住在做完菜后,一定要把厨房打扫干净,东西该还原的一定要还原。下面看看怎么样来还原:
'还原我们做过手脚的lStrPtr
Private Sub CleanUpStrPtrs()
'lStrPtr的原来声明为:ReDim lStrPtrs(0) As Long
按声明的要求还原它
CopyMemory pSA, ByVal VarPtrArray(lStrPtrs), 4
CopyMemory ByVal pSA + 12, pvDataOld, 4
CopyMemory ByVal pSA + 16, 1, 4
CopyMemory ByVal pSA + 20, 0, 4
好了,精华已经讲完了,如果你还有点想不通,看看下面的实验:
Sub Main()
'初始化字串数组
Call InitArray(6)
'改造lStrPtrs
Call SetupStrPtrs
'下面说明两个指针是一样的
Debug.Print lStrPtrs(3)
Debug.Print StrPtr(MyArr(3))
Debug.Print
'先看看原来的字串
Debug.Print MyArr(0)
Debug.Print MyArr(3)
Debug.Print
'现在来交换第0个和第3个字串
Dim lTmp As Long
lTmp = lStrPtrs(3)
lStrPtrs(3) = lStrPtrs(0)
lStrPtrs(0) = lTmp
'再来看看我们的字串,是不是觉得很神奇
Debug.Print MyArr(0)
Debug.Print MyArr(3)
Debug.Print
Call CleanUpStrPtrs
在我的机器上,运行结果如下: 1887420 1887420 OPIIU WCYKOTC WCYKOTC OPIIU
怎么样?如愿已偿!字串通过交换Long型数被实时交换了。
通过这种方式来实现字串数组排序就非常快了,其效率上的提高是惊人的,对冒泡排序这样交换字串次数很多的排序方式,其平均性能能提高一倍以上(要看我们字串平均长度,),对快速排序这样交换次数较少的方法也能有不少性能上的提高,用这种技术实现的快速排序,可以看看本文的配套代码中的QSortPointers。
本道菜最关键的技术已经讲了,至于怎么做完这道菜,怎么把这道菜做得更好,还需要大家自己来实践。
四、我们学到了什么。
仅就SafeArray来说,你可能已经发现我根本就没有直接去用我定义了的SAFEARRAY结构,我也没有展开讲它,实际上对SafeArray我们还可以做很多工作,还有很多巧妙的应用。还有需要注意的,VarPtrArray不能用来返回字串数组和Variant数组的SafeArray结构的指针的指针,为什么会这样和怎样来解决这个问题?这些都需要我们了解BSTR,了解VARIANT,了解VARTYPE,这些也只是COM的冰山一角,要学好VB,乃至整个软件开发技术,COM还有很多很多东西需要学习,我也还在学,在我开始COM的专题之前,大家也应该自学一下。
COM的东西先放一放,下一篇文章,应朋友的要求,我准备来写写内存共享。 后记:
又花了整整一天的时间,希望写的东西有价值,觉得有用就来叫个好吧! AdamBear 熊超
真想不到之五:高效字串指针类 关键字: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
'http://www.csdn.net/Author/AdamBear
你可以自由使用本类模块,不过请保留本声明
'********************************************************
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
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
'还原本字串原BSTR描述符
Public Sub Detach()
CopyMemory ByVal VarPtr(sString), 0&, 4
'让本字串指向源字串
Public Sub AttachStr(sStr As String)
Attach StrPtr(sStr), LenB(sStr)
'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
用法如下,假设我们已通过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共享软件上,名字是《内存共享和指针》。 真想不到之六:有用的技术和没用的指针 关键字: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* pL = &L;
printf("L = %d *pL = %d", l, *pl); 'VB里
Dim pL As New pLong, L As Long
pL.Attach L
'也可以 pL.Ptr = VarPtr(L)
Debug.Print "L ="; L; " *pL ="; pL
结果都能够通过修改pL指针,达到修改变量L的目的。
上面VB代码里的pLong就是一个包装好的Long型变量的指针类,下面看看如何来实现它:
Option Explicit
'********************************************************
'pLong.cls
'包装一个Long型指针的类
'作者: 熊超
ID: AdamBear
'http://www.csdn.net/Author/AdamBear
你可以自由使用本类模块,不过请保留本声明
'********************************************************
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)
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
要将它改成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
Debug.Print "B ="; B; " *pB ="; pB
Debug.Print "B ="; B; " *pB ="; pB
Debug.Print "I ="; I; " *pI ="; pI
Debug.Print "I ="; I; " *pI ="; pI
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
搞出这几种普通数值类型的指针类有什么用?基本上没有什么大用。不过是证明一种方法的可行性,和演示技术。这种技术还有什么用,需要的时候还会再谈。 后记:
本文的东西,可见CSDN共享软件上的《内存共享和指针》,
指针的专题就到这儿了,下一篇准备开始着手写VB和COM的一个系列文章,其间我准备翻译一下《VB Design Patterns》,这是一本不错的书。 转载者后记:感谢作者将我从VB的噩梦中拯救出来,感谢他所以要转载他的文章感化更多的VB用户!!!Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=450985
本文已收录于以下专栏:
博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 vb编程教程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信