c++ ShellExecute not working - windows

I am working on a c++ program that is supposed to launch internet explorer and display a local html file on Windows 7. I am trying to use ShellExecute, but it doesn't work. I googled around, but could not find an answer that worked. Here is the code:
ShellExecute(NULL, "open", "start iexplore %userprofile%\\Desktop\\html_files\\file.hml", NULL, NULL, SW_SHOWDEFAULT);
I copied the command into a system() call just to see if it would work, and it did. Here is the system() call I tried:
system("start iexplore %userprofile%\\Desktop\\html_files\\file.html");
Since the system call worked, its clearly a problem with ShellExecute. Basically, Internet Explorer does not come up. Everything compiles correctly, though. Any ideas?

The paths of the user's shell folders, including the Desktop, can be customized by the user, so %userprofile\desktop is not guaranteed to be the correct path on all systems. The correct way to get the user's actual Desktop path is to use SHGetFolderPath(CSIDL_DESKTOPDIRECTORY) or SHGetKnownFolderPath(FOLDERID_Desktop).
You do not need to know the path to iexplorer.exe, Windows known how to find it. So just specify "iexplorer.exe" by itself as the lpFile parameter and the HTML filename as the lpParameter parameter:
ShellExecute(NULL, "open", "iexplore.exe", "full path to\\file.hml", NULL, SW_SHOWDEFAULT);
With that said, this is very IE-specific. If you want to load the file in the user's default HTML browser/viewer, set the lpVerb parameter to NULL and the HTML file as the lpFile parameter:
ShellExecute(NULL, NULL, "full path to\\file.hml", NULL, NULL, SW_SHOWDEFAULT);
This is the same as if the user had double-clicked on the file in Windows Explorer.

I dont think IE will recognize environment variables in URI's. In fact % have special meaning.
Something like this should work:
#include <windows.h>
int main()
{
ShellExecute(NULL, "open",
"C:\\progra~1\\intern~1\\iexplore.exe",
"file:///C:/Users/UserName/Desktop/html_files/file.html",
"",
SW_MAXIMIZE);
return 0;
}
Another way, obtain the %userprofile% enviroment variable value and concat your URI:
#if (_MSC_VER >= 1400)
#pragma warning(push)
#pragma warning(disable: 4996) // Disabling deprecation... bad...
#endif
#include <windows.h>
#include <stdlib.h>
#include <iostream>
#include <string>
int main()
{
std::string uri = std::string("file:///") + getenv("USERPROFILE") + "/Desktop/html_files/file.txt";
ShellExecute(NULL, "open",
"C:\\progra~1\\intern~1\\iexplore.exe",
uri.c_str(),
"",
SW_MAXIMIZE);
return 0;
}
Im disabling warnings here, but you should use _dupenv_s instead of getenv.
Good luck.

Related

MFC - Display message

I'm trying to display a simple message within my first MFC application.
Strangely, the first sample doesn't work, instead the second one works correctly.
auto text = std::to_wstring(1).c_str();
MessageBox(text, NULL, 0); // Not ok, the message is empty
auto temp = std::to_wstring(1);
MessageBox(temp.c_str(), NULL, 0); // Ok, display 1
Can you explain why of this behavior?
Yes, in the first example, the wstring created by the call to std::to_wstring only has the scope of the line. After the line executes, it is out of scope and its value is dubious.
In the second example, the wstring is still in scope and valid and so the call to .c_str() works.
No, the other answer is wrong. Look at the implementation of c_str(). c_str() returns basically a LPCWSTR... call it a const WCHAR* or const wchar_t* or whatever. However, the return of c_str() is to an internal pointer of wstring. The problem is that after the line of code executes, the wstring returned from to_wstring() is not valid and so the the pointer returned by c_str() is garbage. For fun, try the following code:
//cstr_.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv)
{
auto temp = to_wstring(1).c_str();
wprintf(L"%s\n", temp);
auto temp2 = to_wstring(1);
wprintf(L"%s\n", temp2.c_str());
wstring ws = to_wstring(1);
auto temp3 = ws.c_str();
wprintf(L"%s\n", temp3);
}
I compiled the above from a VC++ shell prompt with: cl.exe cstr.cpp
If the other answer is correct, then the last line should have garbage or nothing output because according to the other answer, c_str() is a temp. But, if my answer is correct, then it should output 1 (which it does). If all else fails, look at the implementation source code.

