diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 545d2cc26e..f2929aa44d 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCE PathResolve.cpp SHCreateFileExtractIconW.cpp SHParseDisplayName.cpp + ShellExecuteCmdLine.cpp ShellExecuteEx.cpp ShellState.cpp menu.cpp diff --git a/modules/rostests/apitests/shell32/ShellExecuteCmdLine.cpp b/modules/rostests/apitests/shell32/ShellExecuteCmdLine.cpp new file mode 100644 index 0000000000..e72846f251 --- /dev/null +++ b/modules/rostests/apitests/shell32/ShellExecuteCmdLine.cpp @@ -0,0 +1,262 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for ShellExecuteCmdLine + * PROGRAMMERS: Katayama Hirofumi MZ + */ +#include "shelltest.h" + +#define NDEBUG +#include +#include + +// HRESULT WINAPI ShellExecCmdLine( +// HWND hwnd, +// LPCWSTR pwszCommand, +// LPCWSTR pwszStartDir, +// int nShow, +// LPVOID pUnused, +// DWORD dwSeclFlags); +typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD); +SHELLEXECCMDLINE g_pShellExecCmdLine = NULL; + +typedef struct TEST_ENTRY +{ + INT lineno; + HRESULT hr; + 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[] = +{ + // notepad + { __LINE__, S_OK, L"Notepad", L"notepad", NULL }, + { __LINE__, S_OK, L"Notepad", L"notepad", L"." }, + { __LINE__, S_OK, L"Notepad", L"notepad", L"system32" }, + { __LINE__, S_OK, L"Notepad", L"notepad", L"C:\\Program Files" }, + { __LINE__, S_OK, L"Notepad", L"notepad \"Test File.txt\"", NULL}, + { __LINE__, S_OK, L"Notepad", L"notepad \"Test File.txt\"", L"." }, + // notepad.exe + { __LINE__, S_OK, L"Notepad", L"notepad.exe", NULL }, + { __LINE__, S_OK, L"Notepad", L"notepad.exe", L"." }, + { __LINE__, S_OK, L"Notepad", L"notepad.exe", L"system32" }, + { __LINE__, S_OK, L"Notepad", L"notepad.exe", L"C:\\Program Files" }, + { __LINE__, S_OK, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL}, + { __LINE__, S_OK, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." }, + // C:\notepad.exe + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." }, + // "notepad" + { __LINE__, S_OK, L"Notepad", L"\"notepad\"", NULL }, + { __LINE__, S_OK, L"Notepad", L"\"notepad\"", L"." }, + { __LINE__, S_OK, L"Notepad", L"\"notepad\"", L"system32" }, + { __LINE__, S_OK, L"Notepad", L"\"notepad\"", L"C:\\Program Files" }, + { __LINE__, S_OK, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL}, + { __LINE__, S_OK, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." }, + // "notepad.exe" + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\"", NULL }, + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\"", L"." }, + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\"", L"system32" }, + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" }, + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL}, + { __LINE__, S_OK, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." }, + // test program.exe + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"test program.exe \"Test File.txt\"", L"." }, + // "test program" + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\"", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\"", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\"", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\"", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." }, + // "test program.exe" + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\"", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\"", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\"", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." }, + // invalid program + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"invalid program \"Test File.txt\"", L"." }, + // \"invalid program.exe\" + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\"", NULL }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\"", L"." }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\"", L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" }, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL}, + { __LINE__, CO_E_APPNOTFOUND, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." }, +}; + +static void DoEntry(const TEST_ENTRY *pEntry) +{ + HRESULT hr; + + _SEH2_TRY + { + hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir, + SW_SHOWNORMAL, NULL, 0); + } + _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(ShellExecuteCmdLine) +{ + using namespace std; + + HMODULE hShell32 = GetModuleHandleA("shell32"); + g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265); + if (!g_pShellExecCmdLine) + { + skip("ShellExecCmdLine is not found\n"); + return; + } + + // 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, NULL, buf0, NULL }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf0, L"." }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf0, L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf1, NULL }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf1, L"." }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf1, L"system32" }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf2, NULL }, + { __LINE__, CO_E_APPNOTFOUND, NULL, buf2, L"." }, + { __LINE__, CO_E_APPNOTFOUND, NULL, 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]); + } + + 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(additionals); ++i) + { + DoEntry(&additionals[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 additionals2[] = + { + { __LINE__, S_OK, NULL, buf0, NULL }, + { __LINE__, S_OK, NULL, buf0, L"." }, + { __LINE__, S_OK, NULL, buf0, L"system32" }, + { __LINE__, S_OK, NULL, buf1, NULL }, + { __LINE__, S_OK, NULL, buf1, L"." }, + { __LINE__, S_OK, NULL, buf1, L"system32" }, + { __LINE__, S_OK, NULL, buf2, NULL }, + { __LINE__, S_OK, NULL, buf2, L"." }, + { __LINE__, S_OK, NULL, buf2, L"system32" }, + }; + for (size_t i = 0; i < _countof(additionals); ++i) + { + DoEntry(&additionals2[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"); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index b2e1b78876..6f0f9647df 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -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_ShellExecuteCmdLine(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 }, + { "ShellExecuteCmdLine", func_ShellExecuteCmdLine }, { "ShellExecuteEx", func_ShellExecuteEx }, { "ShellState", func_ShellState }, { "SHParseDisplayName", func_SHParseDisplayName },