-1

This question is related to a previously asked question found here:

I'm able to invoke another executable within my runProgram() function. However, before this runProgram() function returns back to main() and closes the handle for the process. I need to retrieve the value that the executable returns when it exits...

Here is my current application:

#include <Windows.h>
#include <exception>
#include <stdio.h>
#include <tchar.h>
#include <cstdint>
#include <iostream>

uint32_t runProgram(LPCSTR lpApplicationName) {
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;

    // Set the size of the structures
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    // Run the program
    CreateProcessA(
        lpApplicationName,  // the path
        NULL,               // Command line
        NULL,               // Process handle not inheritable
        NULL,               // Thread handle not inheritable
        FALSE,              // Set handle inheritance to FALSE
        CREATE_NEW_CONSOLE, // Opens file in seperate console
        NULL,               // Use parent's environment block
        NULL,               // Use parent's starting directory
        &si,                // Pointer to STARTUPINFO structure
        &pi                 // Pointer to PROCESS_INFORMATION structure
    );

    uint32_t error = GetLastError();

    if ( error != 0)
        std::cerr << error << "\n";

    // How to retrieve and store the result from the exiting program above...?
    uint32_t cache_size = 0;

    WaitForSingleObject(pi.hProcess, INFINITE);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return cache_size;
}

int main() {
    try {
        const uint32_t cache_size = runProgram("CacheQuerry.exe");
        std::cout << cache_size << '\n';
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << "\n\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

And I want to store the return value from this executable into runProgram()'s local variable: cache_size.

Here is the main.cpp from the invoked program:

#include "CacheQuerry.h"

int main() {
    int error = cache_info();
    if (error != 0)
        std::cout << error << '\n';
    else
        std::cout << '\n';
    std::cout << "l1d_cache_size = " << l1d_cache_size() << std::endl;
    std::cout << "cache line size = " << cache_line_size() << '\n';
    return l1d_cache_size();
}

The invoked program will return the value produced by l1d_cache_size() when this program exits. This is the value that I want to store within runProgram()'s cache_size variable. How do I get this return value after the program exits? The implementation of the function call found in "CacheQuerry.h" shouldn't be relevant here, but if you need to see it's implementation don't hesitate to ask. These are two separate projects within the same solution. The 1st main.cpp which is in its own project relies on the 2nd main.cpp within its own project.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • https://stackoverflow.com/q/30612990/440558 – Some programmer dude Aug 05 '20 at 00:27
  • 1
    Calling `GetLastError()` if `CreateProcessA()` DOES NOT return `FALSE` will return an *indeterminate value*. You can't do error handling with the Win32 API this way. You have to check the return value of `CreateProcessA()` itself FIRST, *then* call `GetLastError()` only if valid to do so. And calling `WaitForSingleObject()` and `CloseHandle()` if `CreateProcessA()` actually fails is absolutely the wrong thing to do. – Remy Lebeau Aug 05 '20 at 00:38

1 Answers1

2

You can get it by calling GetExitCodeProcess (pi.hProcess), after waiting on the handle but before you close it.

The exact code you need is:

DWORD exit_code;
BOOL ok = GetExitCodeProcess (pi.hProcess, &exit_code);

Strange this is not mentioned in the Remarks section for CreateProcess at all.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • Okay, but the function you are suggesting doesn't take 1 argument... It also requires a second: a `lpExitCode` and I'm not sure what to set this too.. – Francis Cugler Aug 05 '20 at 00:27
  • Added the code you need. – Paul Sanders Aug 05 '20 at 00:31
  • Okay I had to create a temporary `DWORD ec` variable and passed that in my reference to the `GetExitCodeProcess()` function, however, I'm getting a value of `1` stored in my variable and I'm expecting it to be `64` as this is what the program returns... – Francis Cugler Aug 05 '20 at 00:31
  • 1
    Is `GetExitCodeProcess` succeeding? – Paul Sanders Aug 05 '20 at 00:32
  • Okay, I think I see what I'm doing wrong... I should be assigning my temp variable to the exit code, not from the return of this function... – Francis Cugler Aug 05 '20 at 00:32
  • Nope, when I return the `ec` I'm getting a value of `259`... It should be `64` – Francis Cugler Aug 05 '20 at 00:35
  • 2
    Are you calling `GetExitCodeProcess` _**after**_ waiting on `pi.hProcess`? Code 259 means 'process still running', IIRC. – Paul Sanders Aug 05 '20 at 00:36
  • Okay I think I see what you are getting at... I have to call that after `WaitForSingleObject()` and not before it... I can try that... – Francis Cugler Aug 05 '20 at 00:37
  • And I made a mistake on my expected output... It was supposed to be 32 as in 32Kb for the l1 cache size, 64 is the cache line size... but It appears to be working now! – Francis Cugler Aug 05 '20 at 00:39
  • Nice I'm able to retrieve values from one executable and import them into another! – Francis Cugler Aug 05 '20 at 00:40
  • 1
    @FrancisCugler note that there are many ways to do that, exit codes are not the only way. Those are good for integers only, but maybe some day you want to return strings instead, or even binary data. Exit codes won't work for those. – Remy Lebeau Aug 05 '20 at 00:41
  • @RemyLebeau I understand that, but for this particular use case and application, the values returned and needed will only ever be integers... – Francis Cugler Aug 05 '20 at 00:49
  • 1
    @FrancisCugler in that case, make sure you pay attention to the warning in the [`GetExitCodeProcess()` documentation](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess) when choosing what kind of integer values to return. – Remy Lebeau Aug 05 '20 at 01:03
  • 1
    @RemyLebeau Those remarks are moot if you wait on the handle first. – Paul Sanders Aug 05 '20 at 01:07
  • @PaulSanders "*...if you wait on the handle...*" - I agree that is the *preferred* way to go, but a lot of people still don't actually do that. I've seen plenty of code where people run `GetExitCodeProcess()` in a loop waiting for the exit code to change from `STILL_ACTIVE` to something else (my own company has been caught doing that in the past). – Remy Lebeau Aug 05 '20 at 01:25