Launching a program before Debugging in VSCode

I am using VSCode 0.10.6 for C++ programming. I am trying to launch a program prior to debugging. The program, OpenOCD, is what GDB connects to. If I manually open and close it through a terminal, it works fine, but it seems like there should be an easy way to get VSCode to just start it for me.
I have played with tasks.json and it appears you need to use some ugly bat/sh files to accomplish this in combination with preLaunchTasks in launch.json.
Currently the answer is that you must indeed use preLaunchTasks to have a chance of making this work. I would have been happy to use an ugly script, if that indeed would work - but it doesn't. In my case, I needed one or more executables to be ran in the background, allowing VSCode to continue into debugging.
Unfortunately, each executable I tried to launch (via start) was not actually running as a "detached" process, and so VSCode would wait for each executable to finish running before it would finish the preLaunchTasks and start debugging. Not what I wanted.
I found an article by someone having a similar "detached process" problem with subversion, and I used his C++ code to solve this same issue with Visual Studio Code. I found a bug or two in that code, which I fixed. Here is what I'm currently using:
// dstart.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
//http://stackoverflow.com/questions/1536205/running-another-program-in-windows-bat-file-and-not-create-child-process
//http://svn.haxx.se/users/archive-2008-11/0301.shtml
int _tmain()
{
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx
LPWSTR pCmd = ::GetCommandLine();
// skip the executable
if (*pCmd++ == L'"')
while (*pCmd++ != L'"');
else
while (*pCmd != NULL && *pCmd != L' ') ++pCmd;
while (*pCmd == L' ') pCmd++;
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
BOOL result = CreateProcess
(
NULL, // No module name (use command line)
pCmd, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set bInheritHandles to FALSE
CREATE_NEW_CONSOLE, // Detach process
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
if (result) return 0;
wchar_t msg[2048];
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
::GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
msg, sizeof(msg),
NULL
);
fputws(msg, stderr);
_flushall();
return -1;
}
Once compiled, you can use it similarly to how the start command works at a DOS prompt. Place that in the script you attach to your preLaunchTasks
in Visual Studio Code.

Best way to have crash dumps generated when processes crash?

