我目前正在将Windows 7支持添加到现有的Vb6项目中,并且我遇到了使用SHGetFolderPath定位特殊文件夹路径的问题,这在Vista开始的Windows版本上不支持。我知道我应该使用SHGetKnownFolderPath,但我找不到一个很好的例子,在VB6中实现使用SHGetKnownFolderPath API调用。
更容易使用Shell对象建议延迟绑定,因为Microsoft没有仔细考虑与此对象的兼容性。
Const ssfCOMMONAPPDATA = &H23
Const ssfLOCALAPPDATA = &H1c
Const ssfAPPDATA = &H1a
Dim strAppData As String
strAppData = _
CreateObject("Shell.Application").NameSpace(ssfAPPDATA).Self.Path
从shfolder.dll
使用SHGetFolderPath
在Vista和Win7下工作良好:
Private Declare Function SHGetFolderPath Lib "shfolder" Alias "SHGetFolderPathA" (ByVal hWnd As Long, ByVal csidl As Long, ByVal hToken As Long, ByVal dwFlags As Long, ByVal szPath As String) As Long
然后在这些CSIDL_Xxx
常量上声明一个enum:
Public Function GetSpecialFolder(ByVal eType As MySpecialFolderType) As String
GetSpecialFolder = String(1000, 0)
Call SHGetFolderPath(0, eType, 0, 0, GetSpecialFolder)
GetSpecialFolder = Left$(GetSpecialFolder, InStr(GetSpecialFolder, Chr$(0)) - 1)
End Function
使用以下代码本文vba/vb6在模块WINAPI32.bas的顶部声明API调用
Private Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" _
(ByVal hwndOwner As Long, ByVal nFolder As Long, _
pidl As ITEMIDLIST) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
(ByVal pidl As Long, ByVal pszPath As String) As Long
Private Type SHITEMID
cb As Long
abID As Byte
End Type
Private Type ITEMIDLIST
mkid As SHITEMID
End Type
新增公共函数:
Public Function SHGetSpecialFolderLocationVB(ByVal lFolder As Long) As String
Dim lRet As Long, IDL As ITEMIDLIST, sPath As String
lRet = SHGetSpecialFolderLocation(100&, lFolder, IDL)
If lRet = 0 Then
sPath = String$(512, chr$(0))
lRet = SHGetPathFromIDList(ByVal IDL.mkid.cb, ByVal sPath)
SHGetSpecialFolderLocationVB = Left$(sPath, InStr(sPath, chr$(0)) - 1)
Else
SHGetSpecialFolderLocationVB = vbNullString
End If
End Function
添加了一个新功能来检查Windows版本Vista或更高版本
Public Function IsVistaOrHigher() As Boolean
Dim osinfo As OSVERSIONINFO
Dim retvalue As Integer
Dim bVista As Boolean
bVista = False
osinfo.dwOSVersionInfoSize = 148
osinfo.szCSDVersion = Space$(128)
retvalue = GetVersionExA(osinfo)
If osinfo.dwPlatformId = 2 Then
If osinfo.dwMajorVersion >= 6 Then
bVista = True
End If
End If
IsVistaOrHigher = bVista
End Function
修改了之前调用SHGetFolderPath的方法
Public Function SHGetFolderPathVB(ByVal lFolder As Long) As String
Dim path As String
If IsVistaOrHigher() Then
SHGetFolderPathVB = SHGetSpecialFolderLocationVB(lFolder)
Else
path = Space$(MAX_PATH)
SHGetFolderPath 0, lFolder, 0, SHGFP_TYPE_CURRENT, path
SHGetFolderPathVB = Left(path, InStr(path, vbNullChar) - 1)
End If
End Function
作品太棒了!
一个迟来的回答。但它实际上展示了如何在x64 VBA中使用SHGetKnownFolderPath
,并且没有解决方法来避免它。
我使用了这个德语来源:https://dbwiki.net/wiki/VBA_Tipp:_Spezielle_Verzeichnisse_ermitteln
给出的解决方案不适用于x64 Office。所以我改了名字。从VBA调用本机DLL需要
- 新关键字
PtrSafe
的使用情况。 - 所有指针使用
LongPtr
而不是Long
。 - 通过
StrPtr
函数将VBA字符串转换为LongPtr
对象。 - 调用Unicode版本的DLL,通常用"w"标记
Public Const FOLDERID_ProgramFiles1 As String = "{905E63B6-C1BF-494E-B29C-65B732D3D21A}"
Public Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Public Const S_OK As Long = 0
Public Const WIN32_NULL As Long = 0
Public Declare PtrSafe Sub CoTaskMemFree Lib "ole32" (ByVal hMem As LongPtr)
Public Declare PtrSafe Function CLSIDFromString Lib "ole32" ( _
ByVal lpszGuid As LongPtr, _
ByRef pGuid As GUID) As Long
Public Declare PtrSafe Function lstrlenW Lib "kernel32" ( _
ByVal lpString As LongPtr) As Long
Public Declare PtrSafe Function SHGetKnownFolderPath Lib "shell32" ( _
ByRef rfid As GUID, _
ByVal dwFlags As Long, _
ByVal hToken As Long, _
ByRef pszPath As LongPtr) As Long
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As LongPtr, _
ByVal Source As LongPtr, _
ByVal length As Long)
Public Function GetBstrFromWideStringPtr(ByVal lpwString As LongPtr) As String
Dim length As Long
If (lpwString) Then length = lstrlenW(lpwString)
If (length) Then
GetBstrFromWideStringPtr = Space$(length)
CopyMemory StrPtr(GetBstrFromWideStringPtr), lpwString, length * 2
End If
End Function
Public Function GetKnownFolder(ByVal KnownFolderID As String) As String
'Returns empty String on any error.
Dim ref As GUID
Dim pszPath As LongPtr
If (CLSIDFromString(StrPtr(KnownFolderID), ref) = S_OK) Then
If (SHGetKnownFolderPath(ref, 0, WIN32_NULL, pszPath) = S_OK) Then
GetKnownFolder = GetBstrFromWideStringPtr(pszPath)
CoTaskMemFree pszPath
End If
End If
End Function
Sub TestKnownFolder()
MsgBox GetKnownFolder(FOLDERID_ProgramFiles1)
End Sub
在上面的链接中,您可以找到所有FOLDERID_Blah
字符串。