导航:首页 > 文件管理 > 导出文件找不到成员

导出文件找不到成员

发布时间:2024-10-13 11:51:10

⑴ VB 如何获得DLL导出函数在其他进程中的地址

关于公用系统DLL的版本用API写程序,经常少不了对Windows公用组件的调用,比如打开、保存对话框、选择文件夹对话框等,都是多数程序都使用的作为打开、保存文件时的标准对话框,因为这些组件是由Windows自己提供的,除了方便,还可以让自己的程序和其它Windows程序有相同的风格,方便用户使用。但是由于这些组件是由Windows提供,随着Windows的升级、系统软件的更新,这些组件可能也被更新了,就可能出现各台计算机上文件版本不同。一般地,软件针对低版本公用系统文件的设计,在高版本中使用是没问题,但是反过来就不一定,因为可能针对高版本的设计中使用到低版本中没有的功能,所以在设计程序的时候,根据文件的版本来做适当调整是很有需要的。这一话我要向你介绍一个获得公用组件版本的办法--使用DllGetVersion().
DllGetVersion并不是一个一定可以使用的API.微软在自己的新版本的公用系统DLL中使用了这个函数,但是早期版本的系统DLL中并没有,而这个函数并不是一个获得其它DLL的版本的函数,而是由DLL自己告诉你它的版本,所以你不可以把它当成一个一般API来看待。那么怎么使用它呢?为了得知要调用的DLL是否有这个函数,我找到了这个方法:用GetProcaddress().
GetProcAddress()能访问一个DLL,返回一个指定的函数的入口地址。使用这个函数,还需要另外两个API:
LoadLibrary()和 FreeLibrary().以下是声明:
Private Declare Function LoadLibrary Lib “kernel32”Alias“LoadLibraryA”(ByVal lpLibFileName As String)As LongPrivate Declare Function FreeLibrary Lib “kernel32”(ByValhLibMole As Long)As LongPrivate Declare Function GetProcAddress Lib “kernel32”(ByValhMole As Long,ByVal lpProcName As String)As Long比如你要获得 COMCTL32.DLL 的版本(如果是其它系统DLL 的话,也应该每个都声明),就要按下面这样声明(虽然你不知道是否有这个函数在该DLL 中):
Private Declare Function DllGetVersion Lib “COMCTL32”(pdviAs DLLVERSIONINFO)As Long这个函数使用到了一个DLLVERSIONINFO 的用户定义类型:
Private Type DLLVERSIONINFOcbSize As LongdwMajor As LongdwMinor As LongdwBuildNumber As LongdwPlatformID As LongEnd TypeDLLVERSIONINFO 类型是由微软规定,无论你要访问哪个系统DLL,如果它有DllGetVersion 这个函数,那么它的参数也是这个类型。
接下来就可以这么做:
Dim hmod As LongDim lR As LongDim lptrDLLVersion As LongDim tDVI As DLLVERSIONINFOhmod =LoadLibrary(“comctl32.dll”)If (hmod <>0)ThenlptrDLLVersion =GetProcAddress(hmod,“DllGetVersion”)If (lptrDLLVersion <>0)ThentDVI.cbSize =Len(tDVI)lR =DllGetVersion(tDVI)If (lR =&H0)ThenDebug.Print “Major:”&tDVI.dwMajor,“Minor:”&tDVI.dwMinor,“Build:”&tDVI.dwBuildNumberEnd IfElseDebug.Print “DllGetVersion Failed”
End IfFreeLibrary hmodEnd If上面这一段就是整个过程。首先用LoadLibrary把系统DLL装载进内存,如果成功就返回非0,然后对返回的句柄执行GetProcAddress,这时是在假设DLL中有DllGetVersion这个函数的情况下调用的,如果成功,GetProcAddress返回非0,如果失败(包括找不到函数),则返回0,所以我们就可以知道DllGetVersion()函数可不可以调用。
调用DllGetVersion()时,需要为DLLVERSIONINFO类型参数设置好cbSize,它告诉DLL你的参数占用多少内存。如果调用DllGetVersion成功,返回值是0,这时dwMajor、dwMinor和dwBuildNumber分别是主、副版本号和编译号,而dwPlatformID是DLL的使用平台:NT(DLLVER_PLATFORM_NT)还是WIN9X(DLLVER_PLATFORM_WINDOWS).其中,DLLVER_PLATFORM_NT值是&H2,DLLVER_PLATFORM_WINDOWS值是&H1,这是API浏览器中找不到的,记下来吧。
最后注意要用FreeLibrary把系统DLL从内存中释放,不然它将一直占用系统资源。
第十六话 最后的礼物这是《细水长流话API》的最后一节,从最早的《从消息说起》到上一期的《回调》,我们一起度过了许多时光,用到了许多可以说是使用API的必备知识,现在我们就来完成一个综合的题目,作为本次连载的压轴。
图1是大家经常看过的选择文件夹对话框,它是常用的Windows公用组件之一,当然,这里吸引你的并不只是如何显示这个对话框,还有图2,在图1的基础上为它初始化了选择的文件夹,并在上面添加了一个可以让你直接输入文件夹路径的文本框。它是怎么做的?相信这是许多人迫切想知道的。
最初我们需要用到以下的API和用户定义类型:
Public Declare Function SHBrowseForFolder Lib “shell32”(lpbiAs BrowseInfo)As LongPublic Declare Function SHGetPathFromIDList Lib “shell32”(ByValpidList As Long,ByVal lpBuffer As String)As LongPublic Type BrowseInfohWndOwner As LongpIDLRoot As LongpszDisplayName As LonglpszTitle As LongulFlags As LonglpfnCallback As LonglParam As LongiImage As LongEnd TypeSHBrowseForFolder调用shell32.dll中的函数,它不同于CommonDialog等在comctl.dll中的函数,而是外壳函数,主要执行系统已经存在的一些功能。我很愿意向你逐一解释BrowseInfo的所有成员的作用,但是我觉得已经没有必要了,因为你已经学到了这里,是时候自己领悟一下了,所以我只说明示例中我认为需要说明的:
hWndOwner 设置所属窗体句柄,它将一直在该窗体之上lpszTitle 指向标题的指针ulFlags 设置对话框的参数,例如:
BIF_BROWSEFORCOMPUTER 只返回计算机,选择其它文件夹将无法点击确定按钮BIF_BROWSEFORPRINTER 只返回打印,选择其它文件夹将无法点击确定按钮BIF_EDITBOX 添加一个输入路径的文本框,需要shell32.dll 版本在4.71 以上BIF_RETURNONLYFSDIRS 只返回本系统的文件夹,选择的文件夹如果不在本机,将无法点击确定按钮BIF_DONTGOBELOWDOMAIN 不显示网络文件夹我需要说明lpszTitle应如何使用。一般情况下,你可以通过我以前讲的办法给它传递一个字符串的指针,但是你马上会发现,假如你正确的使用StrPtr(“Look!”),你却得到了错误的结果:只有一个“L”字母。更糟的是,如果你使用中文,它显示的是一堆乱码。
这是为什么?我说过,Windows9x和部分Windows NT的API使用ANSI字符集,但是VB使用UNICODE.这个情况下,“Look!”在内存中表示成16进制是6C 00 6F 00 6F 00 21 00,API用NULL(即0)表示字符串结束,这就是原因,所以我们需要找一个让lpszTitle指向ANSI字符集的办法。
你可能见过一些人(包括许多国外程序员写的示例)使用这样一种方法:lstrcat(“Look!”,“”),没错,lstrcat把两个字符串连接起来,并返回指针,因为字符串传递给API时已经由VB转换为ANSI字符了,所以它看起来似乎没问题。但是我要建议你不要这样做,因为它可以让你的指针成为无效指针,不信你试试在它返回指针之后,先用Debug.Print打印一次指针(或用其它方法访问这个变量一次)后再使用这个指针,你的指针的返回结果会变成没用的。那么你应该怎么做呢?
Dim szTitle() As ByteszTitle=StrConv(“Look!”,vbFromUnicode)lpszTitle=VarPtr(szTitle(0))上面就是我的办法。我用数组存放,并用StrConv把字符串转换为ANSI的。
在设置好所需要的值给BrowseInfo类型的变量后,就可以调用SHBrowseForFolder了。当选择对话框显示过后(你按了“确定”或“取消”),SHBrowseForFolder会返回一个值(取消则返回0),它又是一个指针,这个指针指向你选择的路径,不过不要试图用CopyMemory把这个指针指向的内容当成字符串般复制下来,你应该使用SHGetPathFromIDList取回(类似函数我已经讲过使用方法,这里不再说明).
用这种方法调用的对话框,就如图1,如果你需要初始化路径,那么你要用到回调。lpfnCallback就是指向函数入口的指针。不过不幸的是,AddressOf是关键字,所以你无法把AddressOf得到的值直接赋给变量,怎么办?
Public Function MyAddressOf(AddressOfX As Long)AsLongMyAddressOf =AddressOfXEnd Function上面是MyAddressOf 函数,我们变通一下,把AddressOf 的得到的值传递给自己的一个函数,再由这个函数返回AddressOf 的结果。通过MyAddressOf(AddressOf functionA)就可以得到 AddressOf functionA 的结果了。
那么接下来是SHBrowseForFolder 所要求的回调函数的形式:
Public Function BrowseForFoldersProc(ByVal hWnd As Long,ByVal uMsg As Long,ByVal lParam As Long,ByVal lpData AsLong)As LonghWnd 是对话框的句柄,uMsg 是消息,lParam 随着uMsg 的不同,有时有作用,有时没有,而lpData,如以前所说过的一些回调函数,也是由你的程序来定,API把它传给回调函数,与之相对应的是BrowseInfo 中的lParam .
这个回调函数在浏览文件夹对话框发送不同消息时被调用,BFFM_INITIALIZED 消息在对话框初始化完成时被发送,在这个时候,我们就可以为对话框设置选择哪个文件夹:用 SendMessage .
SendMessage hWnd,BFFM_SETSELECTIONA,1,ByVallpData我事先为BrowseInfo 类型变量中的lParam 设置好要初始化的文件夹的路径(ANSI 的),当回调函数被调用并且是发生BFFM_INITIALIZED 消息时,我向对话框发送BFFM_SETSELECTIONA 消息,使对话框选择某个文件夹。当然如果你不使用lpData,你也可以在要发送时再把路径的指针作为参数发送,它们没什么不同的。
最后,如果API 成功返回了一个路径,我建议你调用一下CoTaskMemFree:
Public Declare Sub CoTaskMemFree Lib “ole32.dll”(ByValhMem As Long)CoTaskMemFree 的参数是指向一段内存的指针,由于SHBrowseForFolder 返回了一个指向路径的指针,即是说它可能在内存中保留了一部分资源,CoTaskMemFree则可以把这资源回收。虽然如果你不这么做可能不会马上发现问题,但是小心一点总是好的。
好了,除去声明,我把我的整个调用过程列出如下:
Const MAX_PATH =260Dim lpIDList As LongDim sBuffer As StringDim tBrowseInfo As BrowseInfoDim szTitle()As ByteDim sPath()As ByteszTitle =StrConv(“你要选择哪个文件夹?”&vbNullChar,vbFromUnicode)sPath =StrConv(“C:\ ”&vbNullChar,vbFromUnicode)With tBrowseInfo.hWndOwner =Me.hWnd.lpszTitle =VarPtr(szTitle(0)).ulFlags =BIF_RETURNONLYFSDIRS OrBIF_DONTGOBELOWDOMAIN Or BIF_EDITBOX.l p f n C a l l b a c k =M y A d d r e s s O f (A d d r e s s OfBrowseForFoldersProc).lParam =VarPtr(sPath(0))End WithlpIDList =SHBrowseForFolder(tBrowseInfo)If (lpIDList)ThensBuffer =Space(MAX_PATH)SHGetPathFromIDList lpIDList,sBufferCoTaskMemFree lpIDListsBuffer =Left(sBuffer,InStr(sBuffer,vbNullChar)-1)MsgBox sBufferEnd If以下是标准模块中的回调函数:
Public Function BrowseForFoldersProc(ByVal hWnd As Long,ByValuMsg As Long,ByVal lParam As Long,ByVal lpData As Long)As LongSelect Case uMsgCase BFFM_INITIALIZEDSendMessage hWnd,BFFM_SETSELECTIONA,1&,ByVallpDataCase BFFM_SELCHANGED'Selection changedEnd SelectEnd Function注意看我使用字符串的那两个地方,记得结尾加上vbNullChar 以表示字符串结束。
好了,关于这一个的内容我就只讲这么多,有不明白的地方,可以参考上面我的调用过程。一些地方我没有讲,我相信你已经有能力自己进一步去探讨了。关于这个对话框的更复杂的使用,我想MSDN 应该是最好的辅助工具书了。
由于API 浏览器中查不到我给出的常量的值,所以我把这个API 常用到的一些值帮你列在下面:
浏览文件夹的常量:
BIF_RETURNONLYFSDIRS =1BIF_DONTGOBELOWDOMAIN =2BIF_STATUSTEXT =4BIF_RETURNFSANCESTORS =8BIF_EDITBOX =&H10BIF_VALIDATE =&H20BIF_BROWSEFORCOMPUTER =&H1000BIF_BROWSEFORPRINTER =&H2000BIF_BROWSEINCLUDEFILES =&H4000对话框发出的消息:
BFFM_INITIALIZED =1BFFM_SELCHANGED =2BFFM_VALIDATEFAILEDA =3BFFM_VALIDATEFAILEDW =4发给对话框的消息:
WM_USER =&H400BFFM_SETSTATUSTEXTA =(WM_USER +100)BFFM_ENABLEOK =(WM_USER +101)BFFM_SETSELECTIONA =(WM_USER +102)BFFM_SETSELECTIONW =(WM_USER +103)BFFM_SETSTATUSTEXTW =(WM_USER +104)第十七话 再见很高兴你一直看到这里,但愿我在这次连载中没有出什么大差错。我也相信你开始喜欢上了API .API 可以帮助VB 完成许多别人认为不可能的事情。我不禁又要提起自己的NaviEdit,不仅我用它赚钱(笑),我更重视的是,它是我从初次接触API 到熟练使用API 的见证。例如正当许多人正不知从何入手制作Office XP 风格的菜单的时候,我在去年年末就用VB 为NaviEdit 写出了这种风格的菜单(见图3),我靠的是API,同时也是靠一系列的方法。是谁说VB 就不好呢?我就很喜欢用。如果你想得心应手的使用VB,那么API 是必不可少的。虽然关于GDI 方面,我未能在本次连载中讲到,但我相信你已经有独立进入更深一层研究的坚实基础。记住这些基础,继续学习,并应用到实际中去,加油吧!再见!

阅读全文

与导出文件找不到成员相关的资料

热点内容
南网在线app增值板块名称是什么 浏览:139
c文件拷贝和覆盖 浏览:568
西门子编程软件的m00是什么 浏览:753
java结束本次循环 浏览:619
大网站一个月赚多少钱 浏览:820
卸载什么软件会影响移动数据功能 浏览:212
linuxgrub详解 浏览:245
扫描文件存在哪里 浏览:64
win10下载的文件名缺字少字 浏览:403
检测数据的机器有哪些 浏览:536
为什么cad文件显示找不到文件 浏览:463
什么网站可以查企业的成本费用 浏览:289
导出文件找不到成员 浏览:862
电脑desk文件夹在哪里 浏览:435
晨学是什么app有 浏览:449
java判断对象存在 浏览:435
机器对中数据怎么相加 浏览:94
电脑文件怎么上传微信 浏览:665
剑灵最新版本暴击八卦牌搭配 浏览:677
神经网络语言模型 浏览:757

友情链接