I call standard ShellExecuteW call on Windows8.1 to open PPS (powerpoint slide) file.
This works just fine on Windows 7. On Windows 8.1. it reports "There is no program associated to open the file". Of course, the file association is set and if file is saved and run from Explorer (double clicked) it opens just fine. I also tried to change association and to associate another program and then associate back to PPS viewer, no improvement. It just doesn't work for W8.1 but the same call works on earlier Windows.
Can anyone give me a clue what might be wrong here?
The code used to open file is very simple and I see no errors with it:
HINSTANCE hinst = ShellExecuteW(NULL, L"open", L"C:\\path\\to\\file.pps", NULL, NULL, SW_SHOWNORMAL);
// Check if result is error
if ((int)hinst <= 32)
{
wchar_t buf[512] = { 0 };
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 512, NULL);
MSGBOX(buf);
}
I use free PPS viewer as found here:
http://www.microsoft.com/en-us/download/details.aspx?id=13
I found something similar which points to that this could be a bug in Win8.1. Can anyone confirm this? Or reveal a workaround?
I found the solution myself.
The problem on W8.1 was that the verb open was not registered to the application so it used different default verb. So if the ShellExecute call is replaced with:
HINSTANCE hinst = ShellExecuteW(NULL, NULL, L"C:\\path\\to\\file.pps", NULL, NULL, SW_SHOWNORMAL);
Then the system looks for a default verb which may or may not be open (usually is), so by not using this verb explicitly it leaves this decision to the system.
Related
I am trying to open sigverif.exe from my code in c++ but the return value is 2 and .exe does not open
ShellExecute(NULL, _T("open"), _T("C:\\Windows\\System32\\sigverif.exe"), NULL, NULL, SW_RESTORE);
If I open the sigverif.exe from run command typing
"C:\Windows\system32\sigverif.exe"
it works fine
What could be the issue?
the return value is 2 and .exe does not open
The return value based on System Error Codes means ERROR_FILE_NOT_FOUND.
Yes, indeed your application failed to find the given path, because you're building it on x86, where Windows's automatic redirection involves, and replace C:\Windows\System32 with C:\Windows\SysWOW64, which contains 32-bit binaries for Windows.
You have two options:
Either you just build it on x64, or disable the automatic redirection by using Wow64DisableWow64FsRedirection as follows:
PVOID OldValue = nullptr;
Wow64DisableWow64FsRedirection(&OldValue);
ShellExecute(NULL, _T("open"), _T("C:\\Windows\\System32\\sigverif.exe"), NULL, NULL, SW_RESTORE);
Be aware that Wow64DisableWow64FsRedirection affects globally in the current thread, as you can find more detail in the page:
Note The Wow64DisableWow64FsRedirection function affects all file operations performed by the current thread, which can have unintended consequences if file system redirection is disabled for any length of time....
So make sure it won't affect other operations unintentionally, or set it back to enabled immediately after your desire is resolved by invoking Wow64EnableWow64FsRedirection.
Can I safely assume that Windows Explorer is always started from a Windows system directory? Also, is its process always named "explorer.exe"?
And if not, how to get its full file path?
EDIT: Forgot to mention -- I need this to later find out the process ID of the Windows Explorer running in a given user session. Thus my search for its full path.
EDIT 2: Thanks everyone who contributed, and especially to sehe! After his post I found this page that explains how to set up your own shell. I made a wild test by completely replacing explorer.exe with my own process and here's the result:
Here's the full-size link if you it gets re-sized.
As you can see, I can technically replace explorer.exe with whatever process I may come up with. As you can also see in my screenshot Windows gives me a complete control over the Shell (the screenshot is my entire window.)
So the bottom line, the only way to get "explorer.exe" file path (or whatever Shell process is used) is to use those registry keys from the link I quoted above -- pretty much close to what sehe suggested, with just a few more checks to do, but it's a pretty straightforward stuff.
As for Sean Cline's suggestion, it would be a very elegant solution ONLY if we have the "stock" Windows Explorer running that comes with a tray window with that specific class name.
It is probably safe to assume that explorer.exe is always in the %windir% or %SystemRoot% as it hasn't moved for years. But, if you are trying to invoke something via Explorer, chances are you want to use the ShellExecute() function instead.
If you really do need the path, the easiest way to get it is probably with a call to SHGetKnownFolderPath() using FOLDERID_Windows as the first argument.
Edit:
Here is my stab at some code knowing that you are looking for the PID of the shell process:
DWORD trayPID;
HWND trayWnd = FindWindow("Shell_TrayWnd", NULL);
GetWindowThreadProcessId(trayWnd, &trayPID);
It looks for the hWnd of the taskbar and finds the owning PID. You will likely need to add some error handling for the case that explorer is not running and that window does not exist - unlikely, but possible.
No you can't safely assume that and none of this has to do with C++.
Also, you didn't show any code. Here goes:
The registry key for this is Software\Microsoft\Windows NT\CurrentVersion\WinLogon\Shell (see here).
#include <windows.h>
#include <malloc.h>
#include <stdio.h>
#include <string>
LONG GetStringRegKey(HKEY hKey, const std::wstring &strValueName, std::wstring &strValue, const std::wstring &strDefaultValue)
{
strValue = strDefaultValue;
WCHAR szBuffer[512];
DWORD dwBufferSize = sizeof(szBuffer);
ULONG nError;
nError = RegQueryValueExW(hKey, strValueName.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize);
if (ERROR_SUCCESS == nError)
{
strValue = szBuffer;
}
return nError;
}
int main()
{
HKEY hKey;
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", 0, KEY_READ, &hKey);
bool bExistsAndSuccess (lRes == ERROR_SUCCESS);
bool bDoesNotExistsSpecifically (lRes == ERROR_FILE_NOT_FOUND);
std::wstring shell;
GetStringRegKey(hKey, L"Shell", shell, L"");
}
Yes to both. Windows Explorer is always located at %WINDIR%\Explorer.exe.
Is there a simple way to open a file by its associated program in windows?
(like double clicking it in windows explorer but done automatically with my code)
For example, on computer A, "text.txt" will be opened in wordpad but on computer B it will be opened by Notepad++ because of the users file extension assignments.
I tried ShellExecute
ShellExecute(0, L"open", L"c:\\windows\\notepad.exe" ,L"c:\\outfile.txt" , 0 , SW_SHOW );
which works but if I omit the notepad.exe parameter weird things happen (a random explorer is shown).
You want to use the file to open as the file argument, not the parameter argument. No need to specify which program to use, ShellExecute will look it up for you.
ShellExecute(0, 0, L"c:\\outfile.txt", 0, 0 , SW_SHOW );
By leaving the verb as NULL (0) rather than L"open", you get the true default action for the file type - usually this is open but not always.
See Launching Applications:
ShellExecute(NULL, "open", L"c:\\outfile.txt", NULL, NULL, SW_SHOW);
On windows, a good memory hook is to think of all data-files being executable by the shell. You can also try it out in a command box, where you can just type a filename, and it will be opened up. Or, the other way around, every file in Windows can be opened, and the default opening-action for executable files is to execute them.
According to the MS Knowledge Base, ShellExecute should work (we do this in Delphi all the time):
ShellExecute(Handle, "Open", Filename, "", "C:\", SW_SHOWNORMAL)
A little more possibilities here:
If you want to open - for example - the file by default with Notepad++ (if installed), you could scan for it's registry key if it exists and where it is, (Usually HKLM\SOFTWARE\Wow6432Node\Notepad++ [tested Win7]) then take that path and open it.
std::wstring file = L"C:\\Outfile.txt";
if (NotepadPlusPlusExists()) //Open with Notepad++ or use an other program... (maybe your own ?)
{
std::wstring wsNPPPath = GetNotepadPlusPlusPath();
ShellExecuteW(HWND, L"open", wsNPPPath.c_str(), file.c_str(), NULL, SW_NORMAL);
}
else //Open with default associated program <---
ShellExecuteW(HWND, NULL, file.c_str(), NULL, NULL, SW_NORMAL);
If you want the user to be able to change the default program or select a program he/she wants to use, you may open the "Open with" dialog.
//std::wstring StringArgsW(const wchar_t *format, ...);
std::wstring wsCmdOpenWith = StringArgsW(L"C:\\Windows\\system32\\shell32.dll,OpenAs_RunDLL \"%s\"", file.c_str());
ShellExecuteW(HWND, L"open", L"C:\\Windows\\system32\\rundll32.exe", wsCmdOpenWith.c_str(), NULL, SW_NORMAL);
You can also open the file in explorer.
std::wstring wsCmdExplorer = StringArgsW(L"/select,\"%s\"", file.c_str());
ShellExecuteW(HWND, L"open", L"explorer.exe", wsCmdExplorer.c_str(), NULL, SW_NORMAL);
If lpFile specifies a document file, the flag is simply passed to the
associated application
So you need to substitute "c:\\windows\\notepad.exe" with the actual file you want to open and leave lpParameters null.
Maybe try start instead of open?
I can't seem to use the AssignProcessToJobObject function to assign the current process to a job object handle given by CreateJobObject. This has been asked a few times already on StackOverflow, but so far none of the solutions (which usually boil down to embedding an UAC manifest) seem to work for me.
I'm using MSVC9 on Windows 7 for this. Here's the source code for my sample application and a small manifest I'm embedding (which supposedly fixes the problem - but not for me):
My sample application (main.cpp):
#include <windows.h>
static void dumpLastError()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
OutputDebugStringA( (LPTSTR)lpMsgBuf );
LocalFree(lpMsgBuf);
}
int main()
{
HANDLE job = CreateJobObjectA( NULL, "demo job 123" );
if ( !job ) {
OutputDebugStringA( "CreateJobObject failed" );
dumpLastError();
return 1;
}
if ( !AssignProcessToJobObject( job, GetCurrentProcess() ) ) {
OutputDebugStringA( "AssignProcessToJobObject failed" );
dumpLastError();
return 1;
}
return 0;
}
The UAC manifest (main.exe.manifest):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="requireAdministrator"/>
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
</assembly>
I build this sample by running
cl main.cpp
mt -manifest main.exe.manifest -outputresource:main.exe;1
Unfortunately, running my main.exe sample after these steps still yields an 'Access denied' error in debug output when attempting the AssignProcessToJobObject call. Does anybody know why that is?
I know this is an old question, but I was recently having the exact same problem.
As suggested I was using the command-line workaround until a couple of minutes ago I found this post.
Since I was creating the process, I just followed the instructions on the article adding CREATE_BREAKAWAY_FROM_JOB to the process creation flags:
CreateProcess(szPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)
with
CreateProcess(szPath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi)
I tested and it works as expected, the process gets assigned to the job, no UAC manifest, no command-line.
Hope it helps you or anyone else having this problem.
I followed a number of discussions at one point relating to job objects and UAC manifests. The only piece of information that helped me around the same problem that you're having was that this security feature (introduced in Vista) is apparently not enforced when run from a cmd.
I assume you're launching PyCmd from the start menu. Try launching it from cmd, and I'll bet the problem will go away there too.
What I ended up doing (to run mintty with cygwin) was to make a mintty.bat which said
start mintty.exe
and then a shortcut to mintty.bat which had a property set to run 'minimized' (I forget the exact wording). This allows the shell I wanted to be launched from the start menu and still work as if it had been launched from cmd.exe.
As a side-note, I'd love it if someone would come along and actually explain the problem at a build-level, and how to fix it.
We had a line of code
if( !CreateFile( m_hFile, szFile, GENERIC_READ|GENERIC_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL ) )
{
DWORD dwErr = GetLastError();
CString czInfo;
czInfo.Format ("CMemoryMapFile::OpenAppend SetFilePointer call failed - GetLastError returned %d", dwErr);
LOG(czInfo);
return false;
}
This code worked great for many years. A few weeks ago, we had a customer with a problem. Turns out, the problem could be traced to this line of code, where the function would return a INVALID_HANDLE_VALUE handle and GetLastError() returned ERROR_FILE_NOT_FOUND(2).
Now, this is very confusing to us. OPEN_ALWAYS should direct the file to be created if it does not exist. So, why are we getting a ERROR_FILE_NOT_FOUND?
More confusion: For this customer, this only happened on one network share point (we were using a UNC path). Other UNC paths to other machines for this customer worked. Local paths worked. All our other customers (10000+ installs) have no problem at all.
The customer was using XP as the client OS, and the servers were running what appeared to be standard Windows Server 2003 (I think the Small Business Server version). We could not replicate their errors in our test lab using the same OS's. They could repeat the problem with several XP clients, but the problem was only on one server (other Server 2003 servers did not exhibit the problem).
We fixed the problem by nesting two CreateFile calls, the first with OPEN_EXISTING, and the second with CREATE_ALWAYS if OPEN_EXISTING failed. So, we have no immediate need for a fix.
My question: Does anyone have any idea why this API call would fail in this particular way? We are puzzled.
Addendum:
The CreateFile function above is a wrapper on the Windows API function. Here's the code:
bool CMemoryMapFile::CreateFile( HANDLE & hFile, LPCSTR szFile, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes )
{
hFile = ::CreateFile (szFile, dwDesiredAccess, dwShareMode, NULL,
dwCreationDisposition, dwFlagsAndAttributes, NULL);
return (hFile != INVALID_HANDLE_VALUE)
}
First, you should always check for success of the CreateFile() API like this:
if (CreateFile(...) == INVALID_HANDLE_VALUE)
{
// handle the error
}
because CreateFile() doesn't return !=0 in case of success, but anything other than INVALID_HANDLE_VALUE (which is -1).
Then, the CreateFile() can fail in the situation you described if either the directory doesn't exist where you want to create/open the file in, or it can fail if the user has the rights to open and write files in that directory, but no rights to create new files.
Maybe the directory you wanted to create the file in did not exist?
Are you sure you fixed it by using 2 CreateFile calls? Or did you just not reproduce it?
We have also seen this problem when a file is accessed by a UNC share. None of the ideas and suggestions applied in our case - the directory always exists, the parameters to CreateFile are correct, we are checking the return correctly.
We believe this to be a shares issue. We solved the problem with a simple retry loop:
If a CreateFile with OPEN_ALWAYS fails with ERROR_FILE_NOT_FOUND, we simply sleep for a few ms and try again. It always works second time (if it failed first time).
If CreateFile fails, it will return INVALID_HANDLE_VALUE which is non-zero (I think it's negative 1). So CreateFile might be succeeding and returning zero as the handle.
It's only safe to check GetLastError() after a function fails but it looks like you might be checking the last error when CreateFile has succeeded (returned zero).
According to this article, some functions set the "last error" if they succeed:
My guesses would be something server related like the server not implementing support correctly for that filesystem operation. Maybe a linux server compared to a windows server?
Your arguments to create file are incorrect, so I assume that this is some sort of CreateFile helper function.
A call to CreateFile should look like:
m_hFile = CreateFile( szFile, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_hFile == INVALID_HANDLE_VALUE)