VB6: Windows 10 Known Folders / SHGetKnownFolderPath
In Windows XP, the recommended practice to find special folders was to call SHGetFolderPath(), but this was deprecated starting in Windows Vista. From Vista on, we're supposed to use SHGetKnownFolderPath(). Unfortunately, it's extremely hard to find easy-to-use code for SHGetKnownFolderPath without having to add unnecessary references or components to your project. (typelibs, or scripting objects.)
Thanks to Randy Birch's excellent sample code, here's an easy-to-use module that lets you identify special folders using API calls, no references or components needed. Simply add the attached module to your project, then call the following function:
?KnownFolder(enumerated value)
You can call the following from the debug window to see a list of known folders and their values:
Of the 88 total known folders, only 54 return any value for me. The rest are listed as virtual folders by Randy Birch's sample project. I've moved these 34 virtual folders to the end of the enumeration and wrapped them in compiler directives, letting you easily prevent them from being included. This helps reduce clutter in the enumeration dropdown.
The second compiler directive lets you discard the KnownFoldersList() debug window output routines to reduce the size of the code in your finished project.
Here's the known folder enumeration for reference:
The order of the enumerated values doesn't matter, so feel free to pick and choose any of the virtual folders to include by moving them above the compiler directive. Be sure to make the same change to the two functions: KnownFolderGUID() and KnownFolderName(). Alternately, you could move many (most?) of the values to inside the directive to streamline the enumerated list, showing only those values you might actually need.
Last edited by Ellis Dee; May 15th, 2016 at 06:45 PM.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
This is great for a simple path query.. but just wanted to mention that if you're going to be doing more work with known folders besides just getting the path, there's many advantages to using the feature rich IKnownFolderManager/IKnownFolder shell interfaces, which even lets you load by the legacy CSIDL if you're not familiar with the new known folder GUIDs yet. The example here shows how to load all sorts of info about a folder, and the 2nd post shows how to pop up a menu filled with special folders and their icons with just a few lines of code: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by fafalone
This is great for a simple path query.. but just wanted to mention that if you're going to be doing more work with known folders besides just getting the path, there's many advantages to using the feature rich IKnownFolderManager/IKnownFolder shell interfaces, which even lets you load by the legacy CSIDL if you're not familiar with the new known folder GUIDs yet. The example here shows how to load all sorts of info about a folder, and the 2nd post shows how to pop up a menu filled with special folders and their icons with just a few lines of code: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager
For sure, if you need extended interaction with known folders (like writing your own custom-made Explorer replacement -- which might not be a bad idea in terms of privacy) then that's an excellent tool.
If you just need to know where the paths are and nothing else, that seems like tremendous overkill. The module attached to the OP (now moved to the bottom of the post) is much more lightweight and easy to incorporate in your project. Which is only to be expected considering how much less it does. heh.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
The old flat API calls and ActiveX API in Shell32 still work fine for most purposes and are not going anywhere. There are only a few special folders added post-XP that you really need to call SHGetKnownFolderPath() for in most normal applications anyway.
The only one I've really found all that useful is FOLDERID_Public, and even that isn't needed very often.
As for "easy to use" examples of calling it in VB6 go, we've had tons of examples dating back to 2006 when Vista was in pre-release, far earlier than Randy Birch's thorough but more than a little over-the-top example. Rolling your own is pretty trivial anyway, the MSDN documentation is pretty clear and the call is almost trivial, so pretty much every example boils down to the very same thing anyway.
See KNOWNFOLDERID for a list of the available GUIDs, or find them in your Windows SDK header files (i.e. in the KnownFolders.h file).
Here's a concise rendition:
Code:
Option Explicit
'Define any of these you require:
Public Const FOLDERID_ProgramData As String = "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}"
Public Const FOLDERID_Public As String = "{DFDF76A2-C82A-4D63-906A-5644AC457385}"
Private Const S_OK As Long = 0
Private Const WIN32_NULL As Long = 0
Private Declare Sub CoTaskMemFree Lib "ole32" (ByVal hMem As Long)
Private Declare Function CLSIDFromString Lib "ole32" ( _
ByVal lpszGuid As Long, _
ByRef pGuid As Byte) As Long
Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyW" ( _
ByVal lpString1 As Long, _
ByVal lpString2 As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" ( _
ByVal lpString As Long) As Long
Private Declare Function SHGetKnownFolderPath Lib "shell32" ( _
ByRef rfid As Byte, _
ByVal dwFlags As Long, _
ByVal hToken As Long, _
ByRef pszPath As Long) As Long
Public Function GetKnownFolder(ByVal KnownFolderID As String) As String
'Returns empty String on any error.
Dim guidKFID(15) As Byte
Dim pszPath As Long
If CLSIDFromString(StrPtr(KnownFolderID), guidKFID(0)) = S_OK Then
If SHGetKnownFolderPath(guidKFID(0), 0, WIN32_NULL, pszPath) = S_OK Then
GetKnownFolder = Space$(lstrlen(pszPath))
lstrcpy StrPtr(GetKnownFolder), pszPath
CoTaskMemFree pszPath
End If
End If
End Function
Very similar code has been posted here in answers to question threads numerous times.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by dilettante
Very similar code has been posted here in answers to question threads numerous times.
Got any links? I searched high and low but never found any on these forums.
EDIT: I do like the core logic of yours better. It's a touch cleaner and tighter. But I think an enumeration of the values is a far superior approach for something like this compared to just declaring constants.
EDIT 2: Actually, Randy's method appears to support unicode, while yours does not. Do I have that right?
Last edited by Ellis Dee; May 17th, 2016 at 09:39 PM.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by Ellis Dee
But I think an enumeration of the values is a far superior approach for something like this compared to just declaring constants.
Well you are always free to make up a lookup table keyed by some made up Enum, but since these are GUIDs there isn't any natural enumeration defined anywhere or used anywhere in Windows.
As I said above it would be a rare "real" program that ever needed more than just a few of these folder paths anyway.
There are a few more too, but perhaps they don't turn up in searches because SHGetKnownFolderPath() was just used as the minor tool it is within code snippets.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by dilettante
Well you are always free to make up a lookup table keyed by some made up Enum, but since these are GUIDs there isn't any natural enumeration defined anywhere or used anywhere in Windows.
Agreed. Which is exactly what I did, and why I consider this module easy to use.
As I said above it would be a rare "real" program that ever needed more than just a few of these folder paths anyway.
Originally Posted by dilettante
There are a few more too, but perhaps they don't turn up in searches because SHGetKnownFolderPath() was just used as the minor tool it is within code snippets.
You know, your condescending schtick wears very thin very quickly.
Ah, fair enough. I was confused by Randy's use of lstrlenW vs dilettante's lstrlen, but I think it's just because Randy uses a byte array where dilettante uses a string directly.
Normally when I see a "...W" function I think unicode, and then seeing the same function without the "W" I think "not unicode."
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by Ellis Dee
Ah, fair enough. I was confused by Randy's use of lstrlenW vs dilettante's lstrlen, but I think it's just because Randy uses a byte array where dilettante uses a string directly.
Normally when I see a "...W" function I think unicode, and then seeing the same function without the "W" I think "not unicode."
Originally Posted by dilettante
Code:
Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyW" ( _
ByVal lpString1 As Long, _
ByVal lpString2 As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" ( _
ByVal lpString As Long) As Long
Thanks for sharing this, Ellis. Given the low quality of the forum's in-built search function, I feel like "the more the merrier" when it comes to good code samples.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by Ellis Dee
Ah, fair enough. I was confused by Randy's use of lstrlenW vs dilettante's lstrlen, but I think it's just because Randy uses a byte array where dilettante uses a string directly.
Normally when I see a "...W" function I think unicode, and then seeing the same function without the "W" I think "not unicode."
Ah. That's an issue inherent in looking at mostly VB6 code. C/C++ typically Alias's the different API's (through typedef) to either A or W.
That way you only use "lstrlen" in code, and then you're a typedef away from switching the entire app to use ANSI or Unicode APIs.
I alias all APIs to the W version similar to dilettante. It just makes it easier to compare to C/C++.
As a community, we really need to just get away from posting _any_ code that isn't Unicode compliant.
Last edited by DEXWERX; May 19th, 2016 at 08:25 AM.
Re: VB6: Windows 10 Known Folders / SHGetKnownFolderPath
Originally Posted by dilettante
Code:
Public Function GetKnownFolder(ByVal KnownFolderID As String) As String
'Returns empty String on any error.
Dim guidKFID(15) As Byte
Dim pszPath As Long
If CLSIDFromString(StrPtr(KnownFolderID), guidKFID(0)) = S_OK Then
If SHGetKnownFolderPath(guidKFID(0), 0, WIN32_NULL, pszPath) = S_OK Then
GetKnownFolder = Space$(lstrlen(pszPath))
lstrcpy StrPtr(GetKnownFolder), pszPath
CoTaskMemFree pszPath
End If
End If
End Function
is it me or this is prone to "crash"?
according to MS documentation, I read about lstrlen:
Determines the length of the specified string (not including the terminating null character).
Then, on lstrcpy:
The buffer must be large enough to contain the string, including the terminating null character.
wich is not the case here ? Correct me if I'm wrong plz.
still... some interesting link:
For clarification
guidKFID(15) or guidKFID(0 to 15) is right but using the GUID type would be more "by the book"