这个DLL中的函数原型,VB中该如何引用?

seeQe 2011-04-15 09:57:12
有个DLL 中的函数原型:
extern "C" bool __declspec(dllexport) __stdcall EvideoOpenRoom(BSTR RoomIP, BSTR ServerIP, int iFlag)


如果用VB 来引用这个函数,该如何定义呢?

Public Declare Function EvideoOpenRoom Lib "SendWineEV.DLL" (ByVal strRoomIP As String, ByVal strServerIP As String, ByVal nFlag As Long) As Long

定义为这样,始终通不过,请给点参考建议,十分感谢~~
...全文
291 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
seeQe 2011-08-25
  • 打赏
  • 举报
回复
感谢上面各位: Lactoferrin,spt_petrolor,SupermanKing,................能这么好的给与回贴,
给分了,答案正确者分儿最多
Lactoferrin 2011-04-19
  • 打赏
  • 举报
回复
extern "C" bool __declspec(dllexport) __stdcall EvideoOpenRoom(BSTR RoomIP, BSTR ServerIP, int iFlag)
好像不能用vb调用,vb调用要用stdcall


这个不就是__stdcall?

既然vb6的string是unicode,就可以用StrPtr得到字符地址,然后传给需要unicode字符串的api
王二.麻子 2011-04-19
  • 打赏
  • 举报
回复
extern "C" bool __declspec(dllexport) __stdcall EvideoOpenRoom(BSTR RoomIP, BSTR ServerIP, int iFlag)
好像不能用vb调用,vb调用要用stdcall

上面字符的讨论太复杂了,msdn有了呀,可以稍微超越下msdn,但是飞不远吧

将字符串传递到 DLL 过程


通常,字符串应该使用 ByVal 方式传递到 APIs。Visual Basic 使用被称为 BSTR 的 String 数据类型,它是由自动化(以前被称为 OLE自动化)定义的数据类型。一个 BSTR 由头部和字符串组成,头部包含了字符串的长度信息,字符串中可以包含嵌入的 null 值。BSTR 是以指针的形式进行传递的,因而 DLL 过程能够修改字符串。(指针是一个变量,包含另外一个变量的内存地址,而不是数据。) BSTR 是 Unicode 的,即每个字符需要两个字节。BSTR 通常以两字节的 null 字符结束。

图 1.2 BSTR 类型(每一个框表示两个字节)

DLL 中的大部分过程(以及 Window API 中的所有过程)能够识别 LPSTR 类型,这是指向标准的以 null 结束的 C 语言字符串的指针,它也被称为 ASCIIZ 字符串。LPSTR 没有前缀。下图显示了一个指向 ASCIIZ 字符串的 LPSTR。

图 1.3 LPSTR 类型

如果 DLL 过程需要一个 LPSTR(指向以 null 结束的字符串的指针)作为参数,可以将 BSTR 以使用值方式传递给它。因为指向 BSTR 的指针实际指向以 null 值结束的字符串的第一个数据字节,对于 DLL 过程来说,它就是一个 LPSTR。

例如,sndPlaySound 函数接受一个数字声音 (.wav) 文件名,然后演奏该文件。

Private Declare Function sndPlaySound Lib "winmm.dll" _
Alias "sndPlaySoundA" (ByVal lpszSoundName As String, _
ByVal uFlags As Long) As Long

因为该过程的字符串参数被声明为 ByVal,Visual Basic 将传递一个 BSTR,该 BSTR 指向第一个数据字节:

