From c8b09007ed7a44eff45a4ed85287c99f96e49b70 Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Thu, 23 Apr 2009 12:39:11 +0000 Subject: [PATCH] - Call OpenWith dialog when no file association is found - Partial wine syncAlexandre Julliard shell32: Add implementation of WOWShellExecute. The callback is probably not fully compatible, but apps are not supposed to use that function anyway. Vincent Povirk shell32: Add a message for opening files with no association. Marcus Meissner shell32: Fixed potential buffer overwrite in execute_from_key (Coverity). Vincent Povirk shell32: Use the NO_UI flag in ShellExecute. In my testing on Windows, I wasn't able to make ShellExecute display any error dialogs. Paul Chitescu shell32.dll: Return SE_ERR_FNF if file not found before checking for associations. svn path=/trunk/; revision=40666 --- reactos/dll/win32/shell32/shell32_main.h | 2 +- reactos/dll/win32/shell32/shlexec.c | 550 ++++++++++++++++------- reactos/dll/win32/shell32/shresdef.h | 3 + reactos/dll/win32/shell32/stubs.c | 17 - 4 files changed, 390 insertions(+), 182 deletions(-) diff --git a/reactos/dll/win32/shell32/shell32_main.h b/reactos/dll/win32/shell32/shell32_main.h index 23a935d2827..53778370eeb 100644 --- a/reactos/dll/win32/shell32/shell32_main.h +++ b/reactos/dll/win32/shell32/shell32_main.h @@ -213,7 +213,7 @@ static LPWSTR __inline __SHCloneStrAtoW(WCHAR ** target, const char * source) #define HINSTANCE_16(h32) (LOWORD(h32)) typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, - LPSHELLEXECUTEINFOW sei, LPSHELLEXECUTEINFOW sei_out); + const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out); BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc); diff --git a/reactos/dll/win32/shell32/shlexec.c b/reactos/dll/win32/shell32/shlexec.c index b7d55dd0830..d4c36f33882 100644 --- a/reactos/dll/win32/shell32/shlexec.c +++ b/reactos/dll/win32/shell32/shlexec.c @@ -165,7 +165,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp case 'I': if (pidl) { INT chars = 0; - /* %p should not exceed 8, maybe 16 when looking foward to 64bit. + /* %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; @@ -191,7 +191,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp */ /* Make sure that we have at least one more %.*/ - if (wcschr(fmt, '%')) + if (strchrW(fmt, '%')) { WCHAR tmpBuffer[1024]; PWSTR tmpB = tmpBuffer; @@ -275,7 +275,7 @@ static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR psz * */ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, - LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) + const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out) { STARTUPINFOW startup; PROCESS_INFORMATION info; @@ -664,9 +664,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera } else { - /* Truncate on first space, like Windows: - * http://support.microsoft.com/?scid=kb%3Ben-us%3B140724 - */ + /* Truncate on first space */ WCHAR *p = lpResult; while (*p != ' ' && *p != '\0') p++; @@ -728,10 +726,10 @@ static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv, * launching an application and trying (#2) to connect to it * */ -static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, +static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec, const WCHAR* lpFile, WCHAR *env, LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc, - LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) + const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out) { static const WCHAR wApplication[] = {'\\','a','p','p','l','i','c','a','t','i','o','n',0}; static const WCHAR wTopic[] = {'\\','t','o','p','i','c',0}; @@ -771,7 +769,7 @@ static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, else { LPWSTR p,space; - for (p=(LPWSTR)start; (space=wcschr(p, ' ')); p=space+1) + for (p=(LPWSTR)start; (space=strchrW(p, ' ')); p=space+1) { int idx = space-start; memcpy(command, start, idx*sizeof(WCHAR)); @@ -796,7 +794,7 @@ static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, !wcscmp(ptr, wSo)) *ptr = 0; - ptr = wcschr(app, '.'); + ptr = strrchrW(app, '.'); assert(ptr); *ptr = 0; } @@ -843,7 +841,7 @@ static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, SetLastError(ERROR_DDE_FAIL); return 30; /* whatever */ } - wcscpy(endkey, wIfexec); + strcpyW(endkey, wIfexec); ifexeclen = sizeof(ifexec); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS) { @@ -860,7 +858,7 @@ static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request. */ if (unicode) - hDdeData = DdeClientTransaction((LPBYTE)res, (wcslen(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, + hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid); else { @@ -888,7 +886,7 @@ static unsigned dde_connect(WCHAR* key, const WCHAR* start, WCHAR* ddeexec, /************************************************************************* * execute_from_key [Internal] */ -static UINT_PTR execute_from_key(LPWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR szCommandline, +static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR szCommandline, LPCWSTR executable_name, SHELL_ExecuteW32 execfunc, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) @@ -914,8 +912,8 @@ static UINT_PTR execute_from_key(LPWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR /* Is there a replace() function anywhere? */ cmdlen /= sizeof(WCHAR); - if (cmdlen >= sizeof(cmd)/sizeof(WCHAR)) - cmdlen = sizeof(cmd)/sizeof(WCHAR)-1; + if (cmdlen >= sizeof(cmd)/sizeof(WCHAR)) + cmdlen = sizeof(cmd)/sizeof(WCHAR)-1; cmd[cmdlen] = '\0'; SHELL_ArgifyW(param, sizeof(param)/sizeof(WCHAR), cmd, lpFile, psei->lpIDList, szCommandline, &resultLen); if (resultLen > sizeof(param)/sizeof(WCHAR)) @@ -924,14 +922,14 @@ static UINT_PTR execute_from_key(LPWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR /* Get the parameters needed by the application from the associated ddeexec key */ - tmp = wcsstr(key, wCommand); + tmp = strstrW(key, wCommand); assert(tmp); wcscpy(tmp, wDdeexec); if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, &ddeexeclen) == ERROR_SUCCESS) { TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec)); - if (!param[0]) wcscpy(param, executable_name); + if (!param[0]) strcpyW(param, executable_name); retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, psei->lpIDList, execfunc, psei, psei_out); } else if (param[0]) @@ -1017,7 +1015,7 @@ HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpR } /* FIXME: is this already implemented somewhere else? */ -static HKEY ShellExecute_GetClassKey( LPSHELLEXECUTEINFOW sei ) +static HKEY ShellExecute_GetClassKey( const SHELLEXECUTEINFOW *sei ) { LPCWSTR ext = NULL, lpClass = NULL; LPWSTR cls = NULL; @@ -1078,10 +1076,11 @@ static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) else { WCHAR fullpath[MAX_PATH]; + BOOL ret; fullpath[0] = 0; - r = GetFullPathNameW( sei->lpFile, MAX_PATH, fullpath, NULL ); - if (!r) + ret = GetFullPathNameW( sei->lpFile, MAX_PATH, fullpath, NULL ); + if (!ret) goto end; pidl = ILCreateFromPathW( fullpath ); @@ -1272,25 +1271,196 @@ static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei ) return r; } +static UINT_PTR SHELL_execute_class( LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc ) +{ + static const WCHAR wSpace[] = {' ',0}; + WCHAR execCmd[1024], wcmd[1024]; + /* launch a document by fileclass like 'WordPad.Document.1' */ + /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ + /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */ + ULONG cmask=(psei->fMask & SEE_MASK_CLASSALL); + DWORD resultLen; + BOOL done; + + HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL, + (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass: NULL, + psei->lpVerb, + execCmd, sizeof(execCmd)); + + /* FIXME: get the extension of lpFile, check if it fits to the lpClass */ + TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName)); + + wcmd[0] = '\0'; + done = SHELL_ArgifyW(wcmd, sizeof(wcmd)/sizeof(WCHAR), execCmd, wszApplicationName, psei->lpIDList, NULL, &resultLen); + if (!done && wszApplicationName[0]) + { + strcatW(wcmd, wSpace); + strcatW(wcmd, wszApplicationName); + } + if (resultLen > sizeof(wcmd)/sizeof(WCHAR)) + ERR("Argify buffer not large enough... truncating\n"); + return execfunc(wcmd, NULL, FALSE, psei, psei_out); +} + +static BOOL SHELL_translate_idlist( LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen ) +{ + static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0}; + WCHAR buffer[MAX_PATH]; + BOOL appKnownSingular = FALSE; + + /* last chance to translate IDList: now also allow CLSID paths */ + if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW(sei->lpIDList, buffer, sizeof(buffer)))) { + if (buffer[0]==':' && buffer[1]==':') { + /* open shell folder for the specified class GUID */ + if (strlenW(buffer) + 1 > parametersLen) + ERR("parameters len exceeds buffer size (%i > %i), truncating\n", + lstrlenW(buffer) + 1, parametersLen); + lstrcpynW(wszParameters, buffer, parametersLen); + if (strlenW(wExplorer) > dwApplicationNameLen) + ERR("application len exceeds buffer size (%i > %i), truncating\n", + lstrlenW(wExplorer) + 1, dwApplicationNameLen); + lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen); + appKnownSingular = TRUE; + + sei->fMask &= ~SEE_MASK_INVOKEIDLIST; + } else { + WCHAR target[MAX_PATH]; + DWORD attribs; + DWORD resultLen; + /* Check if we're executing a directory and if so use the + handler for the Folder class */ + strcpyW(target, buffer); + attribs = GetFileAttributesW(buffer); + if (attribs != INVALID_FILE_ATTRIBUTES && + (attribs & FILE_ATTRIBUTE_DIRECTORY) && + HCR_GetExecuteCommandW(0, wszFolder, + sei->lpVerb, + buffer, sizeof(buffer))) { + SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, + buffer, target, sei->lpIDList, NULL, &resultLen); + if (resultLen > dwApplicationNameLen) + ERR("Argify buffer not large enough... truncating\n"); + appKnownSingular = FALSE; + } + sei->fMask &= ~SEE_MASK_INVOKEIDLIST; + } + } + return appKnownSingular; +} + +static UINT_PTR SHELL_quote_and_execute( LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc ) +{ + static const WCHAR wQuote[] = {'"',0}; + static const WCHAR wSpace[] = {' ',0}; + UINT_PTR retval; + DWORD len; + WCHAR *wszQuotedCmd; + + /* Length of quotes plus length of command plus NULL terminator */ + len = 2 + lstrlenW(wcmd) + 1; + if (wszParameters[0]) + { + /* Length of space plus length of parameters */ + len += 1 + lstrlenW(wszParameters); + } + wszQuotedCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + /* Must quote to handle case where cmd contains spaces, + * else security hole if malicious user creates executable file "C:\\Program" + */ + strcpyW(wszQuotedCmd, wQuote); + strcatW(wszQuotedCmd, wcmd); + strcatW(wszQuotedCmd, wQuote); + if (wszParameters[0]) { + strcatW(wszQuotedCmd, wSpace); + strcatW(wszQuotedCmd, wszParameters); + } + TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol)); + if (*lpstrProtocol) + retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); + else + retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out); + HeapFree(GetProcessHeap(), 0, wszQuotedCmd); + return retval; +} + +static UINT_PTR SHELL_execute_url( LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc ) +{ + static const WCHAR wShell[] = {'\\','s','h','e','l','l','\\',0}; + static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0}; + UINT_PTR retval; + WCHAR *lpstrProtocol; + LPCWSTR lpstrRes; + INT iSize; + DWORD len; + + lpstrRes = strchrW(lpFile, ':'); + if (lpstrRes) + iSize = lpstrRes - lpFile; + else + iSize = strlenW(lpFile); + + TRACE("Got URL: %s\n", debugstr_w(lpFile)); + /* Looking for ...protocol\shell\lpOperation\command */ + len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1; + if (psei->lpVerb) + len += lstrlenW(psei->lpVerb); + else + len += lstrlenW(wszOpen); + lpstrProtocol = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + memcpy(lpstrProtocol, lpFile, iSize*sizeof(WCHAR)); + lpstrProtocol[iSize] = '\0'; + strcatW(lpstrProtocol, wShell); + strcatW(lpstrProtocol, psei->lpVerb? psei->lpVerb: wszOpen); + strcatW(lpstrProtocol, wCommand); + + /* Remove File Protocol from lpFile */ + /* In the case file://path/file */ + if (!strncmpiW(lpFile, wFile, iSize)) + { + lpFile += iSize; + while (*lpFile == ':') lpFile++; + } + retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters, + wcmd, execfunc, psei, psei_out); + HeapFree(GetProcessHeap(), 0, lpstrProtocol); + return retval; +} + +void do_error_dialog( UINT_PTR retval, HWND hwnd ) +{ + WCHAR msg[2048]; + int error_code=GetLastError(); + + //FIXME + // call SHOpenWithDialog + if (retval == SE_ERR_NOASSOC) + LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg)/sizeof(WCHAR)); + else + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, msg, sizeof(msg)/sizeof(WCHAR), NULL); + + MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); +} + /************************************************************************* * SHELL_execute [Internal] */ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) { - static const WCHAR wQuote[] = {'"',0}; static const WCHAR wSpace[] = {' ',0}; static const WCHAR wWww[] = {'w','w','w',0}; static const WCHAR wFile[] = {'f','i','l','e',0}; static const WCHAR wHttp[] = {'h','t','t','p',':','/','/',0}; - static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0}; - //static const WCHAR wProperties[] = { 'p','r','o','p','e','r','t','i','e','s',0 }; static const DWORD unsupportedFlags = - SEE_MASK_INVOKEIDLIST | SEE_MASK_HOTKEY | - SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | + SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY | + SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_UNICODE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR; - WCHAR *wszApplicationName, wszParameters[1024], wszDir[MAX_PATH]; + WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024]; + WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd; DWORD dwApplicationNameLen = MAX_PATH+2; + DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR); + DWORD dirLen = sizeof(dirBuffer) / sizeof(WCHAR); + DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR); DWORD len; SHELLEXECUTEINFOW sei_tmp; /* modifiable copy of SHELLEXECUTEINFO struct */ WCHAR wfileName[MAX_PATH]; @@ -1298,13 +1468,10 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) WCHAR lpstrProtocol[256]; LPCWSTR lpFile; UINT_PTR retval = SE_ERR_NOASSOC; - WCHAR wcmd[1024]; - WCHAR buffer[MAX_PATH]; - BOOL done; BOOL appKnownSingular = FALSE; /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */ - memcpy(&sei_tmp, sei, sizeof(sei_tmp)); + sei_tmp = *sei; TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb), @@ -1323,7 +1490,7 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) } else if (*sei_tmp.lpFile == '\"') { - DWORD l = wcslen(sei_tmp.lpFile+1); + DWORD l = strlenW(sei_tmp.lpFile+1); if(l >= dwApplicationNameLen) dwApplicationNameLen = l+1; wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR)); memcpy(wszApplicationName, sei_tmp.lpFile+1, (l+1)*sizeof(WCHAR)); @@ -1332,19 +1499,37 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) appKnownSingular = TRUE; TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName)); } else { - DWORD l = wcslen(sei_tmp.lpFile)+1; + DWORD l = strlenW(sei_tmp.lpFile)+1; if(l > dwApplicationNameLen) dwApplicationNameLen = l+1; wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR)); memcpy(wszApplicationName, sei_tmp.lpFile, l*sizeof(WCHAR)); } + wszParameters = parametersBuffer; if (sei_tmp.lpParameters) - wcscpy(wszParameters, sei_tmp.lpParameters); + { + len = lstrlenW(sei_tmp.lpParameters) + 1; + if (len > parametersLen) + { + wszParameters = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + parametersLen = len; + } + strcpyW(wszParameters, sei_tmp.lpParameters); + } else *wszParameters = '\0'; + wszDir = dirBuffer; if (sei_tmp.lpDirectory) - wcscpy(wszDir, sei_tmp.lpDirectory); + { + len = lstrlenW(sei_tmp.lpDirectory) + 1; + if (len > dirLen) + { + wszDir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + dirLen = len; + } + strcpyW(wszDir, sei_tmp.lpDirectory); + } else *wszDir = '\0'; @@ -1367,12 +1552,16 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) if (SUCCEEDED(hr)) { - hr = pSEH->lpVtbl->Execute(pSEH, &sei_tmp); + hr = IShellExecuteHookW_Execute(pSEH, &sei_tmp); - pSEH->lpVtbl->Release(pSEH); + IShellExecuteHookW_Release(pSEH); if (hr == S_OK) { HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); return TRUE; } } @@ -1386,80 +1575,48 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) { sei->hInstApp = (HINSTANCE) 33; HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); return TRUE; } -#if 0 - if (sei_tmp.lpVerb && !wcscmp(sei_tmp.lpVerb, wProperties)) - { - SH_ShowPropertiesDialog(sei_tmp.lpFile); - sei->hInstApp = (HINSTANCE) 33; - return TRUE; - } -#endif + if (sei_tmp.fMask & SEE_MASK_CLASSALL) { - /* launch a document by fileclass like 'WordPad.Document.1' */ - /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ - /* FIXME: szCommandline should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */ - ULONG cmask=(sei_tmp.fMask & SEE_MASK_CLASSALL); - DWORD resultLen; - HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? sei_tmp.hkeyClass : NULL, - (cmask == SEE_MASK_CLASSNAME) ? sei_tmp.lpClass: NULL, - sei_tmp.lpVerb, - wszParameters, sizeof(wszParameters)/sizeof(WCHAR)); - - /* FIXME: get the extension of lpFile, check if it fits to the lpClass */ - TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(wszParameters), debugstr_w(wszApplicationName)); - - wcmd[0] = '\0'; - done = SHELL_ArgifyW(wcmd, sizeof(wcmd)/sizeof(WCHAR), wszParameters, wszApplicationName, sei_tmp.lpIDList, NULL, &resultLen); - if (!done && wszApplicationName[0]) + retval = SHELL_execute_class( wszApplicationName, &sei_tmp, sei, + execfunc ); + if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) { - wcscat(wcmd, wSpace); - wcscat(wcmd, wszApplicationName); - } - if (resultLen > sizeof(wcmd)/sizeof(WCHAR)) - ERR("Argify buffer not large enough... truncating\n"); - retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); + OPENASINFO Info; + //FIXME + // need full path + + Info.pcszFile = wszApplicationName; + Info.pcszClass = NULL; + Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; + + if (SHOpenWithDialog(sei_tmp.hwnd, &Info)) + retval = 33; //FIXME + else + do_error_dialog(retval, sei_tmp.hwnd); + } HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); return retval > 32; } /* Has the IDList not yet been translated? */ if (sei_tmp.fMask & SEE_MASK_IDLIST) { - /* last chance to translate IDList: now also allow CLSID paths */ - if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW(sei_tmp.lpIDList, buffer, sizeof(buffer)))) { - if (buffer[0]==':' && buffer[1]==':') { - /* open shell folder for the specified class GUID */ - wcscpy(wszParameters, buffer); - wcscpy(wszApplicationName, wExplorer); - appKnownSingular = TRUE; - - sei_tmp.fMask &= ~SEE_MASK_INVOKEIDLIST; - } else { - WCHAR target[MAX_PATH]; - DWORD attribs; - DWORD resultLen; - /* Check if we're executing a directory and if so use the - handler for the Folder class */ - wcscpy(target, buffer); - attribs = GetFileAttributesW(buffer); - if (attribs != INVALID_FILE_ATTRIBUTES && - (attribs & FILE_ATTRIBUTE_DIRECTORY) && - HCR_GetExecuteCommandW(0, wszFolder, - sei_tmp.lpVerb, - buffer, sizeof(buffer))) { - SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, - buffer, target, sei_tmp.lpIDList, NULL, &resultLen); - if (resultLen > dwApplicationNameLen) - ERR("Argify buffer not large enough... truncating\n"); - appKnownSingular = FALSE; - } - sei_tmp.fMask &= ~SEE_MASK_INVOKEIDLIST; - } - } + appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters, + parametersLen, + wszApplicationName, + dwApplicationNameLen ); } /* expand environment strings */ @@ -1487,10 +1644,11 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) len++; buf = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR)); ExpandEnvironmentStringsW(sei_tmp.lpParameters, buf, len); - if (len > 1024) - ERR("Parameters exceeds buffer size (%i > 1024)\n",len); - lstrcpynW(wszParameters, buf, min(1024,len)); - HeapFree(GetProcessHeap(),0,buf); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + wszParameters = buf; + parametersLen = len; + sei_tmp.lpParameters = wszParameters; } } @@ -1503,10 +1661,10 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) len++; buf = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR)); ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len); - if (len > 1024) - ERR("Directory exceeds buffer size (%i > 1024)\n",len); - lstrcpynW(wszDir, buf, min(1024,len)); - HeapFree(GetProcessHeap(),0,buf); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); + wszDir = buf; + sei_tmp.lpDirectory = wszDir; } } @@ -1536,7 +1694,7 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) end = src; /* copy the parameter string to 'wszParameters' */ - wcscpy(wszParameters, src); + strcpyW(wszParameters, src); /* terminate previous command string after the quote character */ *end = '\0'; @@ -1549,114 +1707,142 @@ BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) LPWSTR space, s; LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/; - for(s=beg; (space=wcschr(s, ' ')); s=space+1) { + for(s=beg; (space=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), xlpFile, NULL)) + 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(iswspace(*p)) + while(isspaceW(*p)) ++p; - wcscpy(wszParameters, p); + strcpyW(wszParameters, p); *space = '\0'; break; } } - wcscpy(wfileName, sei_tmp.lpFile); + lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR)); } } else - wcscpy(wfileName, sei_tmp.lpFile); + lstrcpynW(wfileName, sei_tmp.lpFile,sizeof(wfileName)/sizeof(WCHAR)); lpFile = wfileName; - wcscpy(wcmd, wszApplicationName); + wcmd = wcmdBuffer; + len = lstrlenW(wszApplicationName) + 1; + if (sei_tmp.lpParameters[0]) + len += 1 + lstrlenW(wszParameters); + if (len > wcmdLen) + { + wcmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + wcmdLen = len; + } + strcpyW(wcmd, wszApplicationName); if (sei_tmp.lpParameters[0]) { - wcscat(wcmd, wSpace); - wcscat(wcmd, wszParameters); + strcatW(wcmd, wSpace); + strcatW(wcmd, wszParameters); } retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); if (retval > 32) { HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); + if (wcmd != wcmdBuffer) + HeapFree(GetProcessHeap(), 0, wcmd); return TRUE; } /* Else, try to find the executable */ wcmd[0] = '\0'; - retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, 1024, lpstrProtocol, &env, sei_tmp.lpIDList, sei_tmp.lpParameters); + retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, lpstrProtocol, &env, sei_tmp.lpIDList, sei_tmp.lpParameters); if (retval > 32) /* Found */ { - WCHAR wszQuotedCmd[MAX_PATH+2]; - /* Must quote to handle case where cmd contains spaces, - * else security hole if malicious user creates executable file "C:\\Program" - */ - wcscpy(wszQuotedCmd, wQuote); - wcscat(wszQuotedCmd, wcmd); - wcscat(wszQuotedCmd, wQuote); - if (wszParameters[0]) { - wcscat(wszQuotedCmd, wSpace); - wcscat(wszQuotedCmd, wszParameters); - } - TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(sei_tmp.lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol)); - if (*lpstrProtocol) - retval = execute_from_key(lpstrProtocol, wszApplicationName, env, sei_tmp.lpParameters, wcmd, execfunc, &sei_tmp, sei); - else - retval = execfunc(wszQuotedCmd, env, FALSE, &sei_tmp, sei); + retval = SHELL_quote_and_execute( wcmd, wszParameters, lpstrProtocol, + wszApplicationName, env, &sei_tmp, + sei, execfunc ); HeapFree( GetProcessHeap(), 0, env ); } + else if (PathIsDirectoryW(lpFile)) + { + static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r',0}; + static const WCHAR wQuote[] = {'"',0}; + WCHAR wExec[MAX_PATH]; + WCHAR * lpQuotedFile = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * (strlenW(lpFile) + 3) ); + + if (lpQuotedFile) + { + retval = SHELL_FindExecutable( sei_tmp.lpDirectory, wExplorer, + wszOpen, wExec, MAX_PATH, + NULL, &env, NULL, NULL ); + if (retval > 32) + { + strcpyW(lpQuotedFile, wQuote); + strcatW(lpQuotedFile, lpFile); + strcatW(lpQuotedFile, wQuote); + retval = SHELL_quote_and_execute( wExec, lpQuotedFile, + lpstrProtocol, + wszApplicationName, env, + &sei_tmp, sei, execfunc ); + HeapFree( GetProcessHeap(), 0, env ); + } + HeapFree( GetProcessHeap(), 0, lpQuotedFile ); + } + else + retval = 0; /* Out of memory */ + } else if (PathIsURLW(lpFile)) /* File not found, check for URL */ { - static const WCHAR wShell[] = {'\\','s','h','e','l','l','\\',0}; - static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0}; - LPWSTR lpstrRes; - INT iSize; - - lpstrRes = wcschr(lpFile, ':'); - if (lpstrRes) - iSize = lpstrRes - lpFile; - else - iSize = wcslen(lpFile); - - TRACE("Got URL: %s\n", debugstr_w(lpFile)); - /* Looking for ...protocol\shell\lpOperation\command */ - memcpy(lpstrProtocol, lpFile, iSize*sizeof(WCHAR)); - lpstrProtocol[iSize] = '\0'; - wcscat(lpstrProtocol, wShell); - wcscat(lpstrProtocol, sei_tmp.lpVerb? sei_tmp.lpVerb: wszOpen); - wcscat(lpstrProtocol, wCommand); - - /* Remove File Protocol from lpFile */ - /* In the case file://path/file */ - if (!_wcsnicmp(lpFile, wFile, iSize)) - { - lpFile += iSize; - while (*lpFile == ':') lpFile++; - } - retval = execute_from_key(lpstrProtocol, lpFile, NULL, sei_tmp.lpParameters, wcmd, execfunc, &sei_tmp, sei); + retval = SHELL_execute_url( lpFile, wFile, wcmd, &sei_tmp, sei, execfunc ); } /* Check if file specified is in the form www.??????.*** */ - else if (!_wcsnicmp(lpFile, wWww, 3)) + else if (!strncmpiW(lpFile, wWww, 3)) { /* if so, append lpFile http:// and call ShellExecute */ WCHAR lpstrTmpFile[256]; - wcscpy(lpstrTmpFile, wHttp); - wcscat(lpstrTmpFile, lpFile); + strcpyW(lpstrTmpFile, wHttp); + strcatW(lpstrTmpFile, lpFile); retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); } TRACE("retval %lu\n", retval); + if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) + { + OPENASINFO Info; + + //FIXME + // need full path + + Info.pcszFile = wszApplicationName; + Info.pcszClass = NULL; + Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; + + if (SHOpenWithDialog(sei_tmp.hwnd, &Info)) + retval = 33; //FIXME + else + do_error_dialog(retval, sei_tmp.hwnd); + } + HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); + if (wcmd != wcmdBuffer) + HeapFree(GetProcessHeap(), 0, wcmd); sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval); + return retval > 32; } @@ -1673,7 +1859,7 @@ HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile, debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd); sei.cbSize = sizeof(sei); - sei.fMask = 0; + sei.fMask = SEE_MASK_FLAG_NO_UI; sei.hwnd = hWnd; sei.lpVerb = lpOperation; sei.lpFile = lpFile; @@ -1759,7 +1945,7 @@ HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, TRACE("\n"); sei.cbSize = sizeof(sei); - sei.fMask = 0; + sei.fMask = SEE_MASK_FLAG_NO_UI; sei.hwnd = hwnd; sei.lpVerb = lpOperation; sei.lpFile = lpFile; @@ -1776,6 +1962,42 @@ HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, return sei.hInstApp; } +/************************************************************************* + * WOWShellExecute [SHELL32.@] + * + * FIXME: the callback function most likely doesn't work the same way on Windows. + */ +HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile, + LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd, void *callback) +{ + SHELLEXECUTEINFOW seiW; + WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL; + HANDLE hProcess = 0; + + seiW.lpVerb = lpOperation ? __SHCloneStrAtoW(&wVerb, lpOperation) : NULL; + seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL; + seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL; + seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL; + + seiW.cbSize = sizeof(seiW); + seiW.fMask = 0; + seiW.hwnd = hWnd; + seiW.nShow = iShowCmd; + seiW.lpIDList = 0; + seiW.lpClass = 0; + seiW.hkeyClass = 0; + seiW.dwHotKey = 0; + seiW.hProcess = hProcess; + + SHELL_execute( &seiW, callback ); + + SHFree(wVerb); + SHFree(wFile); + SHFree(wParameters); + SHFree(wDirectory); + return seiW.hInstApp; +} + /************************************************************************* * OpenAs_RunDLLA [SHELL32.@] */ diff --git a/reactos/dll/win32/shell32/shresdef.h b/reactos/dll/win32/shell32/shresdef.h index 3ca4ac30945..9e4030be6cf 100644 --- a/reactos/dll/win32/shell32/shresdef.h +++ b/reactos/dll/win32/shell32/shresdef.h @@ -135,6 +135,8 @@ #define IDS_LNK_FILE 170 #define IDS_SYS_FILE 171 #define IDS_EMPTY_BITBUCKET 172 +#define IDS_SHLEXEC_NOASSOC 173 + #define IDS_OPEN_VERB 300 #define IDS_RUNAS_VERB 301 #define IDS_EDIT_VERB 302 @@ -165,6 +167,7 @@ #define IDS_FORMATDRIVE 327 #define IDS_RENAME 328 #define IDS_INSERT 329 +#define IDS_DESCRIPTION 330 /* Note: this string is referenced from the registry */ #define IDS_RECYCLEBIN_FOLDER_NAME 8964 diff --git a/reactos/dll/win32/shell32/stubs.c b/reactos/dll/win32/shell32/stubs.c index f9a77f7fccb..19d586c479d 100644 --- a/reactos/dll/win32/shell32/stubs.c +++ b/reactos/dll/win32/shell32/stubs.c @@ -411,23 +411,6 @@ RealDriveTypeFlags(INT iDrive, BOOL bUnknown) return 1; } -/* - * Unimplemented - */ -HINSTANCE -WINAPI -WOWShellExecute(HWND hwnd, - LPCWSTR lpOperation, - LPCWSTR lpFile, - LPCWSTR lpParameters, - LPCWSTR lpDirectory, - INT nShowCmd, - void *lpfnCBWinExec) -{ - FIXME("WOWShellExecute() stub\n"); - return NULL; -} - /* * Unimplemented */