In Windows environments (XP and Win 7):
What is the best way to automatically have a crash dump generated when processes crash on the system?
Can an installer (MSI) package do this?
One of the best way to have an automatic dump for any/specific process on Windows is to configure a set of entries in the registry. I tried the below on Windows 7 64 bit.
Open notepad.exe, paste the below entry and save it as "EnableDump.reg". You can give any name you wish.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpFolder"=hex(2):44,00,3a,00,5c,00,64,00,75,00,6d,00,70,00,00,00
"DumpCount"=dword:00000010
"DumpType"=dword:00000002
"CustomDumpFlags"=dword:00000000
Double click the "EnableDump.reg" and select 'Yes'. I have given the dump folder as 'd:\dump'. You can change it to whatever folder you wish.
Try to execute a crashing application, Windows will display the error dialog. Choose 'Close the Program' option. After that you will see the dump in the configured folder. The name of the dump file will be .exe..dmp.
For more details, you can refer the below link.
http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
Below explanation is based on another answer, but the logic is mine (without attribution need, as said on my profile);
Having your own dump generation framework which automatically creates a process dump when any Unhandled exception is encountered, would avoid clients having to install WinDbg.
At the application start up use SetUnhandledExceptionFilter(...) Win32 API to register a callback (i.e. application level exception-handler).
Now the registered callback function is called whenever there is any exception which is not handled. You may then create the process dump using MiniDumpWriteDump(...) API from DbgHelp.dll.
C++ Sample (unicode-enabled)
header-file
#ifndef CRASH_REPORTER_H
#define CRASH_REPORTER_H
//Exclude rarely used content from the Windows headers.
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
#else
# include <windows.h>
#endif
#include <tchar.h>
#include <DbgHelp.h>
class CrashReporter {
public:
inline CrashReporter() { Register(); }
inline ~CrashReporter() { Unregister(); }
inline static void Register() {
if(m_lastExceptionFilter != NULL) {
fprintf(stdout, "CrashReporter: is already registered\n");
fflush(stdout);
}
SetErrorMode(SEM_FAILCRITICALERRORS);
//ensures UnHandledExceptionFilter is called before App dies.
m_lastExceptionFilter = SetUnhandledExceptionFilter(UnHandledExceptionFilter);
}
inline static void Unregister() {
SetUnhandledExceptionFilter(m_lastExceptionFilter);
}
private:
static LPTOP_LEVEL_EXCEPTION_FILTER m_lastExceptionFilter;
static LONG WINAPI UnHandledExceptionFilter(_EXCEPTION_POINTERS *);
};
#endif // CRASH_REPORTER_H
source-file
#include "crash-report.h"
#include <stdio.h>
LPTOP_LEVEL_EXCEPTION_FILTER CrashReporter::m_lastExceptionFilter = NULL;
typedef BOOL (WINAPI *MiniDumpWriteDumpFunc)(HANDLE hProcess, DWORD ProcessId
, HANDLE hFile
, MINIDUMP_TYPE DumpType
, const MINIDUMP_EXCEPTION_INFORMATION *ExceptionInfo
, const MINIDUMP_USER_STREAM_INFORMATION *UserStreamInfo
, const MINIDUMP_CALLBACK_INFORMATION *Callback
);
LONG WINAPI CrashReporter::UnHandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtr)
{
//we load DbgHelp.dll dynamically, to support Windows 2000
HMODULE hModule = ::LoadLibraryA("DbgHelp.dll");
if (hModule) {
MiniDumpWriteDumpFunc dumpFunc = reinterpret_cast<MiniDumpWriteDumpFunc>(
::GetProcAddress(hModule, "MiniDumpWriteDump")
);
if (dumpFunc) {
//fetch system time for dump-file name
SYSTEMTIME SystemTime;
::GetLocalTime(&SystemTime);
//choose proper path for dump-file
wchar_t dumpFilePath[MAX_PATH] = {0};
_snwprintf_s(dumpFilePath, MAX_PATH, L"crash_%04d-%d-%02d_%d-%02d-%02d.dmp"
, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay
, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond
);
//create and open the dump-file
HANDLE hFile = ::CreateFileW( dumpFilePath, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN
, NULL
);
if (hFile != INVALID_HANDLE_VALUE) {
_MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
exceptionInfo.ThreadId = GetCurrentThreadId();
exceptionInfo.ExceptionPointers = exceptionPtr;
exceptionInfo.ClientPointers = NULL;
//at last write crash-dump to file
bool ok = dumpFunc(::GetCurrentProcess(), ::GetCurrentProcessId()
, hFile, MiniDumpNormal
, &exceptionInfo, NULL, NULL
);
//dump-data is written, and we can close the file
CloseHandle(hFile);
if (ok) {
//Return from UnhandledExceptionFilter and execute the associated exception handler.
// This usually results in process termination.
return EXCEPTION_EXECUTE_HANDLER;
}
}
}
}
//Proceed with normal execution of UnhandledExceptionFilter.
// That means obeying the SetErrorMode flags,
// or invoking the Application Error pop-up message box.
return EXCEPTION_CONTINUE_SEARCH;
}
usage
#include "3rdParty/crash-report.h"
int main(int argc, char *argv[])
{
CrashReporter crashReporter;
(void)crashReporter; //prevents unused warnings
// [application main loop should be here]
return 0;
}
Windows XP:
The following steps enable automatic crash dumps:
1) Open a command prompt, running as administrator
2) Run drwtsn32 -i. This will install Doctor Watson as the default debugger when something crashes
3) Click Ok
4) From the command prompt, run drwtsn32
5) Set the Crash Dump path to your favorite directory, or leave the default.
6) Set the Crash Dump Type to mini. Note that under some circumstances, we may ask you for a full crash dump.
7) Make sure the Dump All Thread Contexts and Create Crash Dump File options are selected.
8) Click Ok
9) If a user.dmp file already exists in the Crash Dump path, delete it.
Windows 7: Location is:
C:\Users[Current User when app crashed]\AppData\Local\Microsoft\Windows\WER\ReportArchive

ShellExecute print verb fails to print from 32 bit app on 64 bit windows

