Skip to content

Commit d663eb4

Browse files
authored
[SHELL32][CMD][SHLWAPI] Use the openas verb when invoking unknown file types (reactos#7981)
CORE-20184
1 parent 6dd8e04 commit d663eb4

File tree

11 files changed

+249
-170
lines changed

11 files changed

+249
-170
lines changed

base/shell/cmd/cmd.c

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,10 @@ typedef BOOL (WINAPI *MYEX)(LPSHELLEXECUTEINFO lpExecInfo);
266266
HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params,
267267
LPTSTR directory, INT show)
268268
{
269-
SHELLEXECUTEINFO sei;
269+
SHELLEXECUTEINFO sei = { sizeof(sei), flags | SEE_MASK_FLAG_DDEWAIT };
270270
HMODULE hShell32;
271271
MYEX hShExt;
272-
BOOL ret;
272+
UINT err;
273273

274274
TRACE ("RunFile(%s)\n", debugstr_aw(filename));
275275
hShell32 = LoadLibrary(_T("SHELL32.DLL"));
@@ -289,19 +289,17 @@ HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params,
289289

290290
TRACE ("RunFile: ShellExecuteExA/W is at %x\n", hShExt);
291291

292-
memset(&sei, 0, sizeof sei);
293-
sei.cbSize = sizeof sei;
294-
sei.fMask = flags;
295292
sei.lpFile = filename;
296293
sei.lpParameters = params;
297294
sei.lpDirectory = directory;
298295
sei.nShow = show;
299-
ret = hShExt(&sei);
300-
301-
TRACE ("RunFile: ShellExecuteExA/W returned 0x%p\n", ret);
296+
err = hShExt(&sei) ? ERROR_SUCCESS : GetLastError();
297+
TRACE ("RunFile: ShellExecuteExA/W returned error %#x\n", err);
302298

303299
FreeLibrary(hShell32);
304-
return ret ? sei.hProcess : NULL;
300+
301+
SetLastError(err);
302+
return err ? NULL : sei.hProcess;
305303
}
306304

307305

@@ -431,6 +429,7 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
431429
/* exec the program */
432430
PROCESS_INFORMATION prci;
433431
STARTUPINFO stui;
432+
UINT execerror = ERROR_FILE_NOT_FOUND;
434433

435434
/* build command line for CreateProcess(): FullName + " " + rest */
436435
BOOL quoted = !!_tcschr(First, _T(' '));
@@ -450,8 +449,8 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
450449
memset(&stui, 0, sizeof(stui));
451450
stui.cb = sizeof(stui);
452451
stui.lpTitle = Full;
453-
stui.dwFlags = STARTF_USESHOWWINDOW;
454-
stui.wShowWindow = SW_SHOWDEFAULT;
452+
stui.dwFlags = 0;
453+
stui.wShowWindow = SW_SHOWNORMAL;
455454

456455
/* Set the console to standard mode */
457456
SetConsoleMode(ConStreamGetOSHandle(StdIn),
@@ -470,14 +469,12 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
470469
{
471470
CloseHandle(prci.hThread);
472471
}
473-
else
472+
else if (GetLastError() == ERROR_BAD_EXE_FORMAT)
474473
{
475474
// See if we can run this with ShellExecute() ie myfile.xls
476-
prci.hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
477-
szFullName,
478-
rest,
479-
NULL,
480-
SW_SHOWNORMAL);
475+
HANDLE hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
476+
szFullName, rest, NULL, SW_SHOWNORMAL);
477+
execerror = hProcess ? ERROR_SUCCESS : GetLastError();
481478
}
482479

483480
*FirstEnd = _T('\0');
@@ -499,10 +496,13 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
499496
}
500497
CloseHandle(prci.hProcess);
501498
}
502-
else
499+
else if (execerror)
503500
{
504501
TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(Full));
505-
error_bad_command(first);
502+
if (execerror == ERROR_NO_ASSOCIATION)
503+
error_cant_exec_program();
504+
else
505+
error_bad_command(first);
506506
dwExitCode = 1;
507507
}
508508

