Getting the My Documents directory, and more - another take...
Category Technical
Bookmark :
I have been working on a new version of the Signature File Wizard (I'll post it later), and I realized that I had partially hardcoded the "My Documents" directory. I didn't want to do that, so I went to look for programmatic way to discover this directory for the user. I did a quick Google for it, didn't find anything, so then I went to my handy-dandy API Guide (remember this? I blog about it frequently when talking about C API calls) and did a quick search, and discovered the calls to make this happen.
Now, before I proceed, let me give full disclosure. I did another search right before writing this blog entry, and actually found an entry posted a couple of days' ago by Mikkel Heisterberg on his blog (Lekkim's World). He wrote about this exact thing in his Show-n-Tell Thursday post here, but I noticed that the API call he makes is different from the technique I found in the API Guide. So, since variety is the spice of life I am offering my way as well. Is one better than the other? I don't know. His is a bit simpler (one less call), but I do have more contants for finding other interesting directories as well. In any case they both work, so now you have a choice - go crazy!
First, I needed to set up some stuff in the (Declarations) of my code:
Const DESKTOP_DIR = &H0 ' \My Documents Const START_PROGRAMS = &H2 ' \Windows\Start Menu\Programs Const CONTROLS_DIR = &H3 ' returns blank - virtual folder containing icons for the control panel applications Const PRINTERS_DIR = &H4 ' returns blank - virtual folder containing installed printers Const MY_DOCUMENTS = &H5 ' \My Documents Const FAVORITES_GRYPHON = &H6 ' \Windows\Start Menu Const STARTUP_DIR = &H7 ' \Windows\StartUp Const RECENT_DIR = &H8 ' \Recent Const SENDTO_DIR = &H9 ' \SendTo Const BITBUCKET = &HA ' returns blank - file system directory containing file objects in the user recycle bin. ' The location of this directory is not in the registry; it is marked with the ' hidden and system attributes to prevent the user from moving or deleting it. Const STARTMENU_DIR = &HB ' \Windows\Start Menu Const DESKTOPDIRECTORY = &H10 ' seems to be the same as DESKTOP_DIR (&H0) Const DRIVES = &H11 ' returns blank - supposed to be virtual folder containing everything on the ' local computer: storage devices, printers, and Control Panel. Const NETWORK_DIR = &H12 ' returns blank - virtual folder representing the top level of the network hierarchy. Const NETHOOD_DIR = &H13 ' \NetHood Const FONTS_DIR = &H14 ' \Windows\Fonts Const TEMPLATES_DIR = &H15 ' \Templates Const FAVORITES_DIR = &H16 ' \Windows\Favorites Const MY_PICTURES = &H27 ' \My Documents\My Pictures Const MAX_PATH = 260 Private Type SHITEMID cb As Long abID As Byte End Type Private Type ITEMIDLIST mkid As SHITEMID End Type Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" (Byval pidl As Long, Byval pszPath As String) As Long Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" (Byval hwndOwner As Long, Byval nFolder As Long, pidl As ITEMIDLIST) As Long
As you can see, I tested each constant to see what it returned. Some returned nothing, so I looked in API Guide to see what they were supposed to return. In all cases they were "virtual folders", so there is probably a way to get a return value but from this call they return blank. The rest of the constants returned good values, and I have made comments after each constant to let you know what I found. Hope this helps!
Now we have the constants defined, we have defined some small complex data types that are needed for our two calls, and we have declared our Win32 C API functions.
The next thing we need to do is wrap all of this into a simple function that we can call...
This simple little function calls the two C API calls to retrieve the desired special folder, and then trims it up to remove the trailing blank spaces. I also added a bit of error handling to it to fit within my system for error handling.
Conclusion
That's it! Once again I want to thank the folks at AllAPI.net for the great API Guide, which is where this example was born. I simply tweaked it to work in LotusScript.
I also want to once again mention Mikkel's post here, because he posted it first and it does provide a nice way to do the same thing - it is just different.
If you happen to find ways to improve upon this, please share!
Rock
**To me, good exercise is soaking in a tub, pulling the plug, and fighting the current!
Function getSpecialfolder(CSIDL As Long) As String Dim r As Long Dim IDL As ITEMIDLIST Dim fpath As String On Error Goto errHandler 'Get the special folder r = SHGetSpecialFolderLocation(100, CSIDL, IDL) ' in Delcarations If r = 0 Then 'Create a buffer fpath = Space$(512) 'Get the path from the IDList r = SHGetPathFromIDList(IDL.mkid.cb, fpath) ' in Declarations 'Remove the unnecessary chr$(0)'s GetSpecialfolder = Left$(fpath, Instr(fpath, Chr$(0)) - 1) Exit Function End If GetSpecialfolder = "" getOut: Exit Function errHandler: On Error Goto 0 getSpecialFolder = "" Error Err, Error$ & " [in " & Lsi_info(2) & ", line: " & Erl & "]" Resume getOut End Function
This simple little function calls the two C API calls to retrieve the desired special folder, and then trims it up to remove the trailing blank spaces. I also added a bit of error handling to it to fit within my system for error handling.
Conclusion
That's it! Once again I want to thank the folks at AllAPI.net for the great API Guide, which is where this example was born. I simply tweaked it to work in LotusScript.
I also want to once again mention Mikkel's post here, because he posted it first and it does provide a nice way to do the same thing - it is just different.
If you happen to find ways to improve upon this, please share!
Rock
**To me, good exercise is soaking in a tub, pulling the plug, and fighting the current!







Blog Roll









Comments
please drop me a line as I have done a rewrite of your code using classes - maybe my version could save you a bit of time.
I did try to get hold of you to send you the DB but you never answered / the mail's bounced :o(
Speak to you later
Ursus
Posted by ursus At 02:37:05 AM On 08/24/2006 | - Website - |
Posted by Mikkel Heisterberg At 04:34:15 PM On 08/23/2006 | - Website - |
Thanks for the links.
What we could do is wrap this thing into a function that tests for the Windows Scripting Host, and if it isn't there have it use the "old school" C API call; if it is there, then use your technique.
That would be a nice chunk of code. (Hint Hint
Rock
Posted by Rock At 09:34:55 PM On 08/26/2006 | - Website - |
don't know if you saw this but Charles Robinson posted a comment on my blog with a very easy way to get at the My Documents folder using the Scripting Host API. This means you don't need any calls to the C API. I have linked to the comment on my site from this comment.
{ Link }
Posted by Mikkel Heisterberg At 03:07:20 AM On 08/25/2006 | - Website - |
Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (Byval nBufferLength As Long, Byval lpBuffer As String) As Long
Public Const max_path_length = 255
temp_path$ = String$(max_path_length, 0)
result = gettemppath(max_path_length, temp_path$)
temp_path$ = Left(temp_path$,result)
I've used it to determine a safe target path for automatically detaching files from docs w/o prompting the user for the location. For example, if you want to silently have Lotusscript detach a file from one doc, and then add it to another doc. The user ALWAYS has write privileges on their TEMP directory, even on terminal servers and other shared Windows systems.
Posted by Dave Hinkle At 06:47:56 PM On 08/30/2006 | - Website - |
It's funny how we all hit this same thing at the same time.
P.S. This comment may disappear shortly... that's been an ongoing issue I've been having with posting comments to Rocky's blog.
Posted by Charles Robinson At 10:14:17 AM On 08/25/2006 | - Website - |