I have a 32 bit program that has been installed by a customer on 64 bit windows.
There appears to be a problem with using ShellExecute and the print verb in that configuration. First my test program.
// printme.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "objbase.h"
#include <windows.h>
#include <shellapi.h>
int main(int argc, char* argv[])
{
if (argc != 2)
{
printf("Usage: %s file_to_print", argv[0]);
return 0;
}
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) ; //| COINIT_DISABLE_OLE1DDE);
HINSTANCE retVal = ::ShellExecute(NULL, "print", argv[1], NULL, NULL, 0); // don't ask, as the user can always cancel...
printf("RetVal = %08x\n", retVal);
printf("LastError = %08x\n", GetLastError());
return 0;
}
This program works correctly on 32 bit windows version up to Windows 7. The program simply runs the print verb on the first argument passed on the command line.
printme Page1.htm
On the system in question, the registry is set up as follows:
HKEY_CLASSES_ROOT\htmlfile\shell\print\command
contains a default value of type REG_EXPAND_SZ containing
rundll32.exe %windir%\system32\mshtml.dll,PrintHTML "%1"
If I run the following command
rundll32 c:\windows\system32\mshtml.dll,PrintHTML “Page1.htm”
the print dialog is successfully shown.
However running my program blinks, but the print dialog never appears, and a stalled copy of
C:\Windows\sysWow64\rundll32.exe is in process manager, which never completes.
Is there a workaround, or is ShellExecute permanently broken for common verbs on common file types from 32 bit programs on 64 bit windows?
It turns out the problem is the last parameter of ShellExecute. While 0 worked for years, it now requires SW_SHOW to function correctly for the print verb in this case. Perhaps a recent windows update changed the behavior?

How to determine if a shell verb is registered for a given file type?

Given a file extension, how would I query to see if any program is registered to handle a specific ShellExecute verb ("print" in my case) without actually invoking said verb?
#include <windows.h>
#include <Shlwapi.h>
#include <stdio.h>
#if _WIN32_WINNT <= 0x0501
#define ASSOCF_INIT_IGNOREUNKNOWN 0x00000400
#endif
BOOL AssocExtHasVerb(LPCTSTR dotExt,LPCTSTR verb)
{
HKEY hKey;
HRESULT hr = AssocQueryKey(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCKEY_SHELLEXECCLASS,dotExt,verb,&hKey);
if (SUCCEEDED(hr))
{
RegCloseKey(hKey);
return TRUE;
}
return FALSE;
}
void main()
{
LPCTSTR ext=".txt",verb="print";
printf("%s:%s=%d\n",ext,verb,AssocExtHasVerb(ext,verb));
}
If you want to query the registry yourself (why?) you need to be able to handle several "redirections", off the top of my head:
(Vista+) First check HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\%.ext%\UserChoice : (REG_SZ) "ProgId"
If that key is empty, read HKCR\%.ext% : (REG_SZ) "" (default)
You should now have a ProgId, then check HKCR\%ProgId% : (REG_SZ) "CurVer", if it exists (and has a shell subkey) you found the "real" ProgId, if not, keep using the original ProgId.
You can now check for HKCR\%ProgId%\shell\%verb% (You really should check to see if it has a command subkey with a string, or a droptarget or dde values etc)
(XP+) If you did not find the verb under the progid:
Check HKCR\SystemFileAssociations\%.ext%\shell\%verb%
(XP+) If you still have not found the verb, read HKCR\%.ext% : (REG_SZ) "PerceivedType", if non empty, check HKCR\SystemFileAssociations\%PerceivedType%\shell
If you still have not found the verb, check under HKCR\*, HKCR\AllFilesystemObjects and HKCR\Unknown
(Win7 added submenus etc, I have not taken those into consideration and I have probably left out some other steps, AFAIK not all of this is documented)
Even if you do all this, there could be a verb and you have not detected it, shell extensions can add verbs at run time and the only way to check for these is to use IContextMenu on the actual file...
I guess you could grovel the Registry.
Look up HKCU\Software\Classes\<.extension> which should have a default value. Look this up HKCU\Software\Classes\<value>\Shell\<verb>\Command. If you don't find the value under HKCU\Software\Classes also try HKLM\Software\Classes (or its alias HKCR).

Resources