- NSIS Discussion
- Using System::Call to call SHGetKnownFolderPath
Archive: Using System::Call to call SHGetKnownFolderPath
wraithdu
7th March 2008 00:10 UTC
Using System::Call to call SHGetKnownFolderPath
I'm having trouble getting this to work. Here's the MSDN for the function
HRESULT SHGetKnownFolderPath(
REFKNOWNFOLDERID rfid,
DWORD dwFlags,
HANDLE hToken,
PWSTR *ppszPath
)
And what I've got
!define SHGetKnownFolderPath "!insertmacro _SHGetKnownFolderPath"
!macro _SHGetKnownFolderPath nFolder dwFlags outVar
System::Call "shell32::SHGetKnownFolderPath(g ${nFolder}, i ${dwFlags}, i, w .s)"
Pop ${outVar}
!macroend
The problem is the FOLDERID for the function is a GUID, for example for the SendTo folder
{8983036C-27C0-404B-8F08-102D10DCFD74}
So the macro call would look like
${SHGetKnownFolderPath} "{8983036C-27C0-404B-8F08-102D10DCFD74}" "0x1000" $0
But I get nothing in the output. Can someone help?
demiller9
7th March 2008 03:39 UTC
You aren't setting a value for the hToken parameter. You might try setting the third parameter to 0 (null) or -1 (if the guid is for a user specific folder and you want the current user).
Don
wraithdu
7th March 2008 04:01 UTC
Thanks for the idea, but it doesn't matter if the value for hToken is set of not. It's supposed to be null anyway. I did test it to make sure. But the SHGetFolderPath function, which I can get to work, also works without setting the hToken var.
I'm sure the problem is to do with the rfid var and the proper way to pass the GUID to the function.
PS - this is a Vista only function.
demiller9
7th March 2008 04:11 UTC
The output is a Unicode string. Are you running the Unicode build, or do you call a function to convert the string to single byte?
The System dll has examples:
!define CLSID_ActiveDesktop {75048700-EF1F-11D0-9888-006097DEACF9}
!define IID_IActiveDesktop {F490EB00-1240-11D1-9888-006097DEACF9}
# create IActiveDesktop interface
System::Call "ole32::CoCreateInstance( \
g '${CLSID_ActiveDesktop}', i 0, \
i ${CLSCTX_INPROC_SERVER}, \
g '${IID_IActiveDesktop}', *i .r0) i.r1"
Your usage of the guid looks similar.
wraithdu
7th March 2008 04:14 UTC
I'm running the ANSI version.
I haven't converted the output yet. Even though, I expected to see some kind of output. It's completely blank. Is that right? Shouldn't I see something even if I haven't converted it yet?
demiller9
7th March 2008 04:17 UTC
A unicode string will look like it has zeros between its characters. Those would be interpreted as end of string. (I don't recall whether the first byte is the zero, which would make your return string appear completely empty).
wraithdu
7th March 2008 04:25 UTC
I believe the second byte is 00, so I should still get the first character.
Crap, converting the string to ANSI would be a whole other problem. I know how to do the DllCall's in AutoIt for this, but not the System::Call's in NSIS (I don't know how to create DLL structs in NSIS for the string buffers).
demiller9
7th March 2008 04:50 UTC
I'm not an expert with the System dll, so maybe they will speak up if you don't get it figured out by yourself. Here's a link to CodeGuru on how to convert multibyte to ansi.
Anders
7th March 2008 08:34 UTC
I don't have vista, so I could not test this code at all, but I would guess it would look something like
System::Call "shell32::SHGetKnownFolderPath(g '{8983036C-27C0-404B-8F08-102D10DCFD74}', i 0x1000 ,i0, i.r1)i.r0"
${If} $0 == 0
System::Call /NoUnload 'kernel32::lstrlenW(i $1)i.r2'
IntOp $2 $2 * 2
System::Call /NoUnload '*$1(&w$2 .r2)'
MessageBox mb_ok path=$2
System::Call 'ole32::CoTaskMemFree(i $1)'
${endif}
Anders
7th March 2008 08:43 UTC
but, why call the Vista function when you can just do:
System::Call 'shell32::SHGetFolderPath(i $hwndparent,i 0x9,i0,i0,t.r0)i'
MessageBox mb_ok $0
and there is $SENDTO also ofcourse
wraithdu
7th March 2008 23:34 UTC
Thanks for the try, but the function crashes if the output data type is an integer (i). It does not crash if output is a unicode string (w), but the output is then just a ?. The return value is still 0 however.
And I realize I can use SHGetFolderPath (or $SENDTO), but SHGetKnownFolderPath has GUIDs with no CSIDL equivalent for the old function. It is also extensible, ie a user can create a GUID for a folder and return it with this function.
Anders
8th March 2008 07:59 UTC
ah yes, its PWSTR*, sorry, missed that, try adding a *
wraithdu
8th March 2008 19:49 UTC
Eureka! Thanks soooo much! This was killing me...
kichik
8th March 2008 19:53 UTC
It's:
System::Call "shell32::SHGetKnownFolderPath(g '{8983036C-27C0-404B-8F08-102D10DCFD74}', \
i 0x1000 , i0, *w.r1) i.r0"
If that doesn't work, it's because `g` is for GUID pointers and SHGetKnownFolderPath expects the GUID itself on the stack.
wraithdu
8th March 2008 20:06 UTC
Nope, I got it. I had the * on the wrong side of the i ;)
Working code
System::Call "shell32::SHGetKnownFolderPath(g '{8983036C-27C0-404B-8F08-102D10DCFD74}', i 0x1000 ,in, *i.r1)i.r0"
${If} $0 == 0
System::Call /NoUnload 'kernel32::lstrlenW(i $1)i.r2'
IntOp $2 $2 * 2
System::Call /NoUnload '*$1(&w$2 .r2)'
MessageBox mb_ok path=$2
System::Call 'ole32::CoTaskMemFree(i $1)'
${endif}
wraithdu
8th March 2008 20:11 UTC
Originally posted by kichik
`g` is for GUID pointers
Can you explain that a little more? I'm trying to get this to work in AutoIt3 as well, but it doesn't have a GUID data type. Is there some way I can create that data type using others? Are you saying that g is a pointer to a GUID string in a structure?
kichik
8th March 2008 20:36 UTC
No, I'm saying for the System plug-in 'g' means pass a pointer to a GUID structure (LPGUID instead of GUID). I don't know how to do that in AutoIt3.
wraithdu
8th March 2008 21:27 UTC
Got it (finally) in AutoIt -
#include <WinAPI.au3>
$tagstruct = DllStructCreate($tagGUID)
$tagptr = DllStructGetPtr($tagstruct)
_WinAPI_GUIDFromStringEx("{8983036C-27C0-404B-8F08-102D10DCFD74}", $tagptr)
So $tagptr has the pointer to the GUID structure.
Now I gotta figure out how to read the buffer....