Fix double-clicking control panel items
[reactos.git] / reactos / lib / shell32 / shlexec.c
index b265e7d..65c0d50 100644 (file)
 #include <ctype.h>
 #include <assert.h>
 
+#define COBJMACROS
+
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "winreg.h"
-#include "wownt32.h"
-#include "shellapi.h"
-#include "wingdi.h"
 #include "winuser.h"
-#include "shlobj.h"
 #include "shlwapi.h"
 #include "ddeml.h"
 
 #include "wine/winbase16.h"
 #include "shell32_main.h"
-#include "undocshell.h"
 #include "pidl.h"
 
 #include "wine/debug.h"
@@ -60,6 +57,8 @@ static const WCHAR wszShell[] = {'\\','s','h','e','l','l','\\',0};
 static const WCHAR wszFolder[] = {'F','o','l','d','e','r',0};
 static const WCHAR wszEmpty[] = {0};
 
+#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
+
 
 /***********************************************************************
  *     SHELL_ArgifyW [Internal]
@@ -77,22 +76,16 @@ static const WCHAR wszEmpty[] = {0};
  * %* all following parameters (see batfile)
  *
  * FIXME: use 'len'
- * FIXME: Careful of going over string-boundries. No checking is done to 'res'...
+ * FIXME: Careful of going over string boundaries. No checking is done to 'res'...
  */
 static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args)
 {
     WCHAR   xlpFile[1024];
     BOOL    done = FALSE;
+    BOOL    found_p1 = FALSE;
     PWSTR   res = out;
     PCWSTR  cmd;
     LPVOID  pv;
-    WCHAR   tmpBuffer[1024];
-    PWSTR   tmpB = tmpBuffer;
-    WCHAR   tmpEnvBuff[MAX_PATH];
-    WCHAR*  tmpE = tmpEnvBuff;
-    DWORD   envRet;
-    static const WCHAR wszSPerc[] = {'%','s',0};
-    static const WCHAR wszPerc[] = {'%',0};
 
     TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
           debugstr_w(lpFile), pidl, args);
@@ -147,8 +140,10 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp
                     else
                         cmd = lpFile;
 
-                    /* Add double quotation marks unless we already have them (e.g.: "file://%1" %* for exefile) */
-                    if (res == out || *(fmt + 1) != '"')
+                    /* Add double quotation marks unless we already have them
+                       (e.g.: "file://%1" %* for exefile) or unless the arg is already
+                       enclosed in double quotation marks */
+                    if ((res == out || *(fmt + 1) != '"') && *cmd != '"')
                     {
                         *res++ = '"';
                         strcpyW(res, cmd);
@@ -161,6 +156,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp
                         res += strlenW(cmd);
                     }
                 }
+                found_p1 = TRUE;
                 break;
 
             /*
@@ -174,6 +170,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp
                    strcpyW(res, lpFile);
                    res += strlenW(lpFile);
                }
+                found_p1 = TRUE;
                 break;
 
             case 'i':
@@ -184,40 +181,43 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp
                    res += sprintfW(res, wszILPtr, pv);
                    SHUnlockShared(pv);
                }
+                found_p1 = TRUE;
                 break;
 
-             default:
-           /*
-            * Check if this is a env-variable here...
-            */
-
-           /* Make sure that we have at least one more %.*/
-           if (strstrW(fmt, wszPerc))
-           {
-                       while (*fmt != '%')
-                               *tmpB++ = *fmt++;
-                       *tmpB++ = 0;
-
-                       TRACE("Checking %s to be a env-var\n", debugstr_w(tmpBuffer));
+           default:
+                /*
+                 * Check if this is an env-variable here...
+                 */
 
-                       envRet = GetEnvironmentVariableW(tmpBuffer, tmpE, MAX_PATH);
-                       if (envRet == 0 || envRet > MAX_PATH)
-                       {
-                               TRACE("The env. var can't be found or is bigger than MAX_PATH => useless.");
-                               res += sprintfW(res, wszSPerc, tmpBuffer);
-            }
-                       else
-                       {
-                               TRACE("Found it %s. Replacing... \n", debugstr_w(tmpEnvBuff));
-                               res += sprintfW(res, wszSPerc, tmpEnvBuff);
-                       }
-               }
+                /* Make sure that we have at least one more %.*/
+                if (strchrW(fmt, '%'))
+                {
+                    WCHAR   tmpBuffer[1024];
+                    PWSTR   tmpB = tmpBuffer;
+                    WCHAR   tmpEnvBuff[MAX_PATH];
+                    DWORD   envRet;
 
-           } /* switch */
+                    while (*fmt != '%')
+                        *tmpB++ = *fmt++;
+                    *tmpB++ = 0;
 
