DLL字符串参数做返回值的看法

本文介绍了作者在使用C++Builder编写DLL时遇到的字符串返回值问题。由于DLL无法像VB的ActiveX DLL那样进行初始化和释放操作,作者采取了通过布尔返回值表示串口操作成功与否,而字符串数据则通过特定方式处理。在C++Builder中,字符串不能直接作为参数返回,作者借鉴网上的资料,利用BSTR和LPSTR的相似性,实现了通过传值方式传递字符串地址,从而在API调用后返回数据。这种方式避免了对实参内容的修改,符合高级语言的传值调用规则。

        最近在写几个串口程序,由于以前都是用VB6写ActiveX DLL做通讯 ,里面加入Mscomm32.ocx做为通讯控件,已经很熟练了.可是对于VB的执行效率很是不满意,于是又拿起C++Builder与RS232通讯的书开始啃起来.打算用C++Builder来写上位机与下位机的通讯动态连接库,用VB做界面开发.

        由于初次学C++语言,很多时间都不明白指针跟引用的关系,里面的字符串也有好多种类,其中AnsiString,Char * Char [],String 之间总是搞不清楚,所以进度很慢.

        首先,VB的ActiveX DLL可以在引用的时候对串口进行初始化,都可以放在Class_Initialize里进行,也可以在退出的时候在Class_Terminate里对程序进行释放等操作.可是C++Builder写的DLL就没办法操作(才学几天,可能说的不对,应该有对DLL初始化操作,本人至今未学会),所以就只能写两个函数,一个是串口打开,一个是串口关闭,这两个函数做为内部函数,不对VB开放.这两个也是用的最多的函数.其次,VB可以用Variant数据类型对串口进行连继的读取,可以用变量加变量的方式把数据接收完.可是在C++Builder里就没办法(我是菜鸟,望大虾指导,邮箱 alpha704@163.com),只能用一个定长Char[]数组操作,每次接收完了把变量转成AnsiString然后相加,这样最后得到全部数据.再把数据转成Char *指针变量,传递出去.讲了这么多,最主要的还没讲到.下面接着来.

        先讲讲传参数吧,首先我的函数都有返回值,函数名就是返回值,但不是对数据的返回,是对串口是否通讯成功的返回,也就是说一个布尔型(bool)数据,如果串口打不开,机器不在线或串口返回值错的我都返回false,如果全部操作都正确了,才返回true.也就是说,一个返回值是对串口操作而言,一个是对下位机数据的返回值.这里有用到字符串数据,关键在这里,字符串数据没办法在参数里返回.接下去说说我解决的办法,不过都是从网上下下来的资料,希望对以后遇到相同问题的人有帮助.

大家都知道,VB中没有C语言中的指针类型。C语言中只有字符数据类型,即字符变量只能存放一个字符,而没有字符串变量,它操作字符串是通过字符型指针来实现的,它的特点是通过’/0’来判断字符是否结束的。而VB有字符串变量类型,一种变长,一种是定长的。并且VB字符串具体自动保护功能。
例如:dim str as string * 6
      str=”abcdef”
      debug.print str’那么将显示abcdef
      str=”abcdefghijklmnopq”
      debug.print str’还显示abcdef,说明它具有保护功能,将超过的字符截掉
在VB中使用的字符是一种叫做BSTR格式的字符串指针类型。
 
字符个数描述符由VB来使用,BSTR指针直接指向第一个字符。
因为大多数API函数是用C或C++来编写的,在C/C++(API)中使用叫做LPSTR类型的指针。
 

(是从别的地方拷过来的,没图)从上图可知:字符串变量X的地址与实际字符串的地址不同,也就是说字符X变量中实际上是存放的字符串的首地址这一点是和C/C++相同的。其实图中descriptor这个描述符就是C中的字符串指针地址。当BSTR指针在忽略字符个数描述前缀的情况下是与LPSTR指针是相同的,在调用API时可以将BSTR以传值方式传递给API。采用传值方式传递时实际上传递的是实参中所存放的字符串的首地址,当调用过API后,可以通过它来返回数据,API修改传送给它的那个地址所指向的字符串数据,而没有修改实参字符变量内的内容,所以可以返回数据。即并没有与高级语言中所规定的传值方式调用方式的形参改变不影响实参的规则相冲突。

实例:
模块中的代码:
Option Explicit
 
Public Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
窗体中的代码:
 
Option Explicit
 
Private Sub Command1_Click()
   Dim str As String
   Dim str1 as String
   Dim length As Long
   length = 255
 
   str = String(length, 0)
   str1=str
   Debug.Print VarPtr(str),”     “,varptr(str1)
   Debug.Print StrPtr(str),”      ”,strptr(str1)
   Debug.Print
 
   GetComputerName str, length
 
   Debug.Print str
   Debug.Print VarPtr(str)
   Debug.Print StrPtr(str), Len(str), length
End Sub
 
在立即窗口中可以看到运行结果。字符串变量的地址与字符串的地址不同。
还可以看到字符串变量str与str1的地址不同,而且字符串地址也不同,这说明在进行赋值操作,并不是将字符的首地址赋给str1而是在内存中另开一空间用来存放字符串。而在C语言内则可以使多个字符型指针变量指向同一个字符串的首地址。
当将API中的ByVal lpBuffer As String传值方式改为:ByRef lpBuffer As String传址方式时,运行程序中出错,VB编程环境将崩溃。
出错图:
 
因为传址时将变量本身的地址传给了API,并没有将字符串的首地址传给API,所以API在修改数据时造成访问错误。
总结
不能用传址方式来调用API,如果用传址方式的话那么传递的是指向指针的指针,API将不能返回数据,并且造成访问数据出错,所以需要用ByVal传递字符串指针
 
以上是拷过来的,版权没写,可以转载.呵
 
bool __stdcall MachineRead(int PortNumber,int MachineNumber,unsigned char * &MachineCard)
这是我DLL里的原型,MachineCard可以用这个返回4字节字符串,可是用在相同的类型上,对于大数据库来说MachineCard只能返回前面的一部分,不明白为什么?在VB里调用都是ByRef传址方式.不会出错.但结果不正确.后来改成
bool __stdcall MachineGetData(int PortNumber,int MachineNumber,unsigned char * MachineData)
VB调用时改成 字符串变量=String(255,0) 先预分配空间,调用采用ByVal,返回数据都正确,没问题了.
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值