c++windowswinapishellexecuteex

ShellExecuteExA sometimes fails with ERROR_FILE_NOT_FOUND (2) and changes lpFile after the call—why?


I'm working on a Windows C++ project using Visual Studio 2022 with the Unicode build, and I'm encountering an issue with ShellExecuteExA. It intermittently fails with ERROR_FILE_NOT_FOUND (error code 2), even though the file path is correct. The issue seems related to the handling of the file path string passed to ShellExecuteExA.

Debugging Logs:

I added logging to print the file path before and after calling ShellExecuteExA, but noticed that the "After" log does not get printed. The "Before" log prints the correct file path, but after ShellExecuteExA is called, the "After" log is not output, suggesting something may be altering or overwriting the string before the second log call.

Here’s the current log output for a test run:

LOG_CONSOLE("Before: %s", _strdup(execinfo.lpFile));   // Print the file path before execution
BOOL bIsExecute = ShellExecuteExA(&execinfo);          // This intermittently fails with ERROR_FILE_NOT_FOUND
LOG_CONSOLE("After: %s", _strdup(execinfo.lpFile));    // This log's Path does not appear after execution

Current Log Output:

Before: C:\Users\<UserName>\Desktop\MyProgram.exe
After:          // After log's Path does not appear

The "After" log's path does not appear, even though the path seems valid in the "Before" log. This issue only occurs when using the raw pointer returned by MakeMiddlewarePath().

Symptom:

Code:

// Windows C++ (Unicode build, Visual Studio 2022)

SHELLEXECUTEINFOA execinfo;
ZeroMemory(&execinfo, sizeof(execinfo));
execinfo.cbSize = sizeof(execinfo);
execinfo.lpVerb = TEXT("open");
execinfo.lpFile = MakeMiddlewarePath();  // File path
execinfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
execinfo.nShow = SW_SHOWMINIMIZED;

LOG_CONSOLE("Before: %s", _strdup(execinfo.lpFile));
BOOL bIsExecute = ShellExecuteExA(&execinfo);  // <‑‑ occasionally fails
LOG_CONSOLE("After: %s", _strdup(execinfo.lpFile)); // After log's Path does not appear after execution

MakeMiddlewarePath() implementation:

CString MakeMiddlewarePath()
{
    char szPath[MAX_PATH] = {};
    GetModuleFileName(nullptr, szPath, MAX_PATH);
    strcpy(strrchr(szPath, '\\') + 1, "MyProgram.exe");
    return CString(szPath);
}

Workaround:

When I cache the string using CString, the issue is resolved:

CString path = MakeMiddlewarePath();  // Cache the string in a variable
execinfo.lpFile = path;   // Pass the variable

BOOL ok = ShellExecuteExA(&execinfo);  // Now works 100% of the time

This caching method ensures the string remains valid and prevents the overwrite issue.

Question:

Any documentation or experience explaining this behavior would be greatly appreciated!

Environment:


Solution

  • The problem is indeed with the MakeMiddlewarePath() function.

    When you do

    return CString(szPath);
    

    you create a temporary object. Its life-time will end as soon as the assignment to execinfo.lpFile is finished.

    That will leave you with a dangling and invalid pointer, and undefined behavior when ShellExecuteExA tries to dereference it.

    This is why it works when you initialize another CString object, because its life-time is longer than the call to ShellExecuteExA.