base/shell/cmd/cmd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ VOID error_parameter_format(TCHAR ch);
216216
VOID error_invalid_switch(TCHAR ch);
217217
VOID error_invalid_parameter_format(PCTSTR s);
218218
VOID error_out_of_memory(VOID);
219+
VOID error_cant_exec_program(VOID);
219220
VOID error_syntax(PCTSTR s);
220221

221222
VOID msg_pause(VOID);

base/shell/cmd/error.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ VOID error_out_of_memory(VOID)
141141
nErrorLevel = 1;
142142
}
143143

144+
VOID error_cant_exec_program(VOID)
145+
{
146+
/* TODO: Windows uses the custom string "The system cannot execute the specified program" here */
147+
ErrorMessage(ERROR_NO_ASSOCIATION, NULL);
148+
nErrorLevel = 1;
149+
}
150+
144151

145152
VOID error_invalid_parameter_format(PCWSTR s)
146153
{

boot/bootdata/hivecls.inf

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ Signature = "$Windows NT$"
55
HKLM,"SOFTWARE\Classes",,0x00000010
66

77
; Default key
8-
HKCR,"*","",0x00000000,""
9-
HKCR,"SystemFileAssociations","",0x00000000,""
8+
HKCR,"*","AlwaysShowExt",0x00000000,""
9+
HKCR,"SystemFileAssociations",,0x00000010
1010

1111
; Open With List
1212
HKCR,"*\OpenWithList\excel.exe","",0x00000000,""
@@ -16,6 +16,12 @@ HKCR,"*\OpenWithList\notepad.exe","",0x00000000,""
1616
HKCR,"*\OpenWithList\winword.exe","",0x00000000,""
1717
HKCR,"*\OpenWithList\wordpad.exe","",0x00000000,""
1818

19+
; Unknown
20+
HKCR,"Unknown","AlwaysShowExt"
21+
HKCR,"Unknown","QueryClassStore"
22+
HKCR,"Unknown\shell","",,"openas"
23+
HKCR,"Unknown\shell\openas\command","",0x00020000,"%SystemRoot%\system32\rundll32.exe %SystemRoot%\system32\shell32.dll,OpenAs_RunDLL %1"
24+
1925
; Folders
2026
HKCR,"Folder","",0x00000000,"Folder"
2127
;HKCR,"Folder\DefaultIcon","",0x00000000,"%SystemRoot%\system32\shell32.dll,-4"

dll/win32/shell32/CDefaultContextMenu.cpp

Lines changed: 65 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ static const struct _StaticInvokeCommandMap_
5858
SHORT DfmCmd;
5959
} g_StaticInvokeCmdMap[] =
6060
{
61-
{ "RunAs", 0 }, // Unimplemented
62-
{ "Print", 0 }, // Unimplemented
63-
{ "Preview", 0 }, // Unimplemented
64-
{ "Open", FCIDM_SHVIEW_OPEN },
61+
{ "runas", 0 }, // Unimplemented
62+
{ "print", 0 }, // Unimplemented
63+
{ "preview", 0 }, // Unimplemented
64+
{ "open", FCIDM_SHVIEW_OPEN },
6565
{ CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER },
6666
{ "cut", FCIDM_SHVIEW_CUT, /* ? */ },
6767
{ "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY },
@@ -139,6 +139,36 @@ EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Ve
139139
return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
140140
}
141141

142+
static HRESULT GetFriendlyVerb(_In_ PCWSTR pszVerb, _Out_ PWSTR pszBuf, _In_ SIZE_T cchMax)
143+
{
144+
static const struct { PCWSTR pszVerb; WORD iResId; } map[] =
145+
{
146+
// { L"open", IDS_OPEN_VERB }, These two have already been handled
147+
// { L"explore", IDS_EXPLORE_VERB },
148+
{ L"edit", IDS_EDIT_VERB },
149+
{ L"print", IDS_PRINT_VERB },
150+
{ L"runas", IDS_RUNAS_VERB },
151+
{ L"openas", IDS_OPEN_VERB },
152+
{ L"find", IDS_FIND_VERB },
153+
};
154+
for (SIZE_T i = 0; i < _countof(map); ++i)
155+
{
156+
if (!_wcsicmp(pszVerb, map[i].pszVerb) &&
157+
LoadStringW(shell32_hInstance, map[i].iResId, pszBuf, cchMax))
158+
{
159+
return S_OK;
160+
}
161+
}
162+
163+
// Try to make a friendly verb based on the verb subkey
164+
if (pszVerb[0] < 127 && !StrChrW(pszVerb, '&') && SUCCEEDED(StringCchCopyW(pszBuf + 1, --cchMax, pszVerb)))
165+
{
166+
*pszBuf = L'&';
167+
return S_OK; // This can be changed to S_FALSE if the caller needs to know we faked it
168+
}
169+
return E_FAIL;
170+
}
171+
142172
class CDefaultContextMenu :
143173
public CComObjectRootEx<CComMultiThreadModelNoCS>,
144174
public IContextMenu3,
@@ -572,9 +602,8 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
572602
{
573603
UINT ntver = RosGetProcessEffectiveVersion();
574604
MENUITEMINFOW mii = { sizeof(mii) };
575-
UINT idResource;
576605
WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key.
577-
UINT fState;
606+
UINT fState, idVerbRes;
578607
UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault;
579608
int iDefVerbIndex = -1;
580609

@@ -587,6 +616,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
587616
{
588617
StaticShellEntry& info = m_StaticEntries.GetNext(it);
589618
BOOL forceFirstPos = FALSE;
619+
bool hide = false;
590620

591621
fState = MFS_ENABLED;
592622

@@ -599,86 +629,59 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
599629

600630
if (info.Verb.CompareNoCase(L"open") == 0)
601631
{
602-
idResource = IDS_OPEN_VERB;
632+
idVerbRes = IDS_OPEN_VERB; // TODO: This string should include '&'
603633
fState |= MFS_DEFAULT; /* override default when open verb is found */
604634
forceFirstPos++;
605635
}
606636
else if (info.Verb.CompareNoCase(L"explore") == 0)
607637
{
608-
idResource = IDS_EXPLORE_VERB;
638+
idVerbRes = IDS_EXPLORE_VERB; // TODO: This string should include '&'
609639
if (uFlags & CMF_EXPLORE)
610640
{
611641
fState |= MFS_DEFAULT;
612642
forceFirstPos++;
613643
}
614644
}
615-
else if (info.Verb.CompareNoCase(L"runas") == 0)
616-
idResource = IDS_RUNAS_VERB;
617-
else if (info.Verb.CompareNoCase(L"edit") == 0)
618-
idResource = IDS_EDIT_VERB;
619-
else if (info.Verb.CompareNoCase(L"find") == 0)
620-
idResource = IDS_FIND_VERB;
621-
else if (info.Verb.CompareNoCase(L"print") == 0)
622-
idResource = IDS_PRINT_VERB;
623645
else if (info.Verb.CompareNoCase(L"printto") == 0)
624-
continue;
646+
hide = true;
625647
else
626-
idResource = 0;
648+
idVerbRes = 0;
627649

628650
/* By default use verb for menu item name */
629651
mii.dwTypeData = (LPWSTR)info.Verb.GetString();
630652

631653
WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
632-
HRESULT hr;
633-
hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
654+
HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
634655
if (FAILED_UNEXPECTEDLY(hr))
635-
{
636-
continue;
637-
}
656+
hide = true;
638657

639658
UINT cmdFlags = 0;
640-
bool hide = false;
641659
HKEY hkVerb;
642-
if (idResource > 0)
660+
if (RegOpenKeyExW(info.hkClass, wszKey, 0, KEY_READ, &hkVerb) == ERROR_SUCCESS)
643661
{
644662
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
645663
{
646-
if (LoadStringW(shell32_hInstance, idResource, wszDispVerb, _countof(wszDispVerb)))
647-
mii.dwTypeData = wszDispVerb; /* use translated verb */
648-
else
649-
ERR("Failed to load string\n");
664+
DWORD cbVerb = sizeof(wszDispVerb);
665+
LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL);
666+
if (res || !*wszDispVerb)
667+
res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
668+
669+
if ((res == ERROR_SUCCESS && *wszDispVerb) ||
670+
(idVerbRes && LoadStringW(shell32_hInstance, idVerbRes, wszDispVerb, _countof(wszDispVerb))) ||
671+
SUCCEEDED(GetFriendlyVerb(info.Verb, wszDispVerb, _countof(wszDispVerb))))
672+
{
673+
mii.dwTypeData = wszDispVerb;
674+
}
650675
}
651-
652-
if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS)
653-
hkVerb = NULL;
654676
}
655677
else
656678
{
657-
if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS)
658-
{
659-
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
660-
{
661-
DWORD cbVerb = sizeof(wszDispVerb);
662-
LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL);
663-
if (res || !*wszDispVerb)
664-
res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
665-
666-
if (res == ERROR_SUCCESS && *wszDispVerb)
667-
{
668-
/* use description for the menu entry */
669-
mii.dwTypeData = wszDispVerb;
670-
}
671-
}
672-
}
673-
else
674-
{
675-
hkVerb = NULL;
676-
}
679+
hkVerb = NULL;
677680
}
678681