Dim SoundFile As String, ReturnLength As Long
SoundFile = Dir("c:\Windows\System\" & "*.wav")
Result = sndPlaySound(SoundFile, 1)

通常,如果 DLL 过程需要 LPSTR 参数,那么使用 ByVal 关键字。如果 DLL 需要得到指向 LPSTR 的指针,则使用引用方式传递 Visual Basic 字符串。

如果要将二进制数据传递到 DLL 过程,可以将变量作为 Byte 数据类型的数组传递,不要将其作为 String 变量。字符串是假定用来包含字符的,如果将二进制数据作为 String 变量传递,外部程序可能无法正确读入数据。

假设声明了一个字符串变量,但没有初始化它,如果将其以使用值方式传递到 DLL,该字符串变量将作为 NULL 传递,而不是作为空字符串 ("")。为了消除代码中的混淆,如果要将 NULL 传递到 LPSTR 参数,请使用 vbNullString 常数。

将字符串传递到使用自动化的 DLL
某些 DLL 是专门使用 BSTR 等自动化数据类型的,它们利用了自动化提供的若干过程。

因为 Visual Basic 使用自动化数据类型作为自己的数据类型,所以能够使用引用方式将 Visual Basic 参数传递到需要自动化数据类型的任何 DLL。因此,如果 DLL 过程需要以 Visual Basic 字符串作为参数,就不必用 ByVal 关键字来声明参数,除非该过程确实需要以使用值方式传递字符串。

某些 DLL 过程可以返回字符串到调用它的过程。除非 DLL 函数是专门为自动化数据类型而编写的,否则它将不能返回字符串。如果确实能够返回字符串,该 DLL 可能会提供对过程进行描述的类型库。请参考该 DLL 的有关文档。

详细信息 关于自动化数据类型,请参阅 Microsoft Press 出版的 OLE 2 PROGRAMMER'S REFERENCE。

修改字符串参数的过程
DLL 过程能够修改作为参数输入的字符串变量的数据。不过,如果修改后的数据超过了原来的长度,过程的修改将越界(越过字符串的结尾),这可能会毁坏其它的数据。

要避免这个问题,一种办法是使字符串参数足够长,从而使 DLL 过程无法超出字符串的尾部。例如,GetWindowsDirectory 过程在第一个参数中返回了 Windows 目录的路径:

Declare Function GetWindowsDirectory Lib "kernel32" _
Alias "GetWindowsDirectoryA" (ByVal lpBuffer As _
String, ByVal nSize As Long) As Long

在调用该过程时,为了安全起见,先使用 String 函数在字符串中填充 255 个空字符(二进制的 0),只要返回的路径少于 255 个字符,就不会出问题:

Path = String(255, vbNullChar)
ReturnLength = GetWindowsDirectory(Path, Len(Path))
Path = Left(Path, ReturnLength)

另一个办法是将字符串定义为定长的:

Dim Path As String * 255
ReturnLength = GetWindowsDirectory(Path, Len(Path))

上述方法的目的只有一个:创建一个固定长度的字符串,使之能够包含过程可能产生的最长的字符串。

注意 Windows API 的 DLL 过程通常不需要超过 255 个字符的字符串缓冲区。尽管这对于其它的许多库也是成立的,为了保险起见,最好参考相应过程的文档。

当 DLL 过程需要内存缓冲区时,既可以使用适合的数据类型,也可以使用字节数据类型的数组。
王二.麻子 2011-04-19
  • 打赏
  • 举报
回复
额,自己看错了
[Quote=引用 23 楼 lactoferrin 的回复:]
extern "C" bool __declspec(dllexport) __stdcall EvideoOpenRoom(BSTR RoomIP, BSTR ServerIP, int iFlag)
好像不能用vb调用,vb调用要用stdcall


这个不就是__stdcall?

既然vb6的string是unicode,就可以用StrPtr得到字符地址,然后传给需要unico……
[/Quote]
现在还是人类 2011-04-18
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 lactoferrin 的回复:]
我没有说vb6用的都是unicode的api,我是说我用vb6编程时都用unicode api,你看清楚点
比如我要弹出对话框,我就
declare function MessageBoxW lib "user32.dll" (byval hwnd as long,byval txt as long,byval caption as long,byval flags as long) as l……
[/Quote]

唉,不知你的自信从何而来,竟然说用vb6编程时都用unicode api,
我不清楚你的API从开始是怎么用的(可能你比较独特吧),但我开始
接触,是通过 VB 自带的 API 浏览器来取得 API 函数的声明的,相
信大多数VB爱好者也都是这么用过来的,但是,API 浏览器中默认的
MessageBox 函数是用的这个。

Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long

看清楚,是 Alias "MessageBoxA",还有用到字符串很多函数都是如此,比如:
Private Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long