+                    TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
 
-            fmt++;
-            done = TRUE;
+                    envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
+                    if (envRet == 0 || envRet > MAX_PATH)
+                        strcpyW( res, tmpBuffer );
+                    else
+                        strcpyW( res, tmpEnvBuff );
+                    res += strlenW(res);
+                }
+                done = TRUE;
+                break;
+            }
+            /* Don't skip past terminator (catch a single '%' at the end) */
+            if (*fmt != '\0')
+            {
+                fmt++;
+            }
         }
         else
             *res++ = *fmt++;
@@ -225,7 +225,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp
 
     *res = '\0';
 
-    return done;
+    return found_p1;
 }
 
 HRESULT SHELL_GetPathFromIDListForExecuteA(LPCITEMIDLIST pidl, LPSTR pszPath, UINT uOutSize)
@@ -302,7 +302,7 @@ static HRESULT SHELL_ResolveShortCutW(LPWSTR wcmd, LPWSTR wargs, LPWSTR wdir, HW
 
                            if (SUCCEEDED(hr) && *ppidl) {
                                /* We got a PIDL instead of a file system path - try to translate it. */
-                               if (SUCCEEDED(SHELL_GetPathFromIDListW(*ppidl, wcmd, MAX_PATH))) {
+                               if (SHGetPathFromIDListW(*ppidl, wcmd)) {
                                    SHFree(*ppidl);
                                    *ppidl = NULL;
                                }
@@ -371,7 +371,7 @@ static UINT SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
     }
     else if ((retval = GetLastError()) >= 32)
     {
-        FIXME("Strange error set by CreateProcess: %d\n", retval);
+        TRACE("CreateProcess returned error %d\n", retval);
         retval = ERROR_BAD_FORMAT;
     }
 
@@ -749,7 +749,7 @@ UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation,
 /******************************************************************
  *             dde_cb
  *
- * callback for the DDE connection. not really usefull
+ * callback for the DDE connection. not really useful
  */
 static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
                                 HSZ hsz1, HSZ hsz2, HDDEDATA hData,
@@ -977,6 +977,11 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
     static const WCHAR wHttp[] = {'h','t','t','p',':','/','/',0};
     static const WCHAR wExtLnk[] = {'.','l','n','k',0};
     static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
+    static const DWORD unsupportedFlags =
+        SEE_MASK_INVOKEIDLIST  | SEE_MASK_ICON         | SEE_MASK_HOTKEY |
+        SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI |
+        SEE_MASK_UNICODE       | SEE_MASK_NO_CONSOLE   | SEE_MASK_ASYNCOK |
+        SEE_MASK_HMONITOR;
 
     WCHAR wszApplicationName[MAX_PATH+2], wszParameters[1024], wszDir[MAX_PATH];
     SHELLEXECUTEINFOW sei_tmp; /* modifiable copy of SHELLEXECUTEINFO struct */
@@ -997,15 +1002,25 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
             sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb),
             debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters),
             debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow,
-            (sei_tmp.fMask & SEE_MASK_CLASSNAME) ? debugstr_w(sei_tmp.lpClass) : "not used");
+            ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
+                debugstr_w(sei_tmp.lpClass) : "not used");
 
     sei->hProcess = NULL;
 
     /* make copies of all path/command strings */
-    if (sei_tmp.lpFile)
-       strcpyW(wszApplicationName, sei_tmp.lpFile);
+    if (!sei_tmp.lpFile)
+        *wszApplicationName = '\0';
+    else if (*sei_tmp.lpFile == '\"')
+    {
+        UINT l;
+        strcpyW(wszApplicationName, sei_tmp.lpFile+1);
+        l=lstrlenW(wszApplicationName);
+        if (wszApplicationName[l-1] == '\"')
+            wszApplicationName[l-1] = '\0';
+        TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName));
+    }
     else
-       *wszApplicationName = '\0';
+        strcpyW(wszApplicationName, sei_tmp.lpFile);
 
     if (sei_tmp.lpParameters)
        strcpyW(wszParameters, sei_tmp.lpParameters);
@@ -1022,12 +1037,9 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
     sei_tmp.lpParameters = wszParameters;
     sei_tmp.lpDirectory = wszDir;
 
-    if (sei_tmp.fMask & (SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY |
-        SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
-        SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE |
-        SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR ))
+    if (sei_tmp.fMask & unsupportedFlags)
     {
-        FIXME("flags ignored: 0x%08lx\n", sei_tmp.fMask);
+        FIXME("flags ignored: 0x%08lx\n", sei_tmp.fMask & unsupportedFlags);
     }
 
     /* process the IDList */
