I have a 64-bit console app that just wants to open a file. File's hidden attribute is set (the file is hidden). The code below fails only on some machines. ShellExecuteEx would actually return TRUE, but the .txt file would not open in Notepad, and hProcess member of SHELLEXECUTEINFO structure remains zero after the call.
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.hWnd = GetConsoleWindow();
sei.lpVerb = _T("open");
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = _T("C:\\some\\existing\\file.txt");
BOOL bRC = ShellExecuteEx(&sei);
MessageBox(GetConsoleWindow(), sei.lpFile, sei.lpVerb, MB_OK);
Call to MessageBox is there just so that there's enough time for ShellExecuteEx to do its magic. The .txt file will open in Notepad if at least one of the following conditions is met:
.txt file is not hidden
the calling process is 32-bit, instead of 64-bit
the machine is some other (can't figure out what is the difference between machines, but the file is not open in at least one Vista and one Windows 8.1)
lpVerb is nullptr, "openas" or "properties" (which will of course just show file's properties), instead of "open", "edit", or even "runas" (for .exe instead of .txt)
What is going on here? Windows Explorer properly opens the hidden file, because it uses null verb, but I have to use the verb ("runas" actually, but please don't be distracted by this info). It should work with "open" as well. What am I doing wrong?
Turns out the problem was in one Explorer extension. Ironically (but unsurprisingly) it was our own extension. If the extension is unregistered the problem is gone, and ShellExecute works again for all verbs.
Related
On Windows 7, I developed a shell namespace extension (NSE) that presents a hierarchy of data that is very similar to a file system. Its junction point is the Desktop. I am able to click the [+] of each node in the tree control of Windows Explorer to expand that node. When I click on a folder in the tree view, I see a list of "folders" and "files", as expected, in the view presented by DefView. If I double-click on a "file", DefView dutifully invokes my corresponding IShellFolder::CreateViewObject to ask for IContextMenu, then eventually invokes IContextMenu::InvokeCommand, at which point I execute ShellExecuteEx with lpClass set to the extension of the "file". Explorer then dutifully launches the appropriate EXE application according to the "file" extension with my weird "file" path supplied as a command-line argument. However, if I double-click on one of my "folders" in the DefView, Explorer refuses to browse into the folder. An analysis of the call stack reveals that Explorer is trying to do some type of zone-checking on my weird "file" paths. I tried fiddling with Control Panel->Internet Options->Security zones, by adding my weird "file" paths to various zones, but that did not work. I can see from the call stack that there are two zone checks: a primary and a secondary. I guess I could brute-force my way around this, but I would like to know the sanctioned way to get past these zone-checks, or, rather, to get Explorer to browse into my "folders". Strangely, the IFileDialog does not have this problem, and browses into my "folders" without hesitation. I also tried adding the following code to my IShellFolderView::MessageSFVCB:
switch (uMsg)
{
case SFVM_GETZONE:
{
DWORD zone = URLZONE_TRUSTED;
* ((DWORD *)lParam) = zone;
}
return S_OK;
...
That did not help.
I also have a strong feeling my drag-and-drop will be blocked for the same underlying reason.
Any ideas?
Here is (partial) call stack:
MyDLL.dll!MyShellFolder::GetAttributesOf(unsigned int uCount, const _ITEMIDLIST * * aPidls, unsigned long * pdwAttributes) Line 385 C++
shell32.dll!CShellItem::GetAttributes(unsigned long,unsigned long *) Unknown
shell32.dll!CDefView::_SelectionHasFolderJunction(void) Unknown
shell32.dll!CDefView::_ZoneCheckFrame(unsigned long,int) Unknown
shell32.dll!CDefView::_InvokeContextMenuVerbOnSelectionWorker(char const *,unsigned int) Unknown
shell32.dll!CDefView::_InvokeContextMenuVerbOnSelection(char const *,unsigned int) Unknown
shell32.dll!CDefView::OnActivateSelection(unsigned long) Unknown
ExplorerFrame.dll!UIItemsView::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64) Unknown
Like often, I post here after several hours of research and tries without any success
I have this old dll written in C. For the moment, it has no interface but I need to add a dialog box to it.
I work with VS2017 and tried the following :
Using VS2017 ressource editor, I added a dialog box (id : IDD_DIALOG_REPLAY, automatically defined to 101 in resource.h file by resource editor) and added the following code to create my dialog box :
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
InitCtrls.dwICC = ICC_LINK_CLASS | ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
HWND hDialog = 0;
hDialog = CreateDialog(pSGL->hInstance,MAKEINTRESOURCE(IDD_DIALOG_REPLAY),NULL,WndProc);
if (!hDialog)
{
char buf [100];
wsprintf (buf, "Error x%x", GetLastError ());
MessageBox (0, buf, "CreateDialog", MB_ICONEXCLAMATION | MB_OK);
return 1;
}
ShowWindow(hDialog, SW_SHOW);
Note 1 : The message loop is already present in another dll executed in the same thread
Note 2 : in a first time, I use a call back function WndProc which is pretty standard and which basically executes the DefWindowProc function
When I compile my dll (with ressource compilation verbose option set), I get the following messages :
1>Writing DIALOG:101, lang:0x40c, size 452.
1>Writing AFX_DIALOG_LAYOUT:101, lang:0x40c, size 2.
When I open the binary of my dll in VS2017, I can see that there is a dialogbox id 101
=> The dialog box is actually present in my binary file.
But when I execute it, I get an error 0x715 : ERROR_RESOURCE_TYPE_NOT_FOUND and of course, the dialog box is not created.
Note : this happen, no matter if the dialog box contains controls or if it is empty
I have absolutly no clue why this is happening. Any help would be really welcome.
Thanks in advance,
Antoine
Ok,thanks to Hans, I found the reason.
I was using the exe hInstance and so, the program was looking for the dialog box inside the exe and not inside the dll.
Changing the hInstance to the dll one fixed my issue.
Thanks again Hans
I am struggling to understand what may be causing the issue in my case.
I am using
ShellExecuteW('open', 'explorer.exe', '/select,[file_name]', None, win32con.SW_SHOW)
What I am trying to do is open the file in the OS, highlight it, and bring the File Explorer to the foreground. This works fine for most cases, but when I try to open a file that exceeds the MAX_PATH limit (260 characters), the file doesn't open, and instead it takes me to the "My Files" page.
I have tried prepending "\\?\" to the beginning of my file name, because that is what other Stack Overflow posts said to do with regards to overriding the MAX_PATH limit, but it has not changed the situation.
Does the ShellExecuteW function not allow for files that exceed MAX_PATH? And, if so, is there any workaround I could use?
I read some cases, about this issue. Find this article:Long Paths in .NET, Part 1 of 3 [Kim Hamilton]
If you prefix the file name with "\?\" and call the Unicode versions of the Windows APIs, then you can use file names up to 32K characters in length. In other words, the \?\ prefix is a way to enable long paths while working with the Windows file APIs.
and:
Long paths with the \?\ prefix can be used in most of the file-related Windows APIs, but not all Windows APIs.
I also test ShellExcuteW with \\?\,it failed.
Working well with SHOpenFolderAndSelectItems
CoInitialize(NULL);
LPCWSTR file_name ;//Change the path according to your needs
PIDLIST_ABSOLUTE pidl;
if (SUCCEEDED(SHParseDisplayName(file_name, 0, &pidl, 0, 0)))
{
ITEMIDLIST idNull = { 0 };
LPCITEMIDLIST pidlNull[1] = { &idNull };
SHOpenFolderAndSelectItems(pidl, 1, pidlNull, 0);
ILFree(pidl);
}
Note:CoInitialize or CoInitializeEx must be called before using SHOpenFolderAndSelectItems. Not doing so causes SHOpenFolderAndSelectItems to fail.
I'm using VBScript to create a UAC prompt for a batch file. I don't know how to get the return value of the UAC prompt. For example if I try to UAC a file that doesn't exist I should get an error, right?
For example:
Dim rc
Set UAC = CreateObject("Shell.Application")
rc = UAC.ShellExecute("thisdoesntexist.exe", "", "", "runas", 1)
WScript.Echo rc
rc doesn't contain a code. Also, is there any way I can get the error code of whatever I'm executing? Is ShellExecute asynchronous in VBScript?
IShellDispatch2.ShellExecute method
Performs a specified operation on a specified file.
Syntax
IShellDispatch2.ShellExecute(sFile [, vArguments] [, vDirectory] [,
vOperation] [, vShow]) Parameters
sFile Required. String that contains the name of the file on which
ShellExecute will perform the action specified by vOperation.
vArguments Optional. Variant that contains the parameter values for
the operation.
vDirectory Optional. Variant that contains the fully qualified path of
the directory that contains the file specified by sFile. If this
parameter is not specified, the current working directory is used.
vOperation Optional. Variant that specifies the operation to be
performed. It should be set to one of the verb strings that is
supported by the file. For a discussion of verbs, see the Remarks
section. If this parameter is not specified, the default operation is
performed.
vShow Optional. Variant that recommends how the window that belongs to
the application that performs the operation should be displayed
initially. The application can ignore this recommendation. vShow can
take one of the following values. If this parameter is not specified,
the application uses its default value.0
Open the application with a hidden window.
1 Open the application with a normal window. If the window is
minimized or maximized, the system restores it to its original size
and position.
2 Open the application with a minimized window.
3 Open the application with a maximized window.
4 Open the application with its window at its most recent size and
position. The active window remains active.
5 Open the application with its window at its current size and
position.
7 Open the application with a minimized window. The active window
remains active.
10 Open the application with its window in the default state specified
by the application.
Return Value
No return value.
Remarks
This method is equivalent to launching one of the commands associated
with a file's shortcut menu. Each command is identified by a verb
string. The supported verbs vary from file to file. The most commonly
supported verb is "open", which is also usually the default verb.
Others might be supported only by certain types of files. For further
discussion of Shell verbs, see Launching Applications or Extending
Shortcut Menus.
This method is not currently available in Microsoft Visual Basic.
Examples
The following example uses ShellExecute to open Microsoft Notepad.
Proper usage is shown for Microsoft JScript and Visual Basic Scripting
Edition (VBScript).
<script language="VBScript">
function fnShellExecuteVB()
dim objShell
set objShell = CreateObject("Shell.Application")
objShell.ShellExecute "notepad.exe", "", "", "open", 1
set objShell = nothing
end function
</script>
Now all COM calls look like this HResult = methodcall(param1, param2, ..., paramn, OUTPARAM).
VB pretends it OUTPARAM = methodcall(param1, Param2, ..., paramn) with HResult appearing in the err.object.
So errors will still fire, it's just that it doesn't wait to find out.
I have read this and understand lpClass can be used to fix the "wrong file extension issue". However, when I am reading the following lines of code, I can't figure out what lpClass is used for when opening an executable file.
//code excerpt from foo.exe
SHELLEXECUTEINFO info;
ZeroMemory(&info, sizeof(SHELLEXECUTEINFO));
info.cbSize = sizeof(SHELLEXECUTEINFO);
info.nShow = SW_NORMAL;
info.lpVerb = L"open";
info.lpClass = L"ProgId Of foo.exe"; //what is this used for???
info.fMask = SEE_MASK_FLAG_LOG_USAGE | SEE_MASK_CLASSNAME;
info.lpFile = L"bar.exe";
info.lpParameters = lpszParam;
ShellExecuteEx(&info);
Without lpClass being specified, if lpVerb is "open" and lpFile is an exe, running the code simply executes the exe. But what if lpClass is specified as in this case?
The parameter lpClass should be the progID of the file type. What does that mean?
Well consider what happens if you don't pass the class.
In reality, it means the Shell looks up the file extension (e.g. .htm) in the registry, under HKEY_CLASSES_ROOT\.htm. Then it checks the default value which is generally htmlfile. (It also uses other tricks, but in the vast majority of cases it's the extension which determines the progid).
Next it looks up HKEY_CLASSES_ROOT\htmlfile, and uses the information there (under HKEY_CLASSES_ROOT\htmlfile\shell\open) to decide how to open the file.
So how do you use lpClass? Well, for example, suppose you have an .TXT file, but you know it is really html, you can pass "htmlfile" as the lpclass parameter. This will skip the step 1 (looking at the file extension to find the class) and go straight to step 2. This will (usually) cause the file to be opened in a browser instead of notepad.
In your example you have passed "bar.exe" as the lpFile parameter. If you pass "txtfile" as lpClass you should find that instead of running bar.exe it opens it in notepad.