0

When I use ShellExecuteEx with such a command "-unused parameter -action capturescreenshot -filename C:\\ATDK\\Screenshots\\abcd.jbg" everything works fine and Executor.exe starts with char* argv[] having all the approx. 9 parameters. But when the command has for a couple symbols more, e.g. file name is "abc...xyz.jpg" then that process has argc == 1 and the command is empty. So the command is OK before sending to ShellExecute After I changed that to the ShellExecute, not Ex, it works! Command can be very long, and it's passed successfully. Can anybody explain what is the difference? Here's a code with the SHELLEXECUTEINFO I filled up.

std::wstringstream wss;
wss << L"-unused" << " "    // added because we need to pass some info as 0 parameter
    << L"parameter" << " "  // added because EU parser sucks
    << L"-action" << " "
    << L"capturescreenshot" << " "
    << L"-filename" << " "
    << L"C:\\ATDK\\Screenshots\\abc.jpg";

SHELLEXECUTEINFO shell_info;
ZeroMemory(&shell_info, sizeof(shell_info));

shell_info.cbSize = sizeof(SHELLEXECUTEINFO);
shell_info.fMask = SEE_MASK_ASYNCOK | SEE_MASK_NO_CONSOLE;
shell_info.hwnd = NULL;
shell_info.lpVerb = NULL;
shell_info.lpFile = L"C:/ATDK/Executor";
shell_info.lpParameters = (LPCWSTR)wss.str().c_str();
shell_info.lpDirectory = NULL;
shell_info.nShow = SW_MINIMIZE;
shell_info.hInstApp = NULL;
// 1
ShellExecuteEx(&shell_info); 
// this sucks, 
// GetLastError returns err code 2147483658, 
//FormatMessage returns The data necessary to complete this operation is not yet available

// 2 
ShellExecute(NULL, NULL, L"C:/ATDK/Executor", (LPCWSTR)wss.str().c_str(), NULL, NULL);
// OK!
banana36
  • 395
  • 3
  • 16
  • 1
    That (LPCWSTR) cast only stopped the compiler from telling you the code was wrong, it did not stop you from doing it wrong. If you don't want to convert to a Unicode string with mbstowcs() or MultiByteToWideString() then use SHELLEXECUTEINFOA and ShellExecuteExA(). – Hans Passant Feb 10 '17 at 09:48
  • You are also not checking for errors correctly. You have to examine the return value of the function. – David Heffernan Feb 10 '17 at 09:54
  • Possible duplicate of [Weird behavior while converting a std::string to a LPCSTR](http://stackoverflow.com/questions/11370536/weird-behavior-while-converting-a-stdstring-to-a-lpcstr) – Raymond Chen Feb 10 '17 at 14:51
  • @HansPassant: The cast is always dubious, in this case however it's just needless. Had your initial analysis been right, changing `ShellExecuteEx` to `ShellExecute` wouldn't have fixed it. – IInspectable Feb 11 '17 at 09:09

1 Answers1

5

Your bug is here:

shell_info.lpParameters = (LPCWSTR)wss.str().c_str();

wss.str() returns a temporary object, that no longer exists after the end of the full expression in which it is created. Using it after that point is undefined behavior.

To solve this you will have to construct a std::wstring object, that lives long enough for the call to ShellExecuteEx to return.

std::wstringstream wss;
wss << L"-unused" << " "
    << L"parameter" << " "
    // ...

SHELLEXECUTEINFO shell_info;
ZeroMemory(&shell_info, sizeof(shell_info));

// Construct string object from string stream
std::wstring params{ wss.str() };

shell_info.cbSize = sizeof(SHELLEXECUTEINFO);
shell_info.fMask = SEE_MASK_ASYNCOK | SEE_MASK_NO_CONSOLE;
shell_info.hwnd = NULL;
shell_info.lpVerb = NULL;
shell_info.lpFile = L"C:\\ATDK\\Executor";  // Path separator on Windows is \
shell_info.lpParameters = params.c_str();  // Use string object that survives the call
shell_info.lpDirectory = NULL;
shell_info.nShow = SW_MINIMIZE;
shell_info.hInstApp = NULL;

ShellExecuteEx(&shell_info); 

Note that your second call

ShellExecute(NULL, NULL, L"C:\\ATDK\\Executor", wss.str().c_str(), NULL, NULL);

reliably works. Even though wss.str() still returns a temporary, it is valid until the end of the full expression (i.e. throughout the function call).

IInspectable
  • 46,945
  • 8
  • 85
  • 181