679682
if (hkVerb)
680683
{
681-
if (!(uFlags & CMF_EXTENDEDVERBS))
684+
if (!hide && !(uFlags & CMF_EXTENDEDVERBS))
682685
hide = RegValueExists(hkVerb, L"Extended");
683686

684687
if (!hide)
@@ -687,6 +690,9 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
687690
if (!hide && !(uFlags & CMF_DISABLEDVERBS))
688691
hide = RegValueExists(hkVerb, L"LegacyDisable");
689692

693+
if (DWORD dwRest = (hide ? 0 : RegGetDword(hkVerb, NULL, L"SuppressionPolicy", 0)))
694+
hide = SHRestricted((RESTRICTIONS)dwRest);
695+
690696
if (RegValueExists(hkVerb, L"NeverDefault"))
691697
fState &= ~MFS_DEFAULT;
692698

@@ -924,6 +930,11 @@ CDefaultContextMenu::QueryContextMenu(
924930

925931
// TODO: DFM_MERGECONTEXTMENU_TOP
926932

933+
// TODO: Remove duplicate verbs. This will be easier when the static items handling
934+
// has been moved to CLSID_ShellFileDefExt so we only have to deal with ShellEx.
935+
// This is a Windows XP+ feature. On an unknown file type, Windows 2000 will
936+
// display both "Open" (openas from Unknown) and "Open with..." (openas from *).
937+
927938
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
928939
}
929940

@@ -1278,8 +1289,6 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
12781289
LPCWSTR FlagsName;
12791290
WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
12801291
HRESULT hr;
1281-
DWORD wFlags;
1282-
DWORD cbVerb;
12831292

12841293
if (!m_site)
12851294
return 0;
@@ -1306,14 +1315,7 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
13061315
hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString());
13071316
if (FAILED_UNEXPECTEDLY(hr))
13081317
return 0;
1309-
1310-
cbVerb = sizeof(wFlags);
1311-
if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1312-
{
1313-
return wFlags;
1314-
}
1315-
1316-
return 0;
1318+
return RegGetDword(pEntry->hkClass, wszKey, FlagsName, 0);
13171319
}
13181320

13191321
HRESULT

0 commit comments

Comments
 (0)