相信不是有特殊需求的开发人员,很少会去改动声明或自己去定义。
但从底层设计来说,微软怎么分配空间来存储都是可以的,我也说过
即使他用unicode或UTF-8也不奇怪,但常用的接口还是普通的ASCII码。

还有,你说BSTR是在COM接口中使用,我这么个用法有问题,请问你到底对
COM 模型了解多少,对 VC 又了解多少?语气还挺狂妄,如果真如你所说
没有这样的必要,何必还有 SysAllocStringByteLen 这种函数存在的价值?
这肯定与具体的程序和需求有关,我常这么用是因为我有这样的需求,有什么
问题?难道你比我更清楚我的需求是什么?程序架构和接口该如何定?

如果你不会VC你就不要和我再继续争论了,连个类型定义都不知道如何找,
你还大言不惭的举例说什么 HWND 类型,如果你真的找不到,我告诉你,BSTR
类型定义在 Afxwin.h 里,内容为
typedef OLECHAR* BSTR;

而 OLECHAR 类型也在 Afxwin.h 里,内容为
typedef WCHAR OLECHAR;

而 WCHAR 的定义在 WinNT.h 里,内容为
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character

而 wchar_t 的定义在 CType.h 里,内容为
typedef unsigned short wchar_t;

所以 BSTR 的最终类型仅仅是 unsigned short * 而已,不会跑出什么其他
的东西。连这种东西都搞不清楚,都不知道你争论个什么劲。
现在还是人类 2011-04-18
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 lactoferrin 的回复:]
vb6的自带的api声明很多都是ansi的,这是因为vb6出来时流行的还是win9x,它们的核心用的是ansi字符串

但是现在大部分都是windows nt,它们的核心主要用unicode字符串,你去看一下ntdll.dll的系统服务,大部分用字符串的都是unicode,你说的mciSendStringA实际上就是对mciSendStringW的封装,许多其他的都是,比如CreateFile……
[/Quote]

首先,我先对上面我说话的语气有点重表示道歉,因为这是技术讨论,不应该有这样的语气出现,
会不利于技术讨论,对此我再次表示歉意。
再者,就是和你继续讨论这个技术问题。

你也说了,VB6出现的时候流行的是Win9x,还没有Win2k和XP的出现,所以VB6的API都是以
ASCII为主。即使至今,VB6还是原来的VB6,还是以ASCII为主的,这个可没变,所以做VB6
接口的时候,用 char * 作为接口,是很符合VB6的操作模式的。
再来说说 Unicode,我一直没有否认 Unicode 的重要性,也不否认在 VB 的 String 类型
里核心存储的内容可能 Unicode 内容,因为我自己在 VC 里也模拟过 VB 的 String 类型
做过一个 VBString 类,我也是以 Unicode 作为核心指针的编码模式为基础,然后通过重载
运算符的多态操作来实现返回或设置编码的转换过程,如:

//......
char * operator =(char * New_Buffer){
set_value(New_Buffer);
return get_value();
}
WCHAR * operator =(char * New_Buffer){
set_value(New_Buffer);
return get_valuew();
}
WCHAR * operator =(WCHAR * New_Buffer){
set_valuew(New_Buffer);
return get_valuew();
}
char * operator =(WCHAR * New_Buffer){
set_valuew(New_Buffer);
return get_value();
}
//......

至于当初在设计VB的时候是不是这么个过程方式,我不是很清楚,但是我认为这种方式很可能也
是VB的处理方式,所以无论你的接口是ASCII或Unicode,VB都应该能很好的处理这个转换过程。

至于你说的“为什么要定义BSTR而不直接用LPWSTR”,其实这个VC的严格的类型要求有关,其实
你也可以尝试强制转换类型的方法来操作,相信同样能行得通,如:

LPWSTR aa;
BSTR bb;
bb = (BSTR)aa;

再者就是不要不相信你看到的VC定义,很多东西其实没那么复杂,不会无中生有的加入什么概念。
即使有,也是系统内核部分的事情,不会影响到应用。
Lactoferrin 2011-04-18
  • 打赏
  • 举报
