[SHELL32] Fix handling of multiple parameters in the 'Run' dialog (#665)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Sun, 5 Aug 2018 11:39:17 +0000 (20:39 +0900)
committerHermès BÉLUSCA - MAÏTO <hermes.belusca-maito@reactos.org>
Sun, 5 Aug 2018 11:39:17 +0000 (13:39 +0200)
The "Run" dialog failed when multiple parameters wee specified.
We use instead a newly-created ShellExecCmdLine() helper function to fix the problem (as found via API-tracing on Windows).
Note that ShellExecCmdLine() starts to be exported with Vista+.

- Implement ShellExecCmdLine() function in shell32.
- Add URL support.
- Fix RunDlgProc function in shell32.
- Add a testcase for ShellExecCmdLine() function.

CORE-14790

dll/win32/shell32/dialogs/dialogs.cpp
dll/win32/shell32/shlexec.cpp
modules/rostests/apitests/shell32/CMakeLists.txt
modules/rostests/apitests/shell32/ShellExecCmdLine.cpp [new file with mode: 0644]
modules/rostests/apitests/shell32/testlist.c
sdk/include/reactos/undocshell.h

index 168b066..77c1bdf 100644 (file)
@@ -527,7 +527,6 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                     HWND htxt = GetDlgItem(hwnd, IDC_RUNDLG_EDITPATH);
                     INT ic;
                     WCHAR *psz, *parent = NULL;
-                    SHELLEXECUTEINFOW sei;
                     NMRUNFILEDLGW nmrfd;
 
                     ic = GetWindowTextLengthW(htxt);
@@ -537,9 +536,6 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                         return TRUE;
                     }
 
-                    ZeroMemory(&sei, sizeof(sei));
-                    sei.cbSize = sizeof(sei);
-
                     /*
                      * Allocate a new MRU entry, we need to add two characters
                      * for the terminating "\\1" part, then the NULL character.
@@ -552,10 +548,7 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                     }
 
                     GetWindowTextW(htxt, psz, ic + 1);
-
-                    sei.hwnd = hwnd;
-                    sei.nShow = SW_SHOWNORMAL;
-                    sei.lpFile = psz;
+                    StrTrimW(psz, L" \t");
 
                     /*
                      * The precedence is the following: first the user-given
@@ -563,12 +556,13 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                      * directory is computed if the RFF_CALCDIRECTORY is set,
                      * otherwise no current directory is defined.
                      */
+                    LPCWSTR pszStartDir;
                     if (prfdp->lpstrDirectory)
-                        sei.lpDirectory = prfdp->lpstrDirectory;
+                        pszStartDir = prfdp->lpstrDirectory;
                     else if (prfdp->uFlags & RFF_CALCDIRECTORY)
-                        sei.lpDirectory = parent = RunDlg_GetParentDir(sei.lpFile);
+                        pszStartDir = parent = RunDlg_GetParentDir(psz);
                     else
-                        sei.lpDirectory = NULL;
+                        pszStartDir = NULL;
 
                     /* Hide the dialog for now on, we will show it up in case of retry */
                     ShowWindow(hwnd, SW_HIDE);
@@ -585,9 +579,9 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                     nmrfd.hdr.code = RFN_VALIDATE;
                     nmrfd.hdr.hwndFrom = hwnd;
                     nmrfd.hdr.idFrom = 0;
-                    nmrfd.lpFile = sei.lpFile;
-                    nmrfd.lpDirectory = sei.lpDirectory;
-                    nmrfd.nShow = sei.nShow;
+                    nmrfd.lpFile = psz;
+                    nmrfd.lpDirectory = pszStartDir;
+                    nmrfd.nShow = SW_SHOWNORMAL;
 
                     lRet = SendMessageW(prfdp->hwndOwner, WM_NOTIFY, 0, (LPARAM)&nmrfd.hdr);
 
@@ -598,7 +592,8 @@ static INT_PTR CALLBACK RunDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
                             break;
 
                         case RF_OK:
