Click here to Skip to main content
15,885,914 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
I tried to write a small POC (Win32 application) that will invoke "calc.exe" using CreateProcess() and periodically obtain the status (is it still running or was it closed) using GetExitCodeProcess().

My code is "quick and dirty" only for the purpose of finding out how to use GetExitCodeProcess(), or whether I should use another method, or maybe calc.exe isn't the ideal process for such POC. That being said, I need to use CMD and am looking for a solution that will allow me to monitor a process that encapsulates CMD in it, as in my article about that[^] which I am hoping to improve.

I was expecting to receive a different status when I shut down calc.exe but I always receive status 0.

What I have tried:

C++
// MonitorProcess.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "MonitorProcess.h"

#define MAX_LOADSTRING 100

#define BUFSIZE MAX_PATH
#define IDT_TIMER1 1000

PROCESS_INFORMATION pi;

BOOL DoRun(WCHAR *command)
{
	BOOL Result = FALSE;
	DWORD retSize;
	LPTSTR pTemp = NULL;
	TCHAR Command[BUFSIZE] = L"";
	_tcscpy_s(Command, L"C:\\Windows\\system32\\cmd.exe /C ");
	_tcscat_s(Command, command);

	STARTUPINFO si;
	SecureZeroMemory(&si, sizeof(STARTUPINFO));

	si.cb = sizeof(STARTUPINFO);

	Result = CreateProcess(
		NULL,
		Command,
		NULL,
		NULL,
		FALSE,
		CREATE_NO_WINDOW,
		NULL,
		NULL,
		&si,
		&pi);

	if (Result == FALSE)
	{
		retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_ARGUMENT_ARRAY,
			NULL,
			GetLastError(),
			LANG_NEUTRAL,
			(LPTSTR)&pTemp,
			0,
			NULL);
		MessageBox(NULL, pTemp, L"Error", MB_OK);
		if (retSize)
		{
			LocalFree(pTemp);
		}
	}
	return Result;
}

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MONITORPROCESS, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
	hInst = hInstance; // Store instance handle in our global variable

	HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

	if (!hWnd)
	{
		return FALSE;
	}

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);


	DoRun((WCHAR *)L"calc.exe");
	SetTimer(hWnd,             // handle to main window 
		IDT_TIMER1,            // timer identifier 
		10000,                 // 10-second interval 
		(TIMERPROC)NULL);     // no timer callback 
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MONITORPROCESS));

    MSG msg;
	DWORD state;


    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
	// note: this is the hProcess returned from OpenProcess


    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MONITORPROCESS));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MONITORPROCESS);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	DWORD exitCode = 0;
	BOOL success = FALSE;
    switch (message)
    {
		case WM_TIMER:
		{
			switch (wParam)
			{
			case IDT_TIMER1:
				success = GetExitCodeProcess(pi.hProcess, &exitCode);
				break;
			default:
				break;
			}
		}
		wchar_t Message[80];
		swprintf(Message,80,L"Exit code = %d success = %d", exitCode, success);
		MessageBox(NULL, Message, L"", MB_OK);
		break;

		case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
		
		case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
		
		case WM_DESTROY:
        PostQuitMessage(0);
        break;
		
		default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
Posted
Updated 16-Aug-19 0:56am
v3

If you try another command invoked by CDM, especially one that takes time, like ipconfig, it might work.

When you run calc.exe its like typing

C++
start calc.exe

in CMD and calc.exe is NOT the process you start with CreateProcess()
 
Share this answer
 
v3
Comments
Michael Haephrati 16-Aug-19 7:31am    
I agree that when an external program like calc.exe is started by CMD, we need to externally locate the process ID of the new process (calc.exe) and monitor it
It appears that cmd is started so you actually get its exit code. If you want to test how you obtain the exit code from a process you should probably make a custom process of your own that will return what ever exit code you want it to return. You could have it prompt you for the exit code if you wanted to.

BTW - you can also call WaitForSingleObject and pass it the handle from the process information structure and it will be signalled when the process exits. You can do this with the handle to threads that are started with CreateThread also.
 
Share this answer
 
Comments
Michael Haephrati 15-Aug-19 11:08am    
In fact, I need to monitor a process that was invoked by CMD. There must be a way to do so. WaitForSingleObject will require a separate thread otherwise I won't be able to periodically check the status, like I do now, with the WM_TIMER event.
Rick York 15-Aug-19 12:11pm    
WaitForSingleObject takes a wait time parameter which can be zero so you can call it in your timer event handler if you want to and it will not wait if the time is zero.

I would write a custom process to test this with because I don't know if cmd passes back the exit code of the process it starts or not.

Does this process really have to be started with cmd? You can't just create your own console and start the process on it?
[no name] 16-Aug-19 6:55am    
If you try another command invoked by CDM, especially one that takes time, like ‘ipconfig’, it might work. When you run calc.exe its like typing ‘start calc.exe’ in CMD and calc.exe is NOT the process you start with CreateProcess
You should caedully read the documentation of CreateProcess to understand the details. Take also a look at the linked example.
 
Share this answer
 
Comments
Michael Haephrati 15-Aug-19 13:40pm    
Can you be more specific? The linked example won't help as long as I wish to check the status of the process periodically

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900