回复
vb6的自带的api声明很多都是ansi的,这是因为vb6出来时流行的还是win9x,它们的核心用的是ansi字符串

但是现在大部分都是windows nt,它们的核心主要用unicode字符串,你去看一下ntdll.dll的系统服务,大部分用字符串的都是unicode,你说的mciSendStringA实际上就是对mciSendStringW的封装,许多其他的都是,比如CreateFileA,CreateProcessA等,你可以去反汇编,所以我主要用unicode api是有理由的,OutputDebugStringW除外,因为它是封装OutputDebugStringA的

至于BSTR的问题,msdn上已有说明,不再跟你解释,虽然它在vc的定义只是unsigned short*,但是含义不仅在此,否则为什么要定义BSTR而不直接用LPWSTR?

我会用vc,所以我可以继续和你论,wchar_t在新的vc中都被默认设置成内置类型,可见unicode越来越重要


Lactoferrin 2011-04-18
  • 打赏
  • 举报
回复
当没有本地版msdn和不能上网时或查一些简单的类型定义,我也常去看头文件,但是有的信息它的确没有
比如CreateWindowEx的lpClassName参数,仅看头文件就会认为lpClassName只能是字符串,但是它也可以是RegisterClassEx的返回值,是16位的

EvideoOpenRoom的BSTR的确是宽字符串,看这里
http://topic.csdn.net/u/20110415/17/5d30118a-15ff-446a-9881-07bdf0a50414.html
由于EvideoOpenRoom在dll中,不易改变,因此只能在调用者中将就它,declare成Long型然后用StrPtr传入字符串的地址
现在还是人类 2011-04-16
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 lactoferrin 的回复:]
原型接口不能改的,这样内部实现都要变。vb本来就是用的宽字符,wchar_t正好。
[/Quote]
“vb本来就是用的宽字符”,在传递内存指针的时候,你用VC测试过VB传递过去的内存数据吗?
指针支持一块内存地址,而指针内的数据是最能真实反映情况的。
Lactoferrin 2011-04-16
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 supermanking 的回复:]
C/C++ code

typedef unsigned short wchar_t;
typedef wchar_t WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR* BSTR;
// 所以 BSTR 就是 unsigned short 的指针类型,即数据类型为 16 位的空间指针
// unsigned short *



如……
[/Quote]
原型接口不能改的,这样内部实现都要变。vb本来就是用的宽字符,wchar_t正好。
现在还是人类 2011-04-16
  • 打赏
  • 举报
回复

typedef unsigned short wchar_t;
typedef wchar_t WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR* BSTR;
// 所以 BSTR 就是 unsigned short 的指针类型,即数据类型为 16 位的空间指针
// unsigned short *


如果你可以改动 原型接口,建议改为 char * 接口,如:

extern "C" bool __declspec(dllexport) __stdcall
EvideoOpenRoom(char *RoomIP, char *ServerIP, int iFlag)

这样VB定义按你的定义就可以用了
Lactoferrin 2011-04-16
  • 打赏
  • 举报
回复
还有,BSTR 就是个很简单的 unsigned short * 类型,并非你说的那么复杂,不相信你可以进
VC 看看他的原型,这个是很明文的写在那的。虽然有一点你说对了


VC的头文件并不会告诉你所有细节,那么你去看看HWND的定义:struct HWND__{ int unused}*HWND;
那么HWND就是一个指向struct{int unused}的指针吗?明显不是。
Lactoferrin 2011-04-16
  • 打赏
  • 举报
回复
还有,使用 BSTR 未必内部也是用 WCHAR* 来操作,因为只要用一两个转换函数就可以将 BSTR
转 char* 了,我就常这么用,没什么不可以的。


这个习惯就不好了,那我还用float*来指向字符串呢
Lactoferrin 2011-04-16
  • 打赏
  • 举报
回复
我没有说vb6用的都是unicode的api,我是说我用vb6编程时都用unicode api,你看清楚点
比如我要弹出对话框,我就
declare function MessageBoxW lib "user32.dll" (byval hwnd as long,byval txt as long,byval caption as long,byval flags as long) as long