-                            if (ShellExecuteExW(&sei))
+                            if (SUCCEEDED(ShellExecCmdLine(hwnd, psz, pszStartDir, SW_SHOWNORMAL, NULL,
+                                                           SECL_ALLOW_NONEXE)))
                             {
                                 /* Call again GetWindowText in case the contents of the edit box has changed? */
                                 GetWindowTextW(htxt, psz, ic + 1);
index 478a3c2..55b3d05 100644 (file)
  */
 
 #include "precomp.h"
+#include <undocshell.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(exec);
 
 static const WCHAR wszOpen[] = L"open";
 static const WCHAR wszExe[] = L".exe";
+static const WCHAR wszCom[] = L".com";
 
 #define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
 
@@ -2322,3 +2324,181 @@ OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow)
     OpenAs_RunDLLW(hwnd, hinst, pszCmdLineW, cmdshow);
     SHFree(pszCmdLineW);
 }
+
+/*************************************************************************/
+
+static LPCWSTR
+SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0)
+{
+    LPCWSTR pch;
+    size_t ich = 0;
+    if (*psz == L'"')
+    {
+        // 1st argument is quoted. the string in quotes is quoted 1st argument.
+        // [pch] --> [pszArg0+ich]
+        for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch)
+        {
+            if (*pch == L'"' && pch[1] == L'"')
+            {
+                // doubled double quotations found!
+                pszArg0[ich] = L'"';
+            }
+            else if (*pch == L'"')
+            {
+                // single double quotation found!
+                ++pch;
+                break;
+            }
+            else
+            {
+                // otherwise
+                pszArg0[ich] = *pch;
+            }
+        }
+    }
+    else
+    {
+        // 1st argument is unquoted. non-space sequence is 1st argument.
+        // [pch] --> [pszArg0+ich]
+        for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch)
+        {
+            pszArg0[ich] = *pch;
+        }
+    }
+    pszArg0[ich] = 0;
+
+    // skip space
+    while (iswspace(*pch))
+        ++pch;
+
+    return pch;
+}
+
+HRESULT WINAPI ShellExecCmdLine(
+    HWND hwnd,
+    LPCWSTR pwszCommand,
+    LPCWSTR pwszStartDir,
+    int nShow,
+    LPVOID pUnused,
+    DWORD dwSeclFlags)
+{
+    SHELLEXECUTEINFOW info;
+    DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC;
+    LPCWSTR pszVerb = NULL;
+    WCHAR szFile[MAX_PATH], szFile2[MAX_PATH];
+    HRESULT hr;
+    LPCWSTR pchParams;
+    LPWSTR lpCommand = NULL;
+
+    if (pwszCommand == NULL)
+        RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE,
+                       1, (ULONG_PTR*)pwszCommand);
+
+    __SHCloneStrW(&lpCommand, pwszCommand);
+    StrTrimW(lpCommand, L" \t");
+
+    if (dwSeclFlags & SECL_NO_UI)
+        dwFlags |= SEE_MASK_FLAG_NO_UI;
+    if (dwSeclFlags & SECL_LOG_USAGE)
+        dwFlags |= SEE_MASK_FLAG_LOG_USAGE;
+    if (dwSeclFlags & SECL_USE_IDLIST)
+        dwFlags |= SEE_MASK_INVOKEIDLIST;
+
+    if (dwSeclFlags & SECL_RUNAS)
+    {
+        dwSize = 0;
+        hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize);
+        if (SUCCEEDED(hr) && dwSize != 0)
+        {
+            pszVerb = L"runas";
+        }
+    }
+
+    if (UrlIsFileUrlW(lpCommand))
+    {
+        StringCchCopyW(szFile, _countof(szFile), lpCommand);
+        pchParams = NULL;
+    }
+    else
+    {
+        pchParams = SplitParams(lpCommand, szFile, _countof(szFile));
+        if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL))
+        {
+            StringCchCopyW(szFile, _countof(szFile), szFile2);
+            pchParams = NULL;
+        }
+        else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL))
+        {
+            StringCchCopyW(szFile, _countof(szFile), szFile2);
+            pchParams = NULL;
+        }
+
+        if (!(dwSeclFlags & SECL_ALLOW_NONEXE))
+        {
+            if (!GetBinaryTypeW(szFile, &dwType))
+            {
+                SHFree(lpCommand);
+
+                if (!(dwSeclFlags & SECL_NO_UI))
+                {
+                    WCHAR szText[128 + MAX_PATH], szFormat[128];
+                    LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
+                    StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
+                    MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
+                }
+                return CO_E_APPNOTFOUND;
+            }
+        }
+        else
+        {
+            if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
+            {
+                SHFree(lpCommand);
+
+                if (!(dwSeclFlags & SECL_NO_UI))
+                {
+                    WCHAR szText[128 + MAX_PATH], szFormat[128];
+                    LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
+                    StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
+                    MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
+                }
+                return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+            }
+        }
+    }
+
+    ZeroMemory(&info, sizeof(info));
+    info.cbSize = sizeof(info);
+    info.fMask = dwFlags;
+    info.hwnd = hwnd;
+    info.lpVerb = pszVerb;
+    info.lpFile = szFile;
+    info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL;
+    info.lpDirectory = pwszStartDir;
+    info.nShow = nShow;
+    if (ShellExecuteExW(&info))
+    {
+        if (info.lpIDList)
+            CoTaskMemFree(info.lpIDList);
+
+        SHFree(lpCommand);
+
+        return S_OK;
+    }
+
+    dwError = GetLastError();
+
+    SHFree(lpCommand);
+
+    return HRESULT_FROM_WIN32(dwError);
+}
index 545d2cc..8201a6f 100644 (file)
@@ -18,6 +18,7 @@ list(APPEND SOURCE
     PathResolve.cpp
     SHCreateFileExtractIconW.cpp
     SHParseDisplayName.cpp
+    ShellExecCmdLine.cpp
     ShellExecuteEx.cpp
     ShellState.cpp
     menu.cpp
diff --git a/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp b/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp
new file mode 100644 (file)
index 0000000..c540321
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * PROJECT:         ReactOS API tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Test for ShellExecCmdLine
+ * PROGRAMMERS:     Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
+ */
+#include "shelltest.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define NDEBUG
+#include <debug.h>
+#include <stdio.h>
+
+//#define ShellExecCmdLine ShellExecCmdLine
+
+#ifndef SECL_NO_UI
+    #define SECL_NO_UI          0x2
+    #define SECL_LOG_USAGE      0x8
+    #define SECL_USE_IDLIST     0x10
+    #define SECL_ALLOW_NONEXE   0x20
+    #define SECL_RUNAS          0x40
+#endif
+
+#ifdef ShellExecCmdLine
+
+#define shell32_hInstance   GetModuleHandle(NULL)
+#define IDS_FILE_NOT_FOUND  (-1)
+
+static const WCHAR wszOpen[] = L"open";
+static const WCHAR wszExe[] = L".exe";
+static const WCHAR wszCom[] = L".com";
+
+static __inline void __SHCloneStrW(WCHAR **target, const WCHAR *source)
+{
+       *target = (WCHAR *)SHAlloc((lstrlenW(source) + 1) * sizeof(WCHAR) );
+       lstrcpyW(*target, source);
+}
+
+static LPCWSTR
+SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0)
+{
+    LPCWSTR pch;
+    size_t ich = 0;
+    if (*psz == L'"')
+    {
+        // 1st argument is quoted. the string in quotes is quoted 1st argument.
+        // [pch] --> [pszArg0+ich]
+        for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch)
+        {
+            if (*pch == L'"' && pch[1] == L'"')
+            {
+                // doubled double quotations found!
+                pszArg0[ich] = L'"';
+            }
+            else if (*pch == L'"')
+            {
+                // single double quotation found!
+                ++pch;
+                break;
+            }
+            else
+            {
+                // otherwise
+                pszArg0[ich] = *pch;
+            }
+        }
+    }
+    else
+    {
+        // 1st argument is unquoted. non-space sequence is 1st argument.
+        // [pch] --> [pszArg0+ich]
+        for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch)
+        {
+            pszArg0[ich] = *pch;
+        }
+    }
+    pszArg0[ich] = 0;
+
+    // skip space
+    while (iswspace(*pch))
+        ++pch;
+
+    return pch;
+}
+
+HRESULT WINAPI ShellExecCmdLine(
+    HWND hwnd,
+    LPCWSTR pwszCommand,
+    LPCWSTR pwszStartDir,
+    int nShow,
+    LPVOID pUnused,
+    DWORD dwSeclFlags)
+{
+    SHELLEXECUTEINFOW info;
+    DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC;
+    LPCWSTR pszVerb = NULL;
+    WCHAR szFile[MAX_PATH], szFile2[MAX_PATH];
+    HRESULT hr;
+    LPCWSTR pchParams;
+    LPWSTR lpCommand = NULL;
+
+    if (pwszCommand == NULL)
+        RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE,
+                       1, (ULONG_PTR*)pwszCommand);
+
+    __SHCloneStrW(&lpCommand, pwszCommand);
+    StrTrimW(lpCommand, L" \t");
+
+    if (dwSeclFlags & SECL_NO_UI)
+        dwFlags |= SEE_MASK_FLAG_NO_UI;
+    if (dwSeclFlags & SECL_LOG_USAGE)
+        dwFlags |= SEE_MASK_FLAG_LOG_USAGE;
+    if (dwSeclFlags & SECL_USE_IDLIST)
+        dwFlags |= SEE_MASK_INVOKEIDLIST;
+
+    if (dwSeclFlags & SECL_RUNAS)
+    {
+        dwSize = 0;
+        hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize);
+        if (SUCCEEDED(hr) && dwSize != 0)
+        {
+            pszVerb = L"runas";
+        }
+    }
+
+    if (UrlIsFileUrlW(lpCommand))
+    {
+        StringCchCopyW(szFile, _countof(szFile), lpCommand);
+        pchParams = NULL;
+    }
+    else
+    {
+        pchParams = SplitParams(lpCommand, szFile, _countof(szFile));
+        if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
+            SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL))
+        {
+            StringCchCopyW(szFile, _countof(szFile), szFile2);
+            pchParams = NULL;
+        }
+        else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
+                 SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL))
+        {
+            StringCchCopyW(szFile, _countof(szFile), szFile2);
+            pchParams = NULL;
+        }
+
+        if (!(dwSeclFlags & SECL_ALLOW_NONEXE))
+        {
+            if (!GetBinaryTypeW(szFile, &dwType))
+            {
+                SHFree(lpCommand);
+
+                if (!(dwSeclFlags & SECL_NO_UI))
+                {
+                    WCHAR szText[128 + MAX_PATH], szFormat[128];
+                    LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
+                    StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
+                    MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
+                }
+                return CO_E_APPNOTFOUND;
+            }
+        }
+        else
+        {
+            if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
+            {
+                SHFree(lpCommand);
+
+                if (!(dwSeclFlags & SECL_NO_UI))
+                {
+                    WCHAR szText[128 + MAX_PATH], szFormat[128];
+                    LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
+                    StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
+                    MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
+                }
+                return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+            }
+        }
+    }
+
+    ZeroMemory(&info, sizeof(info));
+    info.cbSize = sizeof(info);
+    info.fMask = dwFlags;
+    info.hwnd = hwnd;
+    info.lpVerb = pszVerb;
+    info.lpFile = szFile;
+    info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL;
+    info.lpDirectory = pwszStartDir;
+    info.nShow = nShow;
+    if (ShellExecuteExW(&info))
+    {
+        if (info.lpIDList)
+            CoTaskMemFree(info.lpIDList);
+
+        SHFree(lpCommand);
+
+        return S_OK;
+    }
+
+    dwError = GetLastError();
+
+    SHFree(lpCommand);
+
+    return HRESULT_FROM_WIN32(dwError);
+}
+#else
+    typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD);
+    SHELLEXECCMDLINE g_pShellExecCmdLine = NULL;
+#endif
+
+typedef struct TEST_ENTRY
+{
+    INT lineno;
+    HRESULT hr;
+    BOOL bAllowNonExe;
+    LPCWSTR pwszWindowClass;
+    LPCWSTR pwszCommand;
+    LPCWSTR pwszStartDir;
+} TEST_ENTRY;
+
+static const char s_testfile1[] = "Test File.txt";
+static const char s_testfile2[] = "Test File.bat";
+static char s_notepad[] = "notepad.exe";
+
+static const TEST_ENTRY s_entries[] =
+{
+    // NULL
+    { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, NULL },
+    { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"." },
+    { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"system32" },
+    { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"C:\\Program Files" },
+    { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, NULL },
+    { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"." },
+    { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"system32" },
+    { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"C:\\Program Files" },
+    // notepad
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"." },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"system32" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"C:\\Program Files" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"system32" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"C:\\Program Files" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
+    // notepad.exe
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"." },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"system32" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"system32" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
+    // C:\notepad.exe
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
+    // "notepad"
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"." },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"system32" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"system32" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
+    // "notepad.exe"
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"." },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"system32" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"." },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"system32" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
+    // test program.exe
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
+    // "test program"
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
+    // "test program.exe"
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
+    // invalid program
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
+    // \"invalid program.exe\"
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"." },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"." },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
+    { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
+};
+
+static void DoEntry(const TEST_ENTRY *pEntry)
+{
+    HRESULT hr;
+    DWORD dwSeclFlags;
+
+    if (pEntry->bAllowNonExe)
+        dwSeclFlags = SECL_NO_UI | SECL_ALLOW_NONEXE;
+    else
+        dwSeclFlags = SECL_NO_UI;
+
+    _SEH2_TRY
+    {
+#ifdef ShellExecCmdLine
+        hr = ShellExecCmdLine(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
+                              SW_SHOWNORMAL, NULL, dwSeclFlags);
+#else
+        hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
+                                    SW_SHOWNORMAL, NULL, dwSeclFlags);
+#endif
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        hr = 0xDEADFACE;
+    }
+    _SEH2_END;
+
+    ok(hr == pEntry->hr, "Line %d: hr expected 0x%lX, was 0x%lX\n", pEntry->lineno, pEntry->hr, hr);
+
+#define RETRY_COUNT     5
+#define RETRY_INTERVAL  250
+    if (SUCCEEDED(hr) && pEntry->pwszWindowClass)
+    {
+        BOOL bFound = FALSE;
+        Sleep(RETRY_INTERVAL / 2);
+        for (int i = 0; i < RETRY_COUNT; ++i)
+        {
+            HWND hwnd = FindWindowW(pEntry->pwszWindowClass, NULL);
+            if (hwnd)
+            {
+                bFound = TRUE;
+                SendMessage(hwnd, WM_CLOSE, 0, 0);
+                break;
+            }
+            Sleep(RETRY_INTERVAL);
+        }
+        ok(bFound, "Line %d: The window not found\n", pEntry->lineno);
+    }
+#undef RETRY_COUNT
+#undef RETRY_INTERVAL
+}
+
+START_TEST(ShellExecCmdLine)
+{
+    using namespace std;
+
+#ifndef ShellExecCmdLine
+    HMODULE hShell32 = GetModuleHandleA("shell32");
+    g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265);
+    if (!g_pShellExecCmdLine)
+    {
+        skip("ShellExecCmdLine is not found\n");
+        return;
+    }
+#endif
+
+    // s_testfile1
+    FILE *fp = fopen(s_testfile1, "wb");
+    ok(fp != NULL, "failed to create a test file\n");
+    fclose(fp);
+
+    // s_testfile2
+    fp = fopen(s_testfile2, "wb");
+    ok(fp != NULL, "failed to create a test file\n");
+    if (fp)
+    {
+        fprintf(fp, "echo OK\n");
+    }
+    fclose(fp);
+
+    for (size_t i = 0; i < _countof(s_entries); ++i)
+    {
+        DoEntry(&s_entries[i]);
+    }
+
+    WCHAR buf0[MAX_PATH];
+    WCHAR buf1[MAX_PATH];
+    WCHAR buf2[MAX_PATH];
+    TEST_ENTRY additionals[] =
+    {
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"system32" },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"system32" },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"system32" },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, L"Notepad", buf0, L"." },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, L"system32" }, // FIXME
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf1, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"." },
+        { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"system32" },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf2, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"." },
+        { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"system32" },
+    };
+
+    wsprintfW(buf0, L"%hs", s_testfile1);
+    wsprintfW(buf1, L"\"%hs\"", s_testfile1);
+    wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile1);
+    for (size_t i = 0; i < _countof(additionals); ++i)
+    {
+        DoEntry(&additionals[i]);
+    }
+
+    TEST_ENTRY additionals2[] =
+    {
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"system32" },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"system32" },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, NULL },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"." },
+        { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"system32" },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, L"system32" },  // FIXME
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf1, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
+        { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
+        { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf2, NULL }, // FIXME
+        { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
+        { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
+    };
+
+    wsprintfW(buf0, L"%hs", s_testfile2);
+    wsprintfW(buf1, L"\"%hs\"", s_testfile2);
+    wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile2);
+    for (size_t i = 0; i < _countof(additionals2); ++i)
+    {
+        DoEntry(&additionals2[i]);
+    }
+
+    char path[MAX_PATH];
+    ok((INT_PTR)FindExecutableA("notepad.exe", NULL, s_notepad) >= 32, "FindExecutableA failed\n");
+    ok(GetModuleFileNameA(NULL, path, _countof(path)), "GetModuleFileNameA failed\n");
+    char *pch = strrchr(path, '\\');
+
+    if (pch == NULL)
+    {
+        skip("pch == NULL\n");
+    }
+    else
+    {
+        // create "My Directory"
+        strcpy(pch, "\\My Directory");
+        if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES)
+            ok(CreateDirectoryA(path, NULL), "CreateDirectoryA failed\n");
+
+        // create "My Directory\\Notepad.exe" as clone of Notepad.exe
+        strcpy(pch, "\\My Directory\\Notepad.exe");
+        ok(CopyFileA(s_notepad, path, FALSE), "CopyFileA failed\n");
+
+        wsprintfW(buf0, L"%hs", path);
+        wsprintfW(buf1, L"\"%hs\"", path);
+        wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", path);
+        TEST_ENTRY additionals3[] =
+        {
+            { __LINE__, S_OK, FALSE, NULL, buf0, NULL },
+            { __LINE__, S_OK, FALSE, NULL, buf0, L"." },
+            { __LINE__, S_OK, FALSE, NULL, buf0, L"system32" },
+            { __LINE__, S_OK, FALSE, NULL, buf1, NULL },
+            { __LINE__, S_OK, FALSE, NULL, buf1, L"." },
+            { __LINE__, S_OK, FALSE, NULL, buf1, L"system32" },
+            { __LINE__, S_OK, FALSE, NULL, buf2, NULL },
+            { __LINE__, S_OK, FALSE, NULL, buf2, L"." },
+            { __LINE__, S_OK, FALSE, NULL, buf2, L"system32" },
+            { __LINE__, S_OK, TRUE, NULL, buf0, NULL },
+            { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
+            { __LINE__, S_OK, TRUE, NULL, buf0, L"system32" },
+            { __LINE__, S_OK, TRUE, NULL, buf1, NULL },
+            { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
+            { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
+            { __LINE__, S_OK, TRUE, NULL, buf2, NULL },
+            { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
+            { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
+        };
+        for (size_t i = 0; i < _countof(additionals3); ++i)
+        {
+            DoEntry(&additionals3[i]);
+        }
+
+        DeleteFileA(path);
+
+        strcpy(pch, "\\My Directory");
+        RemoveDirectory(path);
+    }
+
+    // clean up
+    ok(DeleteFileA(s_testfile1), "failed to delete the test file\n");
+    ok(DeleteFileA(s_testfile2), "failed to delete the test file\n");
+}
index b2e1b78..1b5339e 100644 (file)
@@ -15,6 +15,7 @@ extern void func_menu(void);
 extern void func_OpenAs_RunDLL(void);
 extern void func_PathResolve(void);
 extern void func_SHCreateFileExtractIconW(void);
+extern void func_ShellExecCmdLine(void);
 extern void func_ShellExecuteEx(void);
 extern void func_ShellState(void);
 extern void func_SHParseDisplayName(void);
@@ -33,6 +34,7 @@ const struct test winetest_testlist[] =
     { "OpenAs_RunDLL", func_OpenAs_RunDLL },
     { "PathResolve", func_PathResolve },
     { "SHCreateFileExtractIconW", func_SHCreateFileExtractIconW },
+    { "ShellExecCmdLine", func_ShellExecCmdLine },
     { "ShellExecuteEx", func_ShellExecuteEx },
     { "ShellState", func_ShellState },
     { "SHParseDisplayName", func_SHParseDisplayName },
index 24d6e82..c2a0b37 100644 (file)
@@ -546,6 +546,21 @@ WORD WINAPI ArrangeWindows(
     WORD cKids,
     CONST HWND * lpKids);
 
+/* Flags for ShellExecCmdLine */
+#define SECL_NO_UI          0x2
+#define SECL_LOG_USAGE      0x8
+#define SECL_USE_IDLIST     0x10
+#define SECL_ALLOW_NONEXE   0x20
+#define SECL_RUNAS          0x40
+
+HRESULT WINAPI ShellExecCmdLine(
+    HWND hwnd,
+    LPCWSTR pwszCommand,
+    LPCWSTR pwszStartDir,
+    int nShow,
+    LPVOID pUnused,
+    DWORD dwSeclFlags);
+
 /* RegisterShellHook types */
 #define RSH_DEREGISTER        0
 #define RSH_REGISTER          1