[shell32.dll]

- rewrite the parser to handle command line argument parsing
- include basic description of what the rules for parsing are
- some minor formatting here and there (couldn't be helped)

svn path=/branches/shell32_new-bringup/; revision=53533
This commit is contained in:
Claudiu Mihail 2011-09-02 19:29:30 +00:00
parent fc902e6f58
commit 18d876cbf2

View file

@ -32,6 +32,125 @@ static const WCHAR wszEmpty[] = {0};
#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY) #define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
{
bool firstCharQuote = false;
bool quotes_opened = false;
bool backslash_encountered = false;
for (int curArg=0; curArg<=argNum && *args; ++curArg)
{
firstCharQuote = false;
if (*args == '"')
{
quotes_opened = true;
firstCharQuote = true;
args++;
}
while(*args)
{
if (*args == '\\')
{
// if we found a backslash then flip the variable
backslash_encountered = !backslash_encountered;
}
else if (*args == '"')
{
if (quotes_opened)
{
if (*(args+1) != '"')
{
quotes_opened = false;
args++;
break;
}
else
{
args++;
}
}
else
{
quotes_opened = true;
}
backslash_encountered = false;
}
else
{
backslash_encountered = false;
if (*args == ' ' && !firstCharQuote)
break;
}
if (curArg == argNum)
{
used++;
if (used < len)
*res++ = *args;
}
args++;
}
while(*args == ' ')
++args;
}
}
static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
{
bool quotes_opened = false;
bool backslash_encountered = false;
for (int curArg=0; curArg<=argNum && *args; ++curArg)
{
while(*args)
{
if (*args == '\\')
{
// if we found a backslash then flip the variable
backslash_encountered = !backslash_encountered;
}
else if (*args == '"')
{
if (quotes_opened)
{
if (*(args+1) != '"')
{
quotes_opened = false;
}
else
{
args++;
}
}
else
{
quotes_opened = true;
}
backslash_encountered = false;
}
else
{
backslash_encountered = false;
if (*args == ' ' && !quotes_opened && curArg!=argNum)
break;
}
if (curArg == argNum)
{
used++;
if (used < len)
*res++ = *args;
}
args++;
}
}
}
/*********************************************************************** /***********************************************************************
* SHELL_ArgifyW [Internal] * SHELL_ArgifyW [Internal]
@ -48,7 +167,18 @@ static const WCHAR wszEmpty[] = {0};
* %S ??? * %S ???
* %* all following parameters (see batfile) * %* all following parameters (see batfile)
* *
*/ * The way we parse the command line arguments was determined through extensive
* testing and can be summed up by the following rules"
*
* - %2
* - if first letter is " break on first non literal " and include any white spaces
* - if first letter is NOT " break on first " or white space
* - if " is opened any pair of consecutive " results in ONE literal "
*
* - %~2
* - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm
*/
static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len) static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len)
{ {
WCHAR xlpFile[1024]; WCHAR xlpFile[1024];
@ -57,175 +187,188 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR*
PWSTR res = out; PWSTR res = out;
PCWSTR cmd; PCWSTR cmd;
DWORD used = 0; DWORD used = 0;
bool tildeEffect = false;
TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
debugstr_w(lpFile), pidl, args); debugstr_w(lpFile), pidl, args);
DbgPrint("[shell32, SHELL_ArgifyW] Processing %ws\n", args);
DbgPrint("[shell32, SHELL_ArgifyW] fmt = %ws\n", fmt);
while (*fmt) while (*fmt)
{ {
if (*fmt == '%') if (*fmt == '%')
{ {
switch (*++fmt) switch (*++fmt)
{ {
case '\0': case '\0':
case '%': case '%':
used++;
if (used < len)
*res++ = '%';
break;
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case '*':
if (args)
{ {
if (*fmt == '*') used++;
{
used++;
if (used < len)
*res++ = '"';
while(*args)
{
used++;
if (used < len)
*res++ = *args++;
else
args++;
}
used++;
if (used < len)
*res++ = '"';
break;
}
else
{
while(*args && !isspace(*args))
{
used++;
if (used < len)
*res++ = *args++;
else
args++;
}
while(isspace(*args))
++args;
}
break;
}
else
{
break;
}
case '1':
if (!done || (*fmt == '1'))
{
/*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
cmd = xlpFile;
else
cmd = lpFile;
used += wcslen(cmd);
if (used < len) if (used < len)
{ *res++ = '%';
wcscpy(res, cmd); }; break;
res += wcslen(cmd);
}
}
found_p1 = TRUE;
break;
/* case '*':
* IE uses this a lot for activating things such as windows media
* player. This is not verified to be fully correct but it appears
* to work just fine.
*/
case 'l':
case 'L':
if (lpFile) {
used += wcslen(lpFile);
if (used < len)
{
wcscpy(res, lpFile);
res += wcslen(lpFile);
}
}
found_p1 = TRUE;
break;
case 'i':
case 'I':
if (pidl) {
DWORD chars = 0;
/* %p should not exceed 8, maybe 16 when looking forward to 64bit.
* allowing a buffer of 100 should more than exceed all needs */
WCHAR buf[100];
LPVOID pv;
HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
pv = SHLockShared(hmem, 0);
chars = swprintf(buf, wszILPtr, pv);
if (chars >= sizeof(buf)/sizeof(WCHAR))
ERR("pidl format buffer too small!\n");
used += chars;
if (used < len)
{
wcscpy(res,buf);
res += chars;
}
SHUnlockShared(pv);
}
found_p1 = TRUE;
break;
default:
/*
* Check if this is an env-variable here...
*/
/* Make sure that we have at least one more %.*/
if (strchrW(fmt, '%'))
{ {
WCHAR tmpBuffer[1024]; if (args)
PWSTR tmpB = tmpBuffer;
WCHAR tmpEnvBuff[MAX_PATH];
DWORD envRet;
while (*fmt != '%')
*tmpB++ = *fmt++;
*tmpB++ = 0;
TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
if (envRet == 0 || envRet > MAX_PATH)
{ {
used += wcslen(tmpBuffer); if (*fmt == '*')
if (used < len)
{ {
wcscpy( res, tmpBuffer ); used++;
res += wcslen(tmpBuffer); while(*args)
{
used++;
if (used < len)
*res++ = *args++;
else
args++;
}
used++;
break;
} }
} }
else }; break;
case '~':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
//case '0':
{
if (*fmt == '~')
{ {
used += wcslen(tmpEnvBuff); fmt++;
if (used < len) tildeEffect = true;
}
if (args)
{
if (tildeEffect)
{ {
wcscpy( res, tmpEnvBuff ); ParseTildeEffect(res, args, len, used, *fmt - '2');
res += wcslen(tmpEnvBuff); tildeEffect = false;
}
else
{
ParseNoTildeEffect(res, args, len, used, *fmt - '2');
} }
} }
} }; break;
done = TRUE;
break; case '1':
if (!done || (*fmt == '1'))
{
/*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
cmd = xlpFile;
else
cmd = lpFile;
used += wcslen(cmd);
if (used < len)
{
wcscpy(res, cmd);
res += wcslen(cmd);
}
}
found_p1 = TRUE;
break;
/*
* IE uses this a lot for activating things such as windows media
* player. This is not verified to be fully correct but it appears
* to work just fine.
*/
case 'l':
case 'L':
if (lpFile)
{
used += wcslen(lpFile);
if (used < len)
{
wcscpy(res, lpFile);
res += wcslen(lpFile);
}
}
found_p1 = TRUE;
break;
case 'i':
case 'I':
if (pidl)
{
DWORD chars = 0;
/* %p should not exceed 8, maybe 16 when looking forward to 64bit.
* allowing a buffer of 100 should more than exceed all needs */
WCHAR buf[100];
LPVOID pv;
HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
pv = SHLockShared(hmem, 0);
chars = swprintf(buf, wszILPtr, pv);
if (chars >= sizeof(buf)/sizeof(WCHAR))
ERR("pidl format buffer too small!\n");
used += chars;
if (used < len)
{
wcscpy(res,buf);
res += chars;
}
SHUnlockShared(pv);
}
found_p1 = TRUE;
break;
default:
/*
* Check if this is an env-variable here...
*/
/* Make sure that we have at least one more %.*/
if (strchrW(fmt, '%'))
{
WCHAR tmpBuffer[1024];
PWSTR tmpB = tmpBuffer;
WCHAR tmpEnvBuff[MAX_PATH];
DWORD envRet;
while (*fmt != '%')
*tmpB++ = *fmt++;
*tmpB++ = 0;
TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
if (envRet == 0 || envRet > MAX_PATH)
{
used += wcslen(tmpBuffer);
if (used < len)
{
wcscpy( res, tmpBuffer );
res += wcslen(tmpBuffer);
}
}
else
{
used += wcslen(tmpEnvBuff);
if (used < len)
{
wcscpy( res, tmpEnvBuff );
res += wcslen(tmpEnvBuff);
}
}
}
done = TRUE;
break;
} }
/* Don't skip past terminator (catch a single '%' at the end) */ /* Don't skip past terminator (catch a single '%' at the end) */
if (*fmt != '\0') if (*fmt != '\0')
@ -248,6 +391,8 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR*
if (out_len) if (out_len)
*out_len = used; *out_len = used;
DbgPrint("[shell32, SHELL_ArgifyW] Done result = %ws\n", out);
return found_p1; return found_p1;
} }
@ -258,13 +403,14 @@ static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR psz
HRESULT hr = SHGetDesktopFolder(&desktop); HRESULT hr = SHGetDesktopFolder(&desktop);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret); {
hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
StrRetToStrNW(pszPath, uOutSize, &strret, pidl); StrRetToStrNW(pszPath, uOutSize, &strret, pidl);
desktop->Release(); desktop->Release();
} }
return hr; return hr;
@ -298,17 +444,23 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
/* ShellExecute specifies the command from psei->lpDirectory /* ShellExecute specifies the command from psei->lpDirectory
* if present. Not from the current dir as CreateProcess does */ * if present. Not from the current dir as CreateProcess does */
if( lpDirectory ) if ( lpDirectory )
if( ( gcdret = GetCurrentDirectoryW( MAX_PATH, curdir))) if ( ( gcdret = GetCurrentDirectoryW( MAX_PATH, curdir)))
if( !SetCurrentDirectoryW( lpDirectory)) if ( !SetCurrentDirectoryW( lpDirectory))
ERR("cannot set directory %s\n", debugstr_w(lpDirectory)); ERR("cannot set directory %s\n", debugstr_w(lpDirectory));
ZeroMemory(&startup,sizeof(STARTUPINFOW)); ZeroMemory(&startup,sizeof(STARTUPINFOW));
startup.cb = sizeof(STARTUPINFOW); startup.cb = sizeof(STARTUPINFOW);
startup.dwFlags = STARTF_USESHOWWINDOW; startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = psei->nShow; startup.wShowWindow = psei->nShow;
dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
if (psei->fMask & SEE_MASK_NO_CONSOLE) if (psei->fMask & SEE_MASK_NO_CONSOLE)
dwCreationFlags |= CREATE_NEW_CONSOLE; dwCreationFlags |= CREATE_NEW_CONSOLE;
//DbgPrint("[shell32, SHELL_ExecuteW] CreateProcessW cmd = %ws\n", (LPWSTR)lpCmd);
//DbgBreakPoint();
if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env, if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env,
lpDirectory, &startup, &info)) lpDirectory, &startup, &info))
{ {
@ -318,6 +470,7 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
if (WaitForInputIdle( info.hProcess, 30000 ) == WAIT_FAILED) if (WaitForInputIdle( info.hProcess, 30000 ) == WAIT_FAILED)
WARN("WaitForInputIdle failed: Error %d\n", GetLastError() ); WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
retval = 33; retval = 33;
if (psei->fMask & SEE_MASK_NOCLOSEPROCESS) if (psei->fMask & SEE_MASK_NOCLOSEPROCESS)
psei_out->hProcess = info.hProcess; psei_out->hProcess = info.hProcess;
else else
@ -333,6 +486,7 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
TRACE("returning %lu\n", retval); TRACE("returning %lu\n", retval);
psei_out->hInstApp = (HINSTANCE)retval; psei_out->hInstApp = (HINSTANCE)retval;
if( gcdret ) if( gcdret )
if( !SetCurrentDirectoryW( curdir)) if( !SetCurrentDirectoryW( curdir))
ERR("cannot return to directory %s\n", debugstr_w(curdir)); ERR("cannot return to directory %s\n", debugstr_w(curdir));
@ -626,8 +780,9 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera
&filetypelen) == ERROR_SUCCESS) &filetypelen) == ERROR_SUCCESS)
{ {
filetypelen /= sizeof(WCHAR); filetypelen /= sizeof(WCHAR);
if (filetypelen == sizeof(filetype)/sizeof(WCHAR)) if (filetypelen == sizeof(filetype)/sizeof(WCHAR))
filetypelen--; filetypelen--;
filetype[filetypelen] = '\0'; filetype[filetypelen] = '\0';
TRACE("File type: %s\n", debugstr_w(filetype)); TRACE("File type: %s\n", debugstr_w(filetype));
} }
@ -644,41 +799,41 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera
filetype[filetypelen] = '\0'; filetype[filetypelen] = '\0';
retval = SHELL_FindExecutableByOperation(lpOperation, key, filetype, command, sizeof(command)); retval = SHELL_FindExecutableByOperation(lpOperation, key, filetype, command, sizeof(command));
if (retval > 32) if (retval > 32)
{
DWORD finishedLen;
SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen);
if (finishedLen > resultLen)
ERR("Argify buffer not large enough.. truncated\n");
/* Remove double quotation marks and command line arguments */
if (*lpResult == '"')
{ {
WCHAR *p = lpResult; DWORD finishedLen;
while (*(p + 1) != '"') SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen);
{ if (finishedLen > resultLen)
*p = *(p + 1); ERR("Argify buffer not large enough.. truncated\n");
p++; DbgPrint("[shell32, SHELL_FindExecutable] Remove double quotation marks and command line arguments\n");
} /* Remove double quotation marks and command line arguments */
*p = '\0'; if (*lpResult == '"')
} {
WCHAR *p = lpResult;
while (*(p + 1) != '"')
{
*p = *(p + 1);
p++;
}
*p = '\0';
}
else else
{ {
/* Truncate on first space */ /* Truncate on first space */
WCHAR *p = lpResult; WCHAR *p = lpResult;
while (*p != ' ' && *p != '\0') while (*p != ' ' && *p != '\0')
p++; p++;
*p='\0'; *p='\0';
} }
} }
} }
else /* Check win.ini */ else /* Check win.ini */
{ {
static const WCHAR wExtensions[] = {'e','x','t','e','n','s','i','o','n','s',0}; static const WCHAR wExtensions[] = {'e','x','t','e','n','s','i','o','n','s',0};
/* Toss the leading dot */ /* Toss the leading dot */
extension++; extension++;
if (GetProfileStringW(wExtensions, extension, wszEmpty, command, sizeof(command)/sizeof(WCHAR)) > 0) if (GetProfileStringW(wExtensions, extension, wszEmpty, command, sizeof(command)/sizeof(WCHAR)) > 0)
{ {
if (wcslen(command) != 0) if (wcslen(command) != 0)
{ {
@ -1370,11 +1525,14 @@ static UINT_PTR SHELL_quote_and_execute( LPCWSTR wcmd, LPCWSTR wszParameters, LP
strcpyW(wszQuotedCmd, wQuote); strcpyW(wszQuotedCmd, wQuote);
strcatW(wszQuotedCmd, wcmd); strcatW(wszQuotedCmd, wcmd);
strcatW(wszQuotedCmd, wQuote); strcatW(wszQuotedCmd, wQuote);
if (wszParameters[0]) { if (wszParameters[0])
{
strcatW(wszQuotedCmd, wSpace); strcatW(wszQuotedCmd, wSpace);
strcatW(wszQuotedCmd, wszParameters); strcatW(wszQuotedCmd, wszParameters);
} }
TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol)); TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol));
if (*lpstrProtocol) if (*lpstrProtocol)
retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out);
else else
@ -1498,14 +1656,20 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
else if (*sei_tmp.lpFile == '\"') else if (*sei_tmp.lpFile == '\"')
{ {
DWORD l = strlenW(sei_tmp.lpFile+1); DWORD l = strlenW(sei_tmp.lpFile+1);
if(l >= dwApplicationNameLen) dwApplicationNameLen = l+1; if(l >= dwApplicationNameLen)
dwApplicationNameLen = l+1;
wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR)); wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
memcpy(wszApplicationName, sei_tmp.lpFile+1, (l+1)*sizeof(WCHAR)); memcpy(wszApplicationName, sei_tmp.lpFile+1, (l+1)*sizeof(WCHAR));
if (wszApplicationName[l-1] == '\"') if (wszApplicationName[l-1] == '\"')
wszApplicationName[l-1] = '\0'; wszApplicationName[l-1] = '\0';
appKnownSingular = TRUE; appKnownSingular = TRUE;
TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName)); TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName));
} else { }
else
{
DWORD l = strlenW(sei_tmp.lpFile)+1; DWORD l = strlenW(sei_tmp.lpFile)+1;
if(l > dwApplicationNameLen) dwApplicationNameLen = l+1; if(l > dwApplicationNameLen) dwApplicationNameLen = l+1;
wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR)); wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
@ -1521,10 +1685,10 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
wszParameters = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); wszParameters = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
parametersLen = len; parametersLen = len;
} }
strcpyW(wszParameters, sei_tmp.lpParameters); strcpyW(wszParameters, sei_tmp.lpParameters);
} }
else else
*wszParameters = '\0'; *wszParameters = '\0';
wszDir = dirBuffer; wszDir = dirBuffer;
if (sei_tmp.lpDirectory) if (sei_tmp.lpDirectory)
@ -1535,10 +1699,10 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
wszDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); wszDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
dirLen = len; dirLen = len;
} }
strcpyW(wszDir, sei_tmp.lpDirectory); strcpyW(wszDir, sei_tmp.lpDirectory);
} }
else else
*wszDir = '\0'; *wszDir = '\0';
/* adjust string pointers to point to the new buffers */ /* adjust string pointers to point to the new buffers */
sei_tmp.lpFile = wszApplicationName; sei_tmp.lpFile = wszApplicationName;
@ -1553,25 +1717,26 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
/* process the IDList */ /* process the IDList */
if (sei_tmp.fMask & SEE_MASK_IDLIST) if (sei_tmp.fMask & SEE_MASK_IDLIST)
{ {
IShellExecuteHookW* pSEH; IShellExecuteHookW* pSEH;
HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL); HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = pSEH->Execute(&sei_tmp); hr = pSEH->Execute(&sei_tmp);
pSEH->Release(); pSEH->Release();
if (hr == S_OK) { if (hr == S_OK)
{
HeapFree(GetProcessHeap(), 0, wszApplicationName); HeapFree(GetProcessHeap(), 0, wszApplicationName);
if (wszParameters != parametersBuffer) if (wszParameters != parametersBuffer)
HeapFree(GetProcessHeap(), 0, wszParameters); HeapFree(GetProcessHeap(), 0, wszParameters);
if (wszDir != dirBuffer) if (wszDir != dirBuffer)
HeapFree(GetProcessHeap(), 0, wszDir); HeapFree(GetProcessHeap(), 0, wszDir);
return TRUE; return TRUE;
} }
} }
SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName); SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName);
appKnownSingular = TRUE; appKnownSingular = TRUE;
@ -1677,65 +1842,71 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir)); TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
/* separate out command line arguments from executable file name */ /* separate out command line arguments from executable file name */
if (!*sei_tmp.lpParameters && !appKnownSingular) { if (!*sei_tmp.lpParameters && !appKnownSingular)
/* If the executable path is quoted, handle the rest of the command line as parameters. */ {
if (sei_tmp.lpFile[0] == '"') { /* If the executable path is quoted, handle the rest of the command line as parameters. */
LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1; if (sei_tmp.lpFile[0] == '"')
LPWSTR dst = wfileName; {
LPWSTR end; LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1;
LPWSTR dst = wfileName;
LPWSTR end;
/* copy the unquoted executable path to 'wfileName' */ /* copy the unquoted executable path to 'wfileName' */
while(*src && *src!='"') while(*src && *src!='"')
*dst++ = *src++; *dst++ = *src++;
*dst = '\0'; *dst = '\0';
if (*src == '"') { if (*src == '"')
end = ++src; {
end = ++src;
while(isspace(*src)) while(isspace(*src))
++src; ++src;
} else }
end = src; else
end = src;
/* copy the parameter string to 'wszParameters' */ /* copy the parameter string to 'wszParameters' */
strcpyW(wszParameters, src); strcpyW(wszParameters, src);
/* terminate previous command string after the quote character */ /* terminate previous command string after the quote character */
*end = '\0'; *end = '\0';
}
else
{
/* If the executable name is not quoted, we have to use this search loop here,
that in CreateProcess() is not sufficient because it does not handle shell links. */
WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
LPWSTR space, s;
LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
for(s=beg; (space= const_cast<LPWSTR>(strchrW(s, ' '))); s=space+1) {
int idx = space-sei_tmp.lpFile;
memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
buffer[idx] = '\0';
/*FIXME This finds directory paths if the targeted file name contains spaces. */
if (SearchPathW(*sei_tmp.lpDirectory? sei_tmp.lpDirectory: NULL, buffer, wszExe, sizeof(xlpFile)/sizeof(xlpFile[0]), xlpFile, NULL))
{
/* separate out command from parameter string */
LPCWSTR p = space + 1;
while(isspaceW(*p))
++p;
strcpyW(wszParameters, p);
*space = '\0';
break;
}
} }
else
{
/* If the executable name is not quoted, we have to use this search loop here,
that in CreateProcess() is not sufficient because it does not handle shell links. */
WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
LPWSTR space, s;
lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR)); LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
} for(s=beg; (space= const_cast<LPWSTR>(strchrW(s, ' '))); s=space+1)
} else {
int idx = space-sei_tmp.lpFile;
memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
buffer[idx] = '\0';
/*FIXME This finds directory paths if the targeted file name contains spaces. */
if (SearchPathW(*sei_tmp.lpDirectory? sei_tmp.lpDirectory: NULL, buffer, wszExe, sizeof(xlpFile)/sizeof(xlpFile[0]), xlpFile, NULL))
{
/* separate out command from parameter string */
LPCWSTR p = space + 1;
while(isspaceW(*p))
++p;
strcpyW(wszParameters, p);
*space = '\0';
break;
}
}
lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR));
}
}
else
lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR)); lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR));
lpFile = wfileName; lpFile = wfileName;
@ -1750,13 +1921,15 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
wcmdLen = len; wcmdLen = len;
} }
strcpyW(wcmd, wszApplicationName); strcpyW(wcmd, wszApplicationName);
if (sei_tmp.lpParameters[0]) { if (sei_tmp.lpParameters[0])
{
strcatW(wcmd, wSpace); strcatW(wcmd, wSpace);
strcatW(wcmd, wszParameters); strcatW(wcmd, wszParameters);
} }
retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
if (retval > 32) { if (retval > 32)
{
HeapFree(GetProcessHeap(), 0, wszApplicationName); HeapFree(GetProcessHeap(), 0, wszApplicationName);
if (wszParameters != parametersBuffer) if (wszParameters != parametersBuffer)
HeapFree(GetProcessHeap(), 0, wszParameters); HeapFree(GetProcessHeap(), 0, wszParameters);
@ -1894,7 +2067,7 @@ BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW)); memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
if (sei->lpVerb) if (sei->lpVerb)
seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb); seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
if (sei->lpFile) if (sei->lpFile)
seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile); seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);