然后MessageBoxW 0,StrPtr("aaa"),0,0

vb6的String默认是unicode,这是正确的,看这里
http://msdn.microsoft.com/en-us/library/aa261360(VS.60).aspx


BSTR通常为COM所用,在windows中用来放宽字符,详情见
http://msdn.microsoft.com/en-us/library/ms221069.aspx




现在还是人类 2011-04-16
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 lactoferrin 的回复:]
vb6内部缺省使用宽字符是公认的事实,你可以自己去试,我在vb6用win32api都用的unicode版本,用StrPtr传递字符串地址

EvideoOpenRoom使用的是宽字符参数,它内部肯定会用宽字符处理程序,根据BSTR的名字,它很可能是和COM有关的,如果修改了参数,内部的实现都要发生很大的改变
[/Quote]
首先,VB6的内部是不是使用宽字符这还不能很确定,你说的VB6用的都是unicode版本的 API
更是你自己的胡乱猜想,不相信你可以搜索一下VB程序必备的DLL,msvbvm60.dll,里面就有
MessageBoxA这么个函数申明,反而找不到MessageBoxW。至于VB内部的String变量到底是怎
么存储的,除了Microsoft的开发者站出来说明,其他的都是自己猜想。因为在VC里有这么个概
念,就是重载和多态,在设计VB的String变量时完全可以采用多态的方法设置返回的内容为
char 或 WCHAR,完全可以根据使用情况来定,至于String原本使用 char 还是 WCHAR 存储,
都不会影响到多态的返回,只是猜想用 WCHAR 会更全面一点,但是也难保证他是用 UTF-8 也
不一定,谁知道呢,只有 Microsoft 最清楚。再多理论去猜也不会是权威答案。但是,不可否
认的是,String 确实可以存放 char* 内容和 WCHAR* 内容。

还有,使用 BSTR 未必内部也是用 WCHAR* 来操作,因为只要用一两个转换函数就可以将 BSTR
转 char* 了,我就常这么用,没什么不可以的。

还有,BSTR 就是个很简单的 unsigned short * 类型,并非你说的那么复杂,不相信你可以进
VC 看看他的原型,这个是很明文的写在那的。虽然有一点你说对了,通常,WCHAR* 是以两个
0x00结尾,但是,我说过 WCHAR* 只不过是个 unsigned short 指针而已,这种规则只能是运
用的时候程序员自觉遵守的规则而已,并不是像 VB 一样,你赋值的时候系统会自动帮你加两个
0x00上去,所以这并不能代表什么,无非是个字节数为 0 或双数的内存指针而已罢了,不会有多特别。
捧剑者 2011-04-16
  • 打赏
  • 举报
回复
调用前byte数组末尾要先赋值为0.
捧剑者 2011-04-16
  • 打赏
  • 举报
回复
调用的时候,用EvideoOpenRoom(strRoomIP(0),strServerIP(0),nFlag)
捧剑者 2011-04-16
  • 打赏
  • 举报
回复
试试:
Public Declare Function EvideoOpenRoom Lib "SendWineEV.DLL" (strRoomIP As Any, strServerIP As Any, ByVal nFlag As Long) As Long

Dim strServerIP() As byte, strRoomIP() As byte

The WideString type represents a dynamically allocated string of 16-bit Unicode characters. In most respects it is similar to AnsiString. On Win32, WideString is compatible with the COM BSTR type.

所以,widestring 与vb6中的string相同,但是在vb6调用api时,会自动将string中的unicode转化为ansi。用byte数组可以避免这样的转换。
码之魂 2011-04-16
  • 打赏
  • 举报
回复
Lactoferrin 2011-04-16
  • 打赏
  • 举报
回复
还有一点就是BSTR并不是一个简单的字符指针,它指向的字符串应以两个0字符结尾,这个指针指向的地址的前4字节包含字符串的字节数,传递时这些应该准备好,否则如果EvideoOpenRoom调用了某些COM函数,会出问题
加载更多回复(3)

1,486

社区成员

发帖
与我相关
我的任务
社区描述
VB API
社区管理员
  • API
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