@@ -1053,13 +1065,14 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
         TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
     }
 
-    if (sei_tmp.fMask & (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY))
+    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! */
-        HCR_GetExecuteCommandW((sei_tmp.fMask & SEE_MASK_CLASSKEY) ? sei_tmp.hkeyClass : NULL,
-                               (sei_tmp.fMask & SEE_MASK_CLASSNAME) ? sei_tmp.lpClass: NULL,
+        ULONG cmask=(sei_tmp.fMask & SEE_MASK_CLASSALL);
+        HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? sei_tmp.hkeyClass : NULL,
+                               (cmask == SEE_MASK_CLASSNAME) ? sei_tmp.lpClass: NULL,
                                (sei_tmp.lpVerb) ? sei_tmp.lpVerb : wszOpen,
                                wszParameters, sizeof(wszParameters)/sizeof(WCHAR));
 
@@ -1224,7 +1237,7 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
            LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
            for(s=beg; (space=strchrW(s, ' ')); s=space+1) {
                int idx = space-sei_tmp.lpFile;
-               strncpyW(buffer, sei_tmp.lpFile, idx);
+               memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
                buffer[idx] = '\0';
 
                /*FIXME This finds directory paths if the targeted file name contains spaces. */
@@ -1250,9 +1263,10 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
 
     lpFile = wfileName;
 
+    strcpyW(wcmd, wszApplicationName);
     if (sei_tmp.lpParameters[0]) {
-        strcatW(wszApplicationName, wSpace);
-        strcatW(wszApplicationName, wszParameters);
+        strcatW(wcmd, wSpace);
+        strcatW(wcmd, wszParameters);
     }
 
     /* We set the default to open, and that should generally work.
@@ -1260,7 +1274,7 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
     if (!sei_tmp.lpVerb)
         sei_tmp.lpVerb = wszOpen;
 
-    retval = execfunc(wszApplicationName, NULL, FALSE, &sei_tmp, sei);
+    retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
     if (retval > 32)
         return TRUE;
 
@@ -1285,7 +1299,7 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
             retval = execute_from_key(lpstrProtocol, wszApplicationName, env, sei_tmp.lpParameters, execfunc, &sei_tmp, sei);
         else
             retval = execfunc(wszQuotedCmd, env, FALSE, &sei_tmp, sei);
-        if (env) HeapFree( GetProcessHeap(), 0, env );
+        HeapFree( GetProcessHeap(), 0, env );
     }
     else if (PathIsURLW((LPWSTR)lpFile))    /* File not found, check for URL */
     {
@@ -1302,7 +1316,7 @@ BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfun
 
         TRACE("Got URL: %s\n", debugstr_w(lpFile));
         /* Looking for ...protocol\shell\lpOperation\command */
-        strncpyW(lpstrProtocol, lpFile, iSize);
+        memcpy(lpstrProtocol, lpFile, iSize*sizeof(WCHAR));
         lpstrProtocol[iSize] = '\0';
         strcatW(lpstrProtocol, wShell);
         strcatW(lpstrProtocol, sei_tmp.lpVerb? sei_tmp.lpVerb: wszOpen);
@@ -1349,7 +1363,8 @@ HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
     HANDLE hProcess = 0;
 
     TRACE("%p,%s,%s,%s,%s,%d\n",
-           hWnd, lpOperation, lpFile, lpParameters, lpDirectory, iShowCmd);
+          hWnd, debugstr_a(lpOperation), debugstr_a(lpFile),
+          debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
 
     sei.cbSize = sizeof(sei);
     sei.fMask = 0;
@@ -1369,17 +1384,6 @@ HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
     return sei.hInstApp;
 }
 
-/*************************************************************************
- * ShellExecuteEx                              [SHELL32.291]
- *
- */
-BOOL WINAPI ShellExecuteExAW (LPVOID sei)
-{
-    if (SHELL_OsIsUnicode())
-       return ShellExecuteExW32 (sei, SHELL_ExecuteW);
-    return ShellExecuteExA (sei);
-}
-
 /*************************************************************************
  * ShellExecuteExA                             [SHELL32.292]
  *
@@ -1406,7 +1410,7 @@ BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
     if (sei->lpDirectory)
         seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
 
-    if ((sei->fMask & SEE_MASK_CLASSNAME) && sei->lpClass)
+    if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass)
         seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
     else
         seiW.lpClass = NULL;
@@ -1415,6 +1419,9 @@ BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
 
     sei->hInstApp = seiW.hInstApp;
 
+    if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
+        sei->hProcess = seiW.hProcess;
+
     if (wVerb) SHFree(wVerb);
     if (wFile) SHFree(wFile);
     if (wParameters) SHFree(wParameters);