我们在编写visualbasic应用程序的时候常常需要自己动手编写一些库函数或activex控件,而这些函数或者控件常常采用vc 语言来写,因而也常为vb和vc两种语言之间不同参数类型、内存空间使用方法等问题为难,怎么做好呢?本文介绍了在vb和vc混合编程的情况下对这些问题的解决方法。
一、自定义类型参数向dll库函数的传递
用vb,vc 进行混合编程时,通常需要在vb中调用vc 编写的dll库函数,这时,一般都要遇到向库函数传递参数的问题。对于标准类型(如double,long等)参数,其传递比较简单,只要保证了vb中对库函数的声明和vc 中对库函数的定义在参数类型、次序和传递方式上的一致性,参数就不会被错误传递。但是,如果需要向库函数传递自定义类型的参数,情况就会变得复杂了。
情况一:自定义类型的所有成员变量都是同一种类型(例如下面的pens自定义类型,其成员都为long型)。
typepens
redpennumaslong
greenpennumaslong
b1uepennumaslong
endtype
这时,只要在vb和vc 中对该结构采用相同的定义,并充分注意到vb和vc 对某些数据类型(如32,位操作系统下,vc 中的int和vb中的integer)存储上的差异,就不会发生参数传递错误。
情况二:自定义类型中成员变量的类型不完全一致。这时,又要区分两种情况:
情况(1)没有double型成员变量。
这时一般也不会出现参数的传递错误。
情况(2)含有double型成员变量。
这时参数通常就会被误传。比如传递下面的person类型的参数到vc 开发的dll库函数,double型成员height的值就会在传递中丢失:
typeperson
ageaslong
heightasdouble
endtype
造成height值丢失的原因是由于在vc 中存储person型变量时,将自动在long型成员age和double型成员height之间插入若干字节的分隔空间,而vb则不会。所以,vc 中存储一个person型变量需要的内存要多于12字节,而vb只需要12个。因此,从vb传入dll库函数的person型变量就不能被正确接收。
解决这一问题的方法有多种,这里介绍一种比较简便和普适的,称之为“引入补位成员法”:在person这种自定义类型中引入若干个内存补位成员,使得任一个double型成员之前的所有成员占用的字节总数都是单个double型变量所占字节数的整数倍(8的整数倍)。
仍以person类型为例,由于age成员占用4字节内存,所以要在其后引入一个占用4字节的补位成员,不妨引入一个string型的成员tempst:
typeperson
ageaslong
tempstasstring*4
heightasdouble
endtype
于是,double型成员height之前的所有成员占用的内存总数变成了8个字节,是8的整数倍。此时,将dll库函数中对person的定义作同样的修改后,就可以正确接收从vb传来的person型参数了。
注意:引入补位成员时,不但要合理分配其占用的字节数,而且要正确安排其在结构体中的位置,二者缺一不可。上例中,若把补位成员放在height之后,doubl型变量height之前的所有成员占用的字节总数仍然是4,不是8的整数倍。
在自己编写dll库函数时,往往会在函数接口处使用复杂的自定义结构。在vb中调用这种函数时,采用“引入补位成员法”适当修改结构体的定义,就可以有效地避免参数传递上的错误。
二、使用在vc 中动态申请的内存
混合语言编程时,有时需要在vb代码中使用通过vc 动态申请到的内存。这时,可以通过下述方法实现:
1)vc 中申请动态内存的dll库函数
char*apientrycreatestringbuffer(longlength)
{
char*bufv;//假设需要申请用以存放字符申的动态内存
buf=(char*)::malloc(length);
returnbuf;//返回字符串指针,其实就是一个long型数
}
2)vb中接收动态内存指针的代码
......
declarefunctioncreatestringbufferlib"c:\dlltest\test.dll"_
(byvallengthaslong)aslong
'long型变t接收动态内存指针
......
dimslbuffer&
atbuffer=createstringbuffer(20)
'申请一块可存放20个字符的内存,得到指向该内存的指针
......
'使用该动态内存
......
注意:vb中使用完动态内存后,为了避免内存泄漏二要将其指针传回vc 进行内存释放工作。
三、自定义类型参数向activex控件的传递
在编写vb程序时,如果使用的是标准activex控件,那么一般不需要向控件传递自定义类型的参数,因为大多数控件的大多数属性都是标准类型(如double,long)的。但是,在混合语言编程中,当我们采用vc 中的atl3.0模板(而不是vb)自行开发activex控件时,往往希望能够向控件的某些属性或方法传递自定义类型的参数,以提高参数的传递效率。
这里介绍一种向控件传递自定义类型参数的简便方法。假设要以vb为客户端开发一个activex控件ax,它有一个student属性,类型是自定义结构person:
typeperson
ageaslong
heightasdouble
endtype
第一,正确编写student属性的接口函数(以用atl3.0模板开发ax为例)。我们将student属性存取函数的接口参数类型写成一个long型的指针,而不再是bstr。因为activex内部的通信全部基于unicode基础之上,所以,这样处理会避免由于字符集不匹配而造成的参数误传。相关的代码如下:
1)ax.idl中对student属性的定义
[propget,id(0),helpstring("propertystudent")]hresult
student([out,retval]long*pval);
[propput,id(0),helpstring("propertystudent")]hresult
student([in]longnewval);
2)ax.h中对student属性存取函数的定义
stdmethod(get_student)(/*[out,retval]*/long*pval);
stdmethod(put_student)(/*[in]*/longnewval);
3)axcpp中对student属性存取函数的实现
stdmethodimpcax::get_student(long*pval)
{
//todd:addyourimplementationcodehere
//得到存储student属性的成员变t的指针,赋给*pval
returns_ok;
}
stdmethodimpcax::put_student(longnewval)
{
//todd:addyourimplementationcodehere
//将存储student属性的成员变址的指针指向newval所指的内存空间,
//然后通过内存拷贝方式拷贝此空间存放的student的属性值
returns_ok;
}
第二,正确编写vb向ax的student属性动态赋值的代码。在vb中,先声明一个person型变量,给该变量赋值后,获取该变量的内存地址并赋给student属性即可。代码如下:
......
dimstudentpropasperson
dimstudentaddraslong
studentprop.age=23
studentprop.heigth=1.78
'得到studentprop变量的内存地址(方法从略),赋给studentaddr
ax1.student=studentaddr
......
借助指针完成自定义类型参数向activex控件的传递所依据的是以下事实:不论控件是.dll还是.ocx,它都是与其客户同在一个进程内的服务器。所以,只要ax被编译成.dll或.ocx,指针的传递就是安全可靠的。
四、中英文混合型字符串输出长度的确定
中英文混合型字符串输出长度的确定问题在vb编程中经常遇到,而且可以通过vb,vc 混合编程有效解决,所以在此一并给出。
vb编程中,经常需要得到某个字符串在实际输出时所需要的长度。这时,我们通常会考虑len()和lenb()这两个函数。
我们知道,len()返回的是字符串中字符的个数,对于不含中文字符的字符串,其返回值通常就等于该字符串的输出长度;lenb()返回的则是按照双字节字符集(dbcs)计算出的字符串所占用的字节数,对于纯中文字符组成的字符串,其返回值通常也等于该字符串的输出长度。但是,当字符串中既有中文又有英文(这里将数字等视为英文)字符时,二者的返回值都不等于该字符串的输出长度。比如:“a中国人”这个字符串,用len()函数时将返回4;lenb()则返回8;而实际输出时(比如向某记录文件输出该字符串),它将占用7个印刷符(每个英文字符占1个,每个中文字符占2个)。
为了计算中英文混合型字符串的输出长度,我们可以用vc 编写一个完成此计算的dll库函数,在vb中直接调用该函数即可。该vc 函数和vb中的调用代码如下:
1)vc 6.0中的dll函数
longapientrysizeof_vbstring(char*at)
{
return(long)(::atrlen(st));
//::atrlen()返回int值,但在32位操作系统下,
//vc 中的int类型与vb中long类型的范围是相当的
}
2)vb6.0中对sizeof_vbstring()的声明和调用
......
declarefunctionsizeof_vbstringlib"c:\dlltest.dll"_
(byvalstasstring)aslong
......
......
dimstlen&
stlen=sizeof_vbstring("a中国人")
stlen=7
......
应该指出:上述方法同样可以计算纯英文或纯中文字符串的输入长度。
->