Search code examples
c++windowswinapi

Handles for certain programs are not properly detached when launched using the CreateProcess function in Windows


I want to use the Windows API to launch other programs (.exe, .url, .lnk) on my computer, so I use the following to do so:

int main() {
    //const std::wstring& programPath = L"C:\\Users\\ghost\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Steam\\Steam.lnk";
    const std::wstring& programPath = L"C:\\Program Files\\Everything\\Everything.exe";

    // Prepare the command line to run the program using cmd.exe
    std::wstring commandLine = L"cmd.exe /c start \"\" \"" + programPath + L"\"";

    // Log the command line to ensure it is correct
    qDebug() << "Attempting to run command: " << commandLine;

    // Prepare the STARTUPINFO structure
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    // Create a writable copy of the command line
    std::vector<wchar_t> commandLineBuffer(commandLine.begin(), commandLine.end());
    commandLineBuffer.push_back(0);

    // Create the process
    if (!CreateProcess(
            NULL,           // Module name (use command line directly)
            commandLineBuffer.data(), // Command line
            NULL,           // Process handle not inheritable
            NULL,           // Thread handle not inheritable
            FALSE,          // Set handle inheritance to FALSE
            CREATE_NO_WINDOW | DETACHED_PROCESS, // No console window, detached process
            NULL,           // Use parent's environment block
            NULL,           // Use parent's starting directory
            &si,            // Pointer to STARTUPINFO structure
            &pi)            // Pointer to PROCESS_INFORMATION structure
        ) {
        qDebug() << "Failed to create process. Error: " << GetLastError();
    } else {
        // Close process and thread handles to ensure they are completely detached
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        qDebug() << "Process created successfully: " << commandLine;
    }

    int t;
    std::cin >> t;

    return 0;
}

Launching .url and .lnk files with this code works fine.

But, when I want to use this code to launch an .exe file, I found that the folder where the application is located still prompts "This folder is in use" when the application finishes running. So I used the Resource Monitor to find out that the handle of the application that was launched with this code still pointed to this folder, which made it impossible to rename the folder.

I started the everything.exe program using the above code, and I can see that the handle to everything.exe still points to this folder, so when I finish running this program, I still can't delete and rename this folder. As you can see from the picture below:

image

I've also tried separating using ShellExecute, and separating using ShellExecuteEx, neither of which work.

Now I want the handle to everything.exe to no longer point to this folder to make sure I can delete or rename this folder. What should I do?


Solution

  • The issue is that you passed NULL as working directory, so the working directory will be inherited from your launcher program. If you start your launcher from the directory it's in (for example by double-clicking the file in Explorer), this then means that the process you start from it will also have this directory as working directory so not only will there be a handle to it but also relative paths will get resolved with that directory as base (which may be unexpected in some cases).

    The solution is to extract the directory part of the path of the program you are launching and passing this as working directory. So if you are launching, say, C:\Windows\System32\notepad.exe, you'd need to set its working directory to C:\Windows\System32. (This is what would also happen if you'd launch the program from Explorer.)

    std::filesystem::path::parent_path will help you with this task.