VB60调用DLL161210.docx
《VB60调用DLL161210.docx》由会员分享,可在线阅读,更多相关《VB60调用DLL161210.docx(33页珍藏版)》请在冰豆网上搜索。
VB60调用DLL161210
VB6.0调用DLL
Hanford
2016年12月10日
目录
第1章VB6.0调用DLL1
1VC++编写DLL1
1.1使用__stdcall1
1.2使用.DEF文件1
2简单数据类型2
2.1传值(ByVal)2
2.2传址(ByRef)3
2.3传址(VarPtr)4
2.4转换为Variant4
3String6
3.1BSTR内存布局6
3.2StrPtr、VarPtr7
3.3示例代码7
3.4转换为Variant12
3.5小结13
4结构14
4.1定义14
4.2示例代码14
5数组17
5.1简单数据类型数组17
5.2转换为Variant18
5.3结构数组19
6Object21
7函数21
第1章VB6.0调用DLL
1VC++编写DLL
VB6.0或VBA可以调用DLL里的导出函数。
可以使用VC++编写DLL,供VB6.0调用。
VC++编写DLL时,需要注意以下几点:
1.1使用__stdcall
C函数在默认情况下,其调用约定为__cdecl。
为了让VB6.0调用此函数,需要特别设置调用约定为__stdcall(或WINAPI)。
如下面的C代码:
void__stdcallTest()
{
}
1.2使用.DEF文件
使用VC++编写DLL,可以使用__declspec(dllexport)导出一个函数。
如:
__declspec(dllexport)void__stdcallTest()
{
}
使用VC++6.0的C编译器编译上述代码,则导出函数名为_Test@0。
此时,VB6.0声明Test函数的代码如下:
PrivateDeclareSubTestLib"Test.dll"Alias"_Test@0"()
“Alias"_Test@0"”明确说明了导出函数的名称。
为了简化VB6.0里Test函数的声明,可使用DEF文件,具体如下:
1、使用DEF文件。
下面是DEF文件的内容,它表示导出Test函数:
EXPORTS
Test
2、VC代码里不使用__declspec(dllexport),如下面的代码
void__stdcallTest()
{
}
3、VB6.0声明Test函数时无需Alias,如下面的声明
PrivateDeclareSubTestLib"Test.dll"()
2简单数据类型
VB6.0中,常见的数据类型请见下表
VB6.0
C
说明
Boolean
VARIANT_BOOL
就是short
VARIANT_TRUE等于-1表示TRUE
VARIANT_FALSE等于0表示FALSE
Byte
BYTE
就是unsignedchar
Currency
CY
CURRENCY
就是__int64
如:
1234567表示货币值123.4567@
Date
DATE
就是double
表示1899年12月30日到当前时刻的天数,对应于MFC的COleDateTime
Decimal
DECIMAL
VB6.0里Decimal用Variant表示
Double
double
Integer
short
Long
long
Single
float
String
BSTR
Unicode字符串
Variant
VARIANT
上表中,除了Decimal、String、Variant之外其它均为简单数据类型。
VB6.0传递简单数据类型给DLL时,分为传值和传址两种方式:
2.1传值(ByVal)
VC++代码:
longWINAPITestSimpleV(VARIANT_BOOLb,BYTEbt,__int64c
DATEdt,doubled,shorti,longl,floats)
{
b=VARIANT_TRUE;//改变形参,不会影响VB6.0里的实参
return0;
}
VB6.0声明代码:
PublicDeclareFunctionTestSimpleVLib"Test.dll"_
(ByValbAsBoolean,ByValbtAsByte,ByValcAsCurrency,_
ByValdtAsDate,ByValdAsDouble,ByValiAsInteger,_
ByVallAsLong,ByValsAsSingle)AsLong
VB6.0调用代码:
DimbAsBoolean,btAsByte,cAsCurrency,dtAsDate
DimdAsDouble,iAsInteger,lAsLong,sAsSingle
DimnRetAsLong
nRet=TestSimpleV(b,bt,c,dt,d,i,l,s)
说明:
1、VB6.0声明函数时,必须使用ByVal,表示把实参的数值传递给形参;
2、因为传递的是数值,所以VC函数里改变形参的数值不会影响到实参。
2.2传址(ByRef)
VC++代码:
longWINAPITestSimpleR(VARIANT_BOOL*b,BYTE*bt,__int64*c
DATE*dt,double*d,short*i,long*l,float*s)
{
if(b){*b=VARIANT_TRUE;}
if(bt){*bt=2;}
if(c){*c=654321;}//货币值65.4321@
if(d){*d=3.4;}
if(i){*i=5;}
if(l){*l=6;}
if(s){*s=7.8f;}
if(dt)
{
SYSTEMTIMEtmSys;
GetLocalTime(&tmSys);
SystemTimeToVariantTime(&tmSys,dt);
}
return1;
}
VB6.0声明代码:
PublicDeclareFunctionTestSimpleRLib"Test.dll"_
(ByRefbAsBoolean,ByRefbtAsByte,ByRefcAsCurrency,_
ByRefdtAsDate,ByRefdAsDouble,ByRefiAsInteger,_
ByReflAsLong,ByRefsAsSingle)AsLong
VB6.0调用代码:
DimbAsBoolean,btAsByte,cAsCurrency,dtAsDate
DimdAsDouble,iAsInteger,lAsLong,sAsSingle
DimnRetAsLong
nRet=TestSimpleR(b,bt,c,dt,d,i,l,s)
说明:
1、VB6.0声明函数时,应使用ByRef,表示把实参的地址传递给形参;
2、因为传递的是地址,所以VC函数里改变形参的数值将影响到实参。
2.3传址(VarPtr)
使用ByRef无法将NULL指针传递给DLL,为此可使用函数VarPtr。
示例代码如下:
VB6.0声明代码:
PublicDeclareFunctionTestSimplePLib"Test.dll"Alias"TestSimpleR"_
(ByValbAsLong,ByValbtAsLong,ByValcAsLong,_
ByValdtAsLong,ByValdAsLong,ByValiAsLong,_
ByVallAsLong,ByValsAsLong)AsLong
VB6.0调用代码:
DimbAsBoolean,btAsByte,cAsCurrency,dtAsDate
DimdAsDouble,iAsInteger,lAsLong,sAsSingle
DimnRetAsLong
nRet=TestSimpleP(VarPtr(b),VarPtr(bt),VarPtr(c),VarPtr(dt),_
VarPtr(d),VarPtr(i),VarPtr(l),VarPtr(s))
VarPtr(b)将返回变量b的地址,被VB6.0当做long传递给DLL。
若要传递NULL指针,可修改VarPtr(...)为0。
VB.NET不支持VarPtr函数,下面是从网上找到的代码,不知是否稳定。
PublicFunctionVarPtr(ByValeAsObject)AsInteger
DimGCAsGCHandle=GCHandle.Alloc(e,GCHandleType.Pinned)
DimGC2AsInteger=GC.AddrOfPinnedObject.ToInt32
GC.Free()
ReturnGC2
EndFunction
2.4转换为Variant
VB6.0里可以将简单数据类型转换为Variant,然后再传递给DLL。
下面是示例代码:
VC++代码:
voidWINAPITestSimpleVariant(VARIANTv,VARIANT*r)
{
if(v.vt==VT_R8)
{
v.dblVal=-1.2;//不会影响实参
}
if(r&&r->vt==(VT_R4|VT_BYREF))
{
*r->pfltVal=-3.4f;//会影响实参
}
}
VB6.0声明代码:
PublicDeclareSubTestSimpleVariantLib"Test.dll"_
(ByValvAsVariant,ByRefrAsVariant)
VB6.0调用代码:
DimdAsDouble,sAsSingle
d=1.2
s=3.4!
MsgBoxHex$(VarPtr(d))&"="&d&vbCrLf&_
Hex$(VarPtr(s))&"="&s
CallTestSimpleVariant(d,s)
MsgBoxHex$(VarPtr(d))&"="&d&vbCrLf&_
Hex$(VarPtr(s))&"="&s
运行VB6.0代码,将依次出现:
1、首先显示如下界面
说明:
变量d的首地址为0x18F374,数值为1.2;变量s的首地址为0x18F370,数值为3.4;
2、VB6.0将d、s转换为Variant,然后传递给VC函数TestSimpleVariant。
注意:
r->pfltVal就是实参s的首地址;
3、VB6.0中CallTestSimpleVariant(d,s)执行完毕后,d的数值未变,s的数值被改变,d、s的地址均未改变,如下图所示:
3String
可以认为VB6.0的String就是C语言的BSTR。
BSTR可以表示宽字符串(Unicode),也可以表示窄字符串(ANSI)。
在VB6.0或EVB3.0里,String对应的BSTR是一个宽字符串。
3.1BSTR内存布局
BSTRs表示了一个字符串。
s是一个指针,指向了一个ANSI或UNICODE字符串。
此外,BSTR还包含了字符串的长度信息。
具体结构如下:
字符串所占字节数
共4字节
字符串
首地址就是s
结束符
如:
字符串”123”以ANSI的BSTR表示(假定s的值为0x1000)
地址
内容
说明
0x0FFC
03H
字符串所占字节数,这里为3
0x0FFD
00H
0x0FFE
00H
0x0FFF
00H
0x1000
31H
字符串123
0x1001
32H
0x1002
33H
0x1003
00H
结束符
字符串”123”以UNICODE的BSTR表示(假定s的值为0x1000)
地址
内容
说明
0x0FFC
06H
字符串所占字节数,这里为6
0x0FFD
00H
0x0FFE
00H
0x0FFF
00H
0x1000
31H
字符串123
0x1001
00H
0x1002
32H
0x1003
00H
0x1004
33H
0x1005
00H
0x1006
00H
结束符
0x1007
00H
所以,当BSTRs表示ANSI字符串时,可以把s当作char*;当BSTRs表示UNICODE字符串时,可以把s当作wchar_t*
3.2StrPtr、VarPtr
StrPtr返回的是字符串首字符的地址(可看做wchar_t*),VarPtr返回的是字符串变量的地址(可看做wchar_t**)。
参考下表,可以更好的理解:
VB6.0代码
C代码
DimsAsString
BSTRs;
DimnAsLong
n=StrPtr(s)
wchar_t*n=(wchar_t*)s;
n=VarPtr(s)
wchar_t**n=(wchar_t**)&s;
3.3示例代码
VB6.0里将String变量传递给DLL的示例代码如下:
VC++代码:
BSTRWINAPITestString(BSTRv,BSTR*r,BSTRsp,BSTR*vp)
{
if(v)
{//VB6.0传过来vbNullString,则v为NULL
char*p=(char*)v;//ANSI字符串首地址
UINT&nLen=((UINT*)p)[-1];//ANSI字符串长度
if(nLen>0)
{
p[0]='v';//允许修改字符串
nLen=1;//允许将字符串长度变小
p[nLen]='\0';//修改字符串长度后,这句是必需的
}
}
{
char*p=*(char**)r;//ANSI字符串首地址
if(p)
{//VB6.0传过来vbNullString,则r不为NULL,p为NULL
UINT&nLen=((UINT*)p)[-1];//ANSI字符串长度
if(nLen>0)
{
p[0]='r';//允许修改字符串
nLen=1;//允许将字符串长度变小
p[nLen]='\0';//修改字符串长度后,这句是必需的
}
}
else
{//重新定义字符串
char*szStr="C函数生成的字符串";
SysFreeString(*r);//释放以前的字符串
*r=SysAllocStringByteLen(szStr,strlen(szStr));//修改字符串
}
}
{
wchar_t*p=(wchar_t*)sp;//Unicode字符串首地址
if(p)
{//VB6.0传过来vbNullString,则sp不为NULL,p为NULL
UINT&nLen2=((UINT*)p)[-1];//Unicode字符串字节数
if(nLen2>=2)
{
p[0]=L's';//允许修改字符串
nLen2=2;//允许将字符串长度变小
p[nLen2>>1]=L'\0';//修改字符串长度后,这句是必需的
}
}
}
{
wchar_t*p=*(wchar_t**)vp;//Unicode字符串首地址
if(p)
{//VB6.0传过来vbNullString,则vp不为NULL,p为NULL
UINT&nLen2=((UINT*)p)[-1];//Unicode字符串字节数
if(nLen2>=2)
{
p[0]=L'v';//允许修改字符串
nLen2=2;//允许将字符串长度变小
p[nLen2>>1]=L'\0';//修改字符串长度后,这句是必需的
}
}
}
{//返回值,允许返回NULL
char*szStr="C函数返回的字符串";
returnSysAllocStringByteLen(szStr,strlen(szStr));
}
}
VB6.0声明代码:
PublicDeclareFunctionTestStringLib"Test.dll"_
(ByValvAsString,ByRefrAsString,_
ByValspAsLong,ByValvpAsLong)AsString
VB6.0调用代码:
DimvAsString
DimrAsString
DimspAsString
DimvpAsString
v=txtV.Text
r=txtR.Text
sp=txtSP.Text
vp=txtVP.Text
txtReturn.Text=TestString(v,r,StrPtr(sp),VarPtr(vp))
txtV.Text=v
txtR.Text=r
txtSP.Text=sp
txtVP.Text=vp
下面是对上述代码的详细说明:
3.3.1ByValvAsString
VB6.0里的变量v是一个String,其实就是一个Unicode的BSTR。
调用TestString之前,VB6.0会将v转换为Ansi的BSTR,并将这个BSTR传递给DLL。
DLL的处理代码如下:
if(v)
{//VB6.0传过来vbNullString,则v为NULL
char*p=(char*)v;//ANSI字符串首地址
UINT&nLen=((UINT*)p)[-1];//ANSI字符串长度
if(nLen>0)
{
p[0]='v';//允许修改字符串
nLen=1;//允许将字符串长度变小
p[nLen]='\0';//修改字符串长度后,这句是必需的
}
}
首先要判断v是否为NULL,因为VB6.0传递字符串vbNullString时,该参数将为NULL。
接下来获得ANSI字符串的首地址p和长度nLen。
允许修改ANSI字符串的内容和长度,但是不能加长字符串。
VC++的TestString函数返回后,VB6.0会将ANSI字符串转换为Unicode字符串,并由此改变实参v。
3.3.2ByRefrAsString
VB6.0里的变量r是一个String,其实就是一个Unicode的BSTR。
调用TestString之前,VB6.0会将r转换为Ansi的BSTR,并将这个BSTR传递给DLL。
DLL的处理代码如下:
{
char*p=*(char**)r;//ANSI字符串首地址
if(p)
{//VB6.0传过来vbNullString,则r不为NULL,p为NULL
UINT&nLen=((UINT*)p)[-1];//ANSI字符串长度
if(nLen>0)
{
p[0]='r';//允许修改字符串
nLen=1;//允许将字符串长度变小
p[nLen]='\0';//修改字符串长度后,这句是必需的
}
}
else
{//重新定义字符串
char*szStr="C函数生成的字符串";
SysFreeString(*r);//释放以前的字符串
*r=SysAllocStringByteLen(szStr,strlen(szStr));//修改字符串
}
}
即:
可以修改ANSI字符串内容和长度,也可以重新生成字符串。
VC++的TestString函数返回后,VB6.0会将ANSI字符串转换为Unicode字符串,并由此改变实参r。
3.3.3ByValspAsLong
VB6.0里的变量sp是一个String,其实就是一个Unicode的BSTR。
调用TestString时使用StrPtr(sp)将字符串的首地址传递给了DLL,DLL的处理代码如下:
{
wchar_t*p=(wchar_t*)sp;//Unicode字符串首地址
if(p)
{//VB6.0传过来vbNullString,则sp不为NULL,p为NULL
UINT&nLen2=((UINT*)p)[-1];//Unicode字符串字节数
if(nLen2>=2)
{
p[0]=L's';//允许修改字符串
nLen2=2;//允许将字符串长度变小
p[nLen2>>1]=L'\0';//修改字符串长度后,这句是必需的
}
}
}
上述代码处理的不再是ANSI字符串,而是Unicode字符串。
不过,修改字符串时只能减小长度,不能增大长度。
3.3.4ByValvpAsLong
VB6.0里的变量vp是一个String,其实就是一个Unicode的BSTR。
调用TestString时使用VarPtr(vp)将字符串变量的地址传递给了DLL,DLL的处理代码如下:
{
wchar_t*p=*(wchar_t**)vp;//Unicode字符串首地址
if(p)
{//VB6.0传过来vbNullString,则vp不为NULL,p为NULL
UINT&nLen2=((UINT*)p)[-1];//Unicode字符串字节数
if(nLen2>=2)
{
p[0]=L'v';//允许修改字符串
nLen2=2;//允许将字符串长度变小
p[nLen2>>1]=L'\0';//修改字符串长度后,这句是必需的
}
}
}
与“ByValspAsLong”一样:
修改字符串时只能减小长度,不能增大。
3.3.5返回值