INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS
Thanks. We have received your request and will respond promptly.
Log In
Come Join Us!
Are you a
Computer / IT professional?
Join Tek-Tips Forums!
- Talk With Other Members
- Be Notified Of Responses
To Your Posts - Keyword Search
- One-Click Access To Your
Favorite Forums - Automated Signatures
On Your Posts - Best Of All, It's Free!
Join Us!
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.
Posting Guidelines
Promoting, selling, recruiting, coursework and thesis posting is forbidden.
Tek-Tips Posting Policies
thread184-1723041
Forum | Search | FAQs | Links | MVPs |
Calling SHGetKnownFolderPath in VFPCalling SHGetKnownFolderPath in VFP(OP) In thread184-1539853: Installed database location? Olaf listed links to the Microsoft documentation page for SHGetKnownFolderPath but I can't work out how to call it as the first parameter is a reference to a GUID. It's the only function I know that will give me access to the full array of pre-defined folders in Windows 8.1 (eg Skydrive Pictures and Camera Roll). Has anyone got this to work in VFP? Rob Spencer RE: Calling SHGetKnownFolderPath in VFPThe GUIDS are listed in the referenced Link on KNONFOLDERIDs: MSDN articles are well documented, so what is your problem, really? Bye, Olaf. RE: Calling SHGetKnownFolderPath in VFP(OP) What I need is a working declare statement and maybe an example :) This is my most recent try... CODE --> VFP#define k_FID_CAMERAROLL"{AB5FB87B-7CE2-4F83-915D-550846C9537B}"#define k_FID_DESKTOP"{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}"#define k_FID_DOCUMENTS"{FDD39AD0-238F-46AF-ADB4-6C85480369C7}"#define k_FID_PICTURES"{33E28130-4E1E-4676-835A-98395C3BC3BB}"#define k_FID_SKYCAMERAROLL"{767E6811-49CB-4273-87C2-20F355E1085B}"#define k_FID_SKYDOCUMENTS"{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}"#define k_FID_SKYPICTURES"{339719B5-8C47-4894-94C2-D8F77ADD44A6}"declare short FormatMessage ; in win32api ; integer dwFlags ,; integer lpvSource,; integer dwMsgId,; integer dwLangId,; STRING @lpBuffer,; integer nSize,; integer Argumentdeclare short SHGetKnownFolderPath ;in shell32.dll ;string @ cRFID,;integer dwFlags,;integer hToken,;string @ ppszPathcDefFoldID= k_FID_DESKTOPcInBuff= space(512)nResult= SHGetKnownFolderPath(@cDefFoldID, 0x2000 + 0x4000, 0, @cInBuff)if m.nResult # 0nSize= FormatMessage(0x1000, 0, m.nResult, 0, @cInBuff, len(cInBuff), 0)? left(m.cInBuff, m.nSize)endif This outputs "The system cannot find the file specified." which I doubt is because I don't have one as I can get the desktop folder from WScript.Shell object. Hope you can help. I was hoping you'd see this Olaf :) Rob Spencer RE: Calling SHGetKnownFolderPath in VFPRob, There's an example of a fully-working function, along with a list of the GUIDs, here: Finding the paths to Windows' special folders in VFP However, the list of GUIDs doesn't mention the Windows 8-specific folders, such as CameraRoll as far as I can see. Mike __________________________________ RE: Calling SHGetKnownFolderPath in VFPI've found a list that does include the new folders like SkyDrive and CameraRoll. See: http://msdn.microsoft.com/en-us/library/windows/de... I see now that the article I referenced in my previous post uses a different set of API calls, and a different set of IDs (but it still works). Mike __________________________________ RE: Calling SHGetKnownFolderPath in VFPNice Rob, don't forget the mention of CoTaskMemFree. The SHGetKnownFolderPath just allocates a few bytes, and you may never had any problems with non freed orphaned memory, but it's a potential risc. Bye, Olaf. RE: Calling SHGetKnownFolderPath in VFP(OP) Thanks Mike. I looked at your code and it is an easier way of doing it than I was using so I've modified my code. Using your method I avoid having to use sys(2600) (Thanks Olaf for pointing it out though :)) Sadly it doesn't help me as I still get the same result :( The new version of that function SHGetKnownFolderIDList() also takes a REFKNOWNFOLDERID which is a reference to a KNOWNFOLDERID. Most of the entries that I'm interested in don't have CSIDL equivalents so I can't use the older form of the call. Again, I've included my reworked code (sorry, I've wrapped the error handler which makes it look more complicated but it isn't really). CODE --> VFP**********************************************function getKnownFolderPath (cDefFoldID as string) as string**********************************************local ok, nOutBuff, cErrMsg, cOutStrif vartype(__G_DLL_SHGETKNOWNFOLDERIDLIST_) = "U"declare long SHGetKnownFolderIDList ;in shell32.dll ;string @ cRFID,;long dwFlags,;long hToken,;long @ ppidlpublic __G_DLL_SHGETKNOWNFOLDERIDLIST_endifif vartype(__G_DLL_SHGETPATHFROMIDLIST_) = "U"declare short SHGetPathFromIDList ;in shell32.dll ;long ppidl, ;string @ cPathpublic __G_DLL_SHGETPATHFROMIDLIST_endifif vartype(__G_DLL_COTASKMEMFREE_) = "U"declare CoTaskMemFree ;in ole32.dll ;long pVoidpublic __G_DLL_COTASKMEMFREE_endifok= .f.&& Pessimist!nOutBuff= 0cErrMsg= checkSysError(SHGetKnownFolderIDList(@m.cDefFoldID, 0x2000 + 0x4000, 0, @nOutBuff))if empty(m.cErrMsg)cOutStr= space(512)cErrMsg= checkSysError(SHGetPathFromIDList(m.nOutBuff, @cOutStr))if empty(m.cErrMsg)cOutStr= left(m.cOutStr, at(chr(0), m.cOutStr) - 1)ok= .t.endifendifif ! m.ok? m.cErrMsgendif* Clean up the memory used in the above callsCoTaskMemFree(m.nOutBuff)return iif(m.ok, m.cOutStr, "")endfunc&& getKnownFolderPath()**********************************************function checkSysError (nResult as integer) as string**********************************************local cMsg, cInBuff, nSizeif empty(m.nResult) or m.nResult = 1&& covers missing parameter, 0 (S_OK) or 1 (S_FALSE)* It must have worked :)cMsg= ""elseif vartype(__G_DLL_FORMATMESSAGE_) = "U"declare long FormatMessage ;in win32api ;long dwFlags ,;long lpvSource,;long dwMsgId,;long dwLangId,;string @lpBuffer,;long nSize,;long Argumentpublic __G_DLL_FORMATMESSAGE_endifcInBuff= space(1024)nSize= FormatMessage(0x1000, 0, m.nResult, 0, @cInBuff, len(m.cInBuff), 0)if m.nSize > 0cMsg= left(m.cInBuff, m.nSize)elsecMsg= "Unknown error - code " + ltrim(str(m.nResult))endifendifreturn m.cMsgendfunc&& checkSysError()* Sample call? GetKnownFolderPath("{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}") The sample call should return my desktop folder but simply outputs "The system cannot find the file specified." which is odd. I'm at a loss on what I'm doing wrong. Any help would be very gratefully accepted :) Rob Spencer RE: Calling SHGetKnownFolderPath in VFPI think the solution is easy: You have to provide the GUID in binary form, not as hex string. I haven't tried, though. When you create a guid you get a GUID in binary form and can format it in the hex form we all know, there also should be an inverse API call to take a formatted GUID string and return it in 16 byte binary form. Be warned: The byte order is not the same as in the hexstring, so a hex to binary conversion of the string without the curly braces and minuses won't work, you have to find the API call. Bye, Olaf. RE: Calling SHGetKnownFolderPath in VFP(OP) Thanks Olaf. One step closer but still no cigar! Latest code listed below. I've converted the GUID string to UNICODE and null terminated it to get a successful CLSIDFromString() result. You'll notice that I've replaced the GUID parameter with a double in both of the first two calls. If I change both references to long, I get a "Declare DLL call caused an exception" when it runs the SHGetKnownFolderIDList() call. The nGUID parameter is negative (but I'm not sure that I care since I'm just passing on what I get given back by the previous call???? I only care because it crashes!). With the parameter declared as a double, I get a "The parameter is incorrect" result instead but at least that is a return code from the call. I have tried several other variations but nothing that I've tried has helped get any closer. CODE --> VFP**********************************************function getKnownFolderPath (cDefFoldID as string) as string**********************************************local ok, nOutBuff, cErrMsg, cOutStr, cUCStr, nGUIDif vartype(__G_DLL_CLSIDFROMSTRING_) = "U"declare long CLSIDFromString ;in ole32.dll ;string cGUID, ;double @ nRFIDpublic __G_DLL_CLSIDFROMSTRING_endifif vartype(__G_DLL_SHGETKNOWNFOLDERIDLIST_) = "U"declare long SHGetKnownFolderIDList ;in shell32.dll ;double nRFID,;long dwFlags,;long hToken,;long @ ppidlpublic __G_DLL_SHGETKNOWNFOLDERIDLIST_endifif vartype(__G_DLL_SHGETPATHFROMIDLISTW_) = "U"declare long SHGetPathFromIDListW ;in shell32.dll ;long ppidl, ;string @ cPathpublic __G_DLL_SHGETPATHFROMIDLISTW_endifif vartype(__G_DLL_COTASKMEMFREE_) = "U"declare CoTaskMemFree ;in ole32.dll ;long pVoidpublic __G_DLL_COTASKMEMFREE_endifok= .f.&& Pessimist!nGUID= 0cUCStr= strconv(m.cDefFoldID, 5) + chr(0)cErrMsg= checkSysError(CLSIDFromString(m.cUCStr, @nGUID))if empty(m.cErrMsg)nOutBuff= 0cErrMsg= checkSysError(SHGetKnownFolderIDList(m.nGUID, 0x2000 + 0x4000, 0, @nOutBuff))endifif empty(m.cErrMsg)cOutStr= space(512)cErrMsg= checkSysError(SHGetPathFromIDListW(m.nOutBuff, @cOutStr))if empty(m.cErrMsg)cOutStr= left(m.cOutStr, at(chr(0), m.cOutStr) - 1)ok= .t.endifendifif ! m.ok? m.cErrMsgendif* Clean up the memory used in the above callsCoTaskMemFree(m.nOutBuff)return iif(m.ok, m.cOutStr, "")endfunc&& getKnownFolderPath()**********************************************function checkSysError (nResult as integer) as string**********************************************local cMsg, cInBuff, nSizeif empty(m.nResult) or m.nResult = 1&& covers missing parameter, 0 (S_OK) or 1 (S_FALSE)* It must have worked :)cMsg= ""elseif vartype(__G_DLL_FORMATMESSAGE_) = "U"declare long FormatMessage ;in win32api ;long dwFlags ,;long lpvSource,;long dwMsgId,;long dwLangId,;string @lpBuffer,;long nSize,;long Argumentpublic __G_DLL_FORMATMESSAGE_endifcInBuff= space(1024)nSize= FormatMessage(0x1000, 0, m.nResult, 0, @cInBuff, len(m.cInBuff), 0)if m.nSize > 0cMsg= left(m.cInBuff, m.nSize)elsecMsg= "Unknown error - code " + ltrim(str(m.nResult))endifendifreturn m.cMsgendfunc&& checkSysError() Sorry to still be asking for help :( I should be able to sort this out for myself given the advice I've received so far. Thanks again for your help. Rob Spencer RE: Calling SHGetKnownFolderPath in VFP16 byte is too long for a long, you have to keep that at String. Just because it's binary now, it's not a number, not an int, not a long, not a double, it's 16 bytes memory, and that as transported by a string, also in many, many other API function calls. In regard to the output I still got only "C" as a first result, but that's because you use the UNICODE variant SHGetPathFromIDListW, in VFP use SHGetPathFromIDListA. You're cuting off at the first chr(0), but chr(0) are just normal for Unicode strings, normal letters cause each second byte of the unicode string to be chr(0). Overall I changed your code and this works on Win7 (it's the temp path for CD burning data), you might put in a Win8 or Win 8.1 GUID to see yourself, if you get these paths, too, now. CODE -->? getKnownFolderPath('{9E52AB10-F80D-49DF-ACB8-4330F5687855}')**********************************************function getKnownFolderPath (cDefFoldID as string) as string**********************************************local ok, nOutBuff, cErrMsg, cOutStr, cUCStr, nGUIDif vartype(__G_DLL_CLSIDFROMSTRING_) = "U"declare long CLSIDFromString ;in ole32.dll ;string cGUID, ;string @ cCLSIDpublic __G_DLL_CLSIDFROMSTRING_endifif vartype(__G_DLL_SHGETKNOWNFOLDERIDLIST_) = "U"declare long SHGetKnownFolderIDList ;in shell32.dll ;string cbCLSID,;long dwFlags,;long hToken,;long @ ppidlpublic __G_DLL_SHGETKNOWNFOLDERIDLIST_endifif vartype(__G_DLL_SHGETPATHFROMIDLISTA_) = "U"declare long SHGetPathFromIDListA ;in shell32.dll ;long ppidl, ;string @ cPathpublic __G_DLL_SHGETPATHFROMIDLISTA_endifif vartype(__G_DLL_COTASKMEMFREE_) = "U"declare CoTaskMemFree ;in ole32.dll ;long pVoidpublic __G_DLL_COTASKMEMFREE_endifok= .f.&& Pessimist!cbGUID= Space(16)cUCStr= strconv(m.cDefFoldID, 5) + chr(0)cErrMsg= checkSysError(CLSIDFromString(m.cUCStr, @cbGUID))if empty(m.cErrMsg)nOutBuff= 0cErrMsg= checkSysError(SHGetKnownFolderIDList(m.cbGUID, 0x2000 + 0x4000, 0, @nOutBuff))endifif empty(m.cErrMsg)cOutStr= space(512)cErrMsg= checkSysError(SHGetPathFromIDListA(m.nOutBuff, @cOutStr))if empty(m.cErrMsg)*cOutStr= left(m.cOutStr, at(chr(0), m.cOutStr) - 1)ok= .t.endifendifif ! m.ok? m.cErrMsgendif* Clean up the memory used in the above callsCoTaskMemFree(m.nOutBuff)return iif(m.ok, m.cOutStr, "")endfunc&& getKnownFolderPath()**********************************************function checkSysError (nResult as integer) as string**********************************************local cMsg, cInBuff, nSizeif empty(m.nResult) or m.nResult = 1&& covers missing parameter, 0 (S_OK) or 1 (S_FALSE)* It must have worked :)cMsg= ""elseif vartype(__G_DLL_FORMATMESSAGE_) = "U"declare long FormatMessage ;in win32api ;long dwFlags ,;long lpvSource,;long dwMsgId,;long dwLangId,;string @lpBuffer,;long nSize,;long Argumentpublic __G_DLL_FORMATMESSAGE_endifcInBuff= space(1024)nSize= FormatMessage(0x1000, 0, m.nResult, 0, @cInBuff, len(m.cInBuff), 0)if m.nSize > 0cMsg= left(m.cInBuff, m.nSize)elsecMsg= "Unknown error - code " + ltrim(str(m.nResult))endifendifreturn m.cMsgendfunc&& checkSysError() See for yourself what I changed, I haven't marked it, sorry, I was too lazy. It's several places. Bye, Olaf. RE: Calling SHGetKnownFolderPath in VFPIn regard to CLSIDFromString usage, also see http://www.news2news.com/vfp/?example=456&func... Taken from news2news: CODEDO declare LOCAL cGUID, cGUIDString, cGUID1cGUID = REPLICATE(CHR(0), 16) && 128 bits IF CoCreateGuid(@cGUID) = 0 cGUIDString = StringFromGUID(cGUID) ? cGUID ? cGUIDString * converting from String back to GUID cGUID1 = REPLICATE(CHR(0), 16) * Olaf Doschke: Here you see how they called CLSIDFromString, also using STRCONV(...,5) for unicode, but not +chr(0) = CLSIDFromString(STRCONV(cGUIDString,5), @cGUID1) ? cGUID1ENDIF* end of main FUNCTION StringFromGUID(cGUID) LOCAL cBuffer, nBufsize nBufsize=128 cBuffer = REPLICATE(CHR(0), nBufsize*2) = StringFromGUID2(cGUID, @cBuffer, nBufsize) cBuffer = SUBSTR(cBuffer, 1, AT(CHR(0)+CHR(0), cBuffer))RETURN STRCONV(cBuffer, 6) PROCEDURE declare DECLARE INTEGER CoCreateGuid IN ole32 STRING @pguid DECLARE INTEGER CLSIDFromString IN ole32; STRING lpsz, STRING @pclsid DECLARE INTEGER StringFromGUID2 IN ole32; STRING rguid, STRING @lpsz, INTEGER cchMax Bye, Olaf. RE: Calling SHGetKnownFolderPath in VFP2 RobSpencer (Programmer)(OP) Hi Olaf, Thanks so much. Yes, the use of the SHGetPathFromIDListW version of the function was a left-over from some "clutching at straws" and it should have simply been the basic version. The information about the added chr(0) came from http://www.foxpert.com/knowlbits_200709.htm and in fact the behaviour I've found is that it is required. Without it, I was able to run it once but not multiple times (I'd get "Invalid class string" errors from subsequent calls). I've tidied up the code including some basic documentation and included it below. CODE --> VFP* Define constants to use with SHGetKnownFolderIDList routine* Values copied from MSDN reference site (although not the names)#define k_FID_CAMERAROLL"{AB5FB87B-7CE2-4F83-915D-550846C9537B}"#define k_FID_DESKTOP"{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}"#define k_FID_DOCUMENTS"{FDD39AD0-238F-46AF-ADB4-6C85480369C7}"#define k_FID_PICTURES"{33E28130-4E1E-4676-835A-98395C3BC3BB}"#define k_FID_SKYCAMERAROLL"{767E6811-49CB-4273-87C2-20F355E1085B}"#define k_FID_SKYDOCUMENTS"{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}"#define k_FID_SKYPICTURES"{339719B5-8C47-4894-94C2-D8F77ADD44A6}"? GetKnownFolderPath(k_FID_SKYDOCUMENTS)**********************************************function getKnownFolderPath (cDefFoldID as string, cErrMsg as string) as string**********************************************local nOutBuff, cOutStr, cbGUID* Declare all the required system calls (protected for repeat calls)if vartype(__G_DLL_CLSIDFROMSTRING_) = "U"declare long CLSIDFromString ;in ole32.dll ;string cInStr, ;string @ cGUIDpublic __G_DLL_CLSIDFROMSTRING_endifif vartype(__G_DLL_SHGETKNOWNFOLDERIDLIST_) = "U"declare long SHGetKnownFolderIDList ;in shell32.dll ;string cGUID,;long dwFlags,;long hToken,;long @ ppidlpublic __G_DLL_SHGETKNOWNFOLDERIDLIST_endifif vartype(__G_DLL_SHGETPATHFROMIDLIST_) = "U"declare long SHGetPathFromIDList ;in shell32.dll ;long ppidl, ;string @ cPathpublic __G_DLL_SHGETPATHFROMIDLIST_endifif vartype(__G_DLL_COTASKMEMFREE_) = "U"declare CoTaskMemFree ;in ole32.dll ;long pVoidpublic __G_DLL_COTASKMEMFREE_endif* Convert the string GUID to a binary versioncbGUID= space(16)cErrMsg= checkSysError(CLSIDFromString(strconv(m.cDefFoldID, 5) + chr(0), @cbGUID))if empty(m.cErrMsg)* Get back the folder path in a PIDL structure* Flags - 0x1000 -> KF_FLAG_NO_ALIAS - Expand any alias placeholders, eg. %USERPROFILE%* - 0x4000 -> KF_FLAG_DONT_VERIFY - Don't check that the folder exists before returning the valuenOutBuff= 0cErrMsg= checkSysError(SHGetKnownFolderIDList(m.cbGUID, 0x1000 + 0x4000, 0, @nOutBuff))endifif empty(m.cErrMsg)* Extract the path string from the PIDL structurecOutStr= space(512)cErrMsg= checkSysError(SHGetPathFromIDList(m.nOutBuff, @cOutStr))endif* Clean up the memory used in the above system callsCoTaskMemFree(m.nOutBuff)* Ensure any returned path has the trailing backslashreturn iif(empty(m.cErrMsg), addbs(m.cOutStr), "")endfunc&& getKnownFolderPath()**********************************************function checkSysError (nResult as integer) as string**********************************************local cMsg, cInBuff, nSizeif empty(m.nResult) or m.nResult = 1&& covers missing parameter, 0 (S_OK) or 1 (S_FALSE)* It must have worked :)cMsg= ""else* Declare the required system call (protected for repeat calls)if vartype(__G_DLL_FORMATMESSAGE_) = "U"declare long FormatMessage ;in win32api ;long dwFlags ,;long lpvSource,;long dwMsgId,;long dwLangId,;string @lpBuffer,;long nSize,;long Argumentpublic __G_DLL_FORMATMESSAGE_endif* Retrieve the system message* Flags - 0x1000 -> FORMAT_MESSAGE_FROM_SYSTEM Use the system message-tablecInBuff= space(1024)nSize= FormatMessage(0x1000, 0, m.nResult, 0, @cInBuff, len(m.cInBuff), 0)if m.nSize > 0cMsg= left(m.cInBuff, m.nSize)else* No message came back - maybe it is a application specific messagecMsg= "Unknown error - code " + ltrim(str(m.nResult))endifendifreturn m.cMsgendfunc&& checkSysError() On my system it returns C:\Users\Rob\SkyDrive\Documents\ which is exactly what I was looking for. Hoping to use the application on a Surface Pro 2. Thank you all so much for the help, especially Olaf :) Rob Spencer RE: Calling SHGetKnownFolderPath in VFPNice hint on the foxperts article. I'm sure Christof is right. Indeed in this case you have a 16 byte buffer turning to 32 bytes unicode, and VFP adds a CHR(0), which makes it 33 bytes and an odd number of bytes is often bad, sometimes your total length should even be multiple of longs, multiples of 4 bytes, to have an aligned memory addres. Perhaps news2news should then update their routine. Glad it works and thanks for sharing the brushed up version. Bye, Olaf. Red Flag SubmittedThank you for helping keep Tek-Tips Forums free from inappropriate posts. Reply To This ThreadPosting in the Tek-Tips forums is a member-only feature.Click Here to join Tek-Tips and talk with other members! Already a Member? Login |