4 * Copyright 2014 David Quintana <gigaherz@gmail.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 BOOL WINAPI
GUIDFromStringW(
30 static BOOL
_CopyAndUnquoteText(LPCWSTR strFieldSource
, LPWSTR strField
, size_t cchField
)
33 PWSTR tmpField
= strField
;
37 // Remove leading whitespace
38 cChar
= *strFieldSource
;
39 while (cChar
== L
' ' || cChar
== L
'\t' || cChar
== L
'\n' || cChar
== L
'\r')
41 strFieldSource
= CharNextW(strFieldSource
);
42 cChar
= *strFieldSource
;
45 while (cChar
&& cChar
!= L
'=' && cChar
!= L
',')
49 // [1] is always valid read because of null-termination
50 if (inQuote
&& strFieldSource
[1] == L
'"')
52 if (lenField
< cchField
)
69 if (inQuote
|| (cChar
!= L
'=' && cChar
!= L
','))
71 if (lenField
< cchField
)
74 *(tmpField
++) = cChar
;
80 strFieldSource
= CharNextW(strFieldSource
);
81 cChar
= *strFieldSource
;
84 // Remove trailing whitespace
85 while (tmpField
> strField
)
87 tmpField
= CharPrevW(strField
, tmpField
);
89 if (cChar
!= L
' ' && cChar
!= L
'\t' && cChar
!= L
'\n' && cChar
!= L
'\r')
91 tmpField
= CharNextW(tmpField
);
96 // Terminate output string
102 static BOOL
_FindNextArg(PCWSTR
* pstrFieldSource
)
104 PCWSTR strFieldSource
= *pstrFieldSource
;
105 WCHAR cChar
= *strFieldSource
;
106 BOOL inQuote
= FALSE
;
110 if (!inQuote
&& (cChar
== L
'=' || cChar
== L
','))
116 strFieldSource
= CharNextW(strFieldSource
);
117 cChar
= *strFieldSource
;
122 *pstrFieldSource
= strFieldSource
;
126 *pstrFieldSource
= CharNextW(strFieldSource
);
130 static PCWSTR
_FindFirstField(PCWSTR strFieldSource
)
132 //Find end of first arg, because
133 // behaviour is different if the first separator is an '='
134 BOOL inQuote
= FALSE
;
135 PCWSTR tmpArgs
= strFieldSource
;
136 WCHAR cChar
= *tmpArgs
;
148 tmpArgs
= CharNextW(tmpArgs
);
152 // Skip the text before the first equal sign, if not quoted, unless the arg 0 was requested.
153 if (*tmpArgs
== L
'=' && !inQuote
)
155 strFieldSource
= ++tmpArgs
;
156 TRACE("Skipped content before the first '=', remainder=%S\n", strFieldSource
);
159 return strFieldSource
;
162 static BOOL
_ReadNextArg(PCWSTR
* pstrFieldSource
, PWSTR strField
, size_t cchField
)
164 // Copy and unquote text
165 _CopyAndUnquoteText(*pstrFieldSource
, strField
, cchField
);
167 return _FindNextArg(pstrFieldSource
);
170 static LPITEMIDLIST
_ILReadFromSharedMemory(PCWSTR strField
)
172 LPITEMIDLIST ret
= NULL
;
174 // Ensure it really is an IDLIST-formatted parameter
175 // Format for IDLIST params: ":pid:shared"
176 if (*strField
!= L
':')
179 HANDLE hData
= IntToPtr(StrToIntW(strField
+ 1));
180 PWSTR strSecond
= StrChrW(strField
+ 1, L
':');
184 int pid
= StrToIntW(strSecond
+ 1);
185 void* pvShared
= SHLockShared(hData
, pid
);
188 ret
= ILClone((LPCITEMIDLIST
) pvShared
);
189 SHUnlockShared(pvShared
);
190 SHFreeShared(hData
, pid
);
196 static HRESULT
_ParsePathToPidl(PWSTR strPath
, LPITEMIDLIST
* pidl
)
198 CComPtr
<IShellFolder
> psfDesktop
;
200 HRESULT hr
= SHGetDesktopFolder(&psfDesktop
);
204 return psfDesktop
->ParseDisplayName(NULL
, NULL
, strPath
, NULL
, pidl
, NULL
);
207 static LPITEMIDLIST
_GetDocumentsPidl()
209 CComPtr
<IShellFolder
> ppshf
;
211 WCHAR guid
[] = L
"::{450d8fba-ad25-11d0-98a8-0800361b1103}";
213 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL
, CSIDL_MYDOCUMENTS
, &pidl
)))
216 if (FAILED(SHGetDesktopFolder(&ppshf
)))
219 if (FAILED(ppshf
->ParseDisplayName(NULL
, NULL
, guid
, NULL
, &pidl
, NULL
)))
225 /*************************************************************************
226 * SHExplorerParseCmdLine [BROWSEUI.107]
231 SHExplorerParseCmdLine(ExplorerCommandLineParseResults
* pInfo
)
233 WCHAR strField
[MAX_PATH
];
234 WCHAR strDir
[MAX_PATH
];
236 PCWSTR strCmdLine
= GetCommandLineW();
237 PCWSTR strFieldArray
= PathGetArgsW(strCmdLine
);
242 pInfo
->pidlPath
= _GetDocumentsPidl();
243 if (!pInfo
->pidlPath
)
245 GetWindowsDirectoryW(strDir
, MAX_PATH
);
246 PathStripToRootW(strDir
);
247 pInfo
->pidlPath
= ILCreateFromPathW(strDir
);
249 return (LONG_PTR
)(pInfo
->pidlPath
);
252 PCWSTR strNextArg
= _FindFirstField(strFieldArray
);
256 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));
259 // Basic flags-only params first
260 if (!StrCmpIW(strField
, L
"/N"))
262 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_N
| SH_EXPLORER_CMDLINE_FLAG_ONE
;
263 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
265 else if (!StrCmpIW(strField
, L
"/S"))
267 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_S
;
268 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
270 else if (!StrCmpIW(strField
, L
"/E"))
272 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_E
;
273 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
275 else if (!StrCmpIW(strField
, L
"/SELECT"))
277 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_SELECT
;
278 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
280 else if (!StrCmpIW(strField
, L
"/NOUI"))
282 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_NOUI
;
283 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
285 else if (!StrCmpIW(strField
, L
"-embedding"))
287 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_EMBED
;
288 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
290 else if (!StrCmpIW(strField
, L
"/SEPARATE"))
292 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_SEPARATE
;
293 TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField
, pInfo
->dwFlags
);
295 else if (!StrCmpIW(strField
, L
"/INPROC"))
297 // No idea what Inproc is supposed to do, but it gets a GUID, and parses it.
299 TRACE("CmdLine Parser: Found %S flag\n", strField
);
304 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));
306 if (!GUIDFromStringW(strField
, &(pInfo
->guidInproc
)))
309 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_INPROC
;
311 TRACE("CmdLine Parser: Parsed /INPROC flag. dwFlags=%08lx, guidInproc=%S\n", pInfo
->dwFlags
, strField
);
313 else if (!StrCmpIW(strField
, L
"/ROOT"))
315 LPITEMIDLIST pidlRoot
= NULL
;
317 // The window should be rooted
319 TRACE("CmdLine Parser: Found %S flag\n", strField
);
321 if (!pInfo
->pidlPath
)
327 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));
329 // Root may be a pidl
330 if (!StrCmpIW(strField
, L
"/IDLIST"))
334 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));
336 pidlRoot
= _ILReadFromSharedMemory(strField
);
340 // Or just a path string
341 _ParsePathToPidl(strField
, &pidlRoot
);
344 pInfo
->pidlRoot
= pidlRoot
;
346 // The root defaults to the desktop
349 if (FAILED(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP
, &(pInfo
->pidlRoot
))))
350 pInfo
->pidlRoot
= NULL
;
353 // TODO: Create rooted PIDL from pInfo->pidlPath and pInfo->pidlRoot
355 TRACE("CmdLine Parser: Parsed /ROOT flag. dwFlags=%08lx, pidlRoot=%p\n", pInfo
->dwFlags
, pInfo
->pidlRoot
);
359 // Anything else is part of the target path to browse to
360 TRACE("CmdLine Parser: Found target path %S\n", strField
);
362 // Which can be a shared-memory itemidlist
363 if (!StrCmpIW(strField
, L
"/IDLIST"))
365 LPITEMIDLIST pidlArg
;
370 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));
371 pidlArg
= _ILReadFromSharedMemory(strField
);
376 ILFree(pInfo
->pidlPath
);
377 pInfo
->pidlPath
= pidlArg
;
379 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo
->dwFlags
, pInfo
->pidlPath
);
383 // Or just a plain old string.
385 if (PathIsDirectoryW(strField
))
386 PathAddBackslash(strField
);
388 WCHAR szPath
[MAX_PATH
];
389 DWORD result
= GetFullPathNameW(strField
, _countof(szPath
), szPath
, NULL
);
391 if (result
!= 0 && result
<= _countof(szPath
) && PathFileExistsW(szPath
))
392 StringCchCopyW(strField
, _countof(strField
), szPath
);
394 LPITEMIDLIST pidlPath
= ILCreateFromPathW(strField
);
396 pInfo
->pidlPath
= pidlPath
;
400 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_IDLIST
;
401 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo
->dwFlags
, pInfo
->pidlPath
);
405 // The path could not be parsed into an ID List,
406 // so pass it on as a plain string.
408 PWSTR field
= StrDupW(strField
);
409 pInfo
->strPath
= field
;
412 pInfo
->dwFlags
|= SH_EXPLORER_CMDLINE_FLAG_STRING
;
413 TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, strPath=%S\n", pInfo
->dwFlags
, field
);
422 hasNext
= _ReadNextArg(&strNextArg
, strField
, _countof(strField
));