C++ Multithreading CreateProcess. Exe running slower with more threads active - c++

Recently upgraded my dev machine to a i9-13900k (24 cores, 32 threads) as a result I'm looking into refactoring a test harness I utilise, the issue appears to be that there is no improvement in total time taken for tests to complete past 6 threads, which is not expected.
The setup.
std::vector<std::string> tests;
std::ifstream input("");
for (std::string line; getline(input, line); ) {
tests.push_back(line);
}
const int processor_count = 24;
vector<int> thread_pool(processor_count, thread_status::thread_free);
for (int i = 0; i < processor_count; i++) {
thread(ThreadedFunc, ref(thread_pool[i])).detach();
}
auto not_all_done = [&thread_pool]()
{
return any_of(thread_pool.begin(), thread_pool.end(), [](int a_ThreadStatus)
{
return a_ThreadStatus < thread_done;
});
};
while (not_all_done()) {
}
void ThreadedFunc(int& a_ThreadStatus)
{
while (!tests.empty() ) {
// CRITICAL SECTION
std::string l_Test;
{
std::lock_guard<std::mutex> guard(mtx);
l_Test = tests.front();
tests.erase(tests.begin() + 0);
}
// ================
auto start = std::chrono::system_clock::now();
std::time_t start_time = std::chrono::system_clock::to_time_t(start);
std::stringstream msg;
msg << "Test: " << l_Test << " started." << std::endl;
std::cout << msg.str();
auto l_ClassID = std::stoi(l_Test);
std::wstring l_wexe = std::wstring(m_exe.begin(), m_exe.end());
LPCWSTR l_exe = l_wexe.c_str();
std::wstring cmdArgslistSetChannel = l_wexe;
cmdArgslistSetChannel += L" -utest ";
cmdArgslistSetChannel += std::to_wstring(l_ClassID);
cmdArgslistSetChannel += L" cpufactor ";
cmdArgslistSetChannel += std::to_wstring(m_CPUFactor);
cmdArgslistSetChannel += L" ParallelRunning CrashReporting";
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
start = std::chrono::system_clock::now();
CreateProcess(NULL, &cmdArgslistSetChannel[0], NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
auto end = std::chrono::system_clock::now();
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
auto diff_time = std::difftime(end_time, start_time);
std::stringstream msg1;
msg1 << "Test: " << l_Test << " completed. Elapsed: " << diff_time << std::endl;
std::cout << msg1.str();
}
a_ThreadStatus = thread_status::thread_done;
}
It is appearing that past 6 threads the CreateProcess call is taking longer to execute. Would this be expected given the hardware.
Some numbers for 100 tests.
thread
time (mins)
1
?
2
1.07
4
0.71
6
0.68
8
0.72
12
0.79
24
1.07
I was not expecting 2 threads to be on par with 24. We know from test logging the test itself is NOT taking any longer to execute, would it be the 24 CreateProcess calls fighting each other, before the exe launches.
I started writing this in perl as that's out current testing scripting lang, switched to C++ and from a one parent firing off a new thread (fork) for every test to a thread manages its work model, to the same results.

Related

C++ winapi VirtualQueryEx function gives me "000000"

I am trying to display information about the virtual memory of each process on the system:
#include <windows.h>
#include <conio.h>
#include <tlhelp32.h>
#include <iostream>
using namespace std;
void main() {
HANDLE CONST hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
PROCESSENTRY32 proc;
TCHAR Buffer[1024];
TCHAR Buffer2[1024];
DWORD temp;
HANDLE CONST hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnap)
{
return;
}
proc.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnap, &proc);
do {
MEMORY_BASIC_INFORMATION mbi = {};
wsprintf(Buffer, L" %s %d \n ", proc.szExeFile, proc.th32ProcessID);
WriteConsole(hStdOut, Buffer, lstrlen(Buffer), &temp, NULL);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc.th32ProcessID);
VirtualQueryEx(hProcess, 0, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
printf("Alloc = %p, base = %p , size = %d, type = %d, state = %p\n", mbi.AllocationBase, mbi.BaseAddress, mbi.RegionSize, mbi.Type,mbi.State);
} while (Process32Next(hSnap, &proc));
CloseHandle(hSnap);
}
Output looks like this:
output
I get base address and alloc equal to 0000000 and type 0
How do I get normal values? I mean size and state seem to be ok but the rest is "0000000"
I do not know what's the problem
You're currently only retrieving information about the first block of memory for each process. A process will typically have a lot of memory blocks, not just one (thousands to tens of thousands would be pretty typical).
Here's some code that retrieves and prints out the status of each block of data in a specified process.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#include <string>
unsigned long show_module(MEMORY_BASIC_INFORMATION info) {
unsigned long usage = 0;
std::cout << info.BaseAddress << "(" << info.RegionSize / 1024 << ")\t";
switch (info.State) {
case MEM_COMMIT:
std::cout << "Committed";
break;
case MEM_RESERVE:
std::cout << "Reserved";
break;
case MEM_FREE:
std::cout << "Free";
break;
}
std::cout << "\t";
switch (info.Type) {
case MEM_IMAGE:
std::cout << "Code Module";
break;
case MEM_MAPPED:
std::cout << "Mapped ";
break;
case MEM_PRIVATE:
std::cout << "Private ";
}
std::cout << "\t";
int guard = 0, nocache = 0;
if ( info.AllocationProtect & PAGE_NOCACHE)
nocache = 1;
if ( info.AllocationProtect & PAGE_GUARD )
guard = 1;
info.AllocationProtect &= ~(PAGE_GUARD | PAGE_NOCACHE);
if ((info.State == MEM_COMMIT) && (info.AllocationProtect == PAGE_READWRITE || info.AllocationProtect == PAGE_READONLY))
usage += info.RegionSize;
switch (info.AllocationProtect) {
case PAGE_READONLY:
std::cout << "Read Only";
break;
case PAGE_READWRITE:
std::cout << "Read/Write";
break;
case PAGE_WRITECOPY:
std::cout << "Copy on Write";
break;
case PAGE_EXECUTE:
std::cout << "Execute only";
break;
case PAGE_EXECUTE_READ:
std::cout << "Execute/Read";
break;
case PAGE_EXECUTE_READWRITE:
std::cout << "Execute/Read/Write";
break;
case PAGE_EXECUTE_WRITECOPY:
std::cout << "COW Executable";
break;
}
if (guard)
std::cout << "\tguard page";
if (nocache)
std::cout << "\tnon-cacheable";
std::cout << "\n";
return usage;
}
unsigned long show_modules(HANDLE process) {
unsigned long usage = 0;
unsigned char* p = NULL;
MEMORY_BASIC_INFORMATION info;
for ( p = NULL;
VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
p += info.RegionSize )
{
usage += show_module(info);
}
return usage;
}
int main(int argc, char **argv) {
int pid;
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <process ID>\n";
return EXIT_FAILURE;
}
pid = std::stoi(argv[1]);
HANDLE process = OpenProcess(
PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
false,
pid);
unsigned long mem_used = show_modules(process);
std::cout << "Total memory used: " << mem_used / 10224 << "KB\n";
}
To give an idea of the result, here are the first few lines of output from a process on my system:
0000000000000000(64) Free
0000000000010000(64) Committed Mapped Read/Write
0000000000020000(4) Committed Mapped Read Only
0000000000021000(60) Free
0000000000030000(4) Committed Private
0000000000031000(60) Reserved Private
But be aware: you're likely to get a lot more output than that for most typical processes. That particular process (Thunderbird) produced a total of 3,686 lines of output. A quick test with Chrome (using a couple gigabytes of memory) produces over 46,000 lines of output (i.e., over 46,000 separate memory blocks being tracked by the system for it).
If you're going to print something out for every process in the system, you'll probably want to summarize the data quite a bit (but without knowing why you want this, it's hard to guess what sort of result you're likely to want).

CreateProcess stdout with CreateNamedPipe Overlapped

I'm trying to read the std output of an external process (pgdump ) that I started with CreateProcess. I got this working with anonymous pipes but then the output is blocked and no like when I execute it via the commandline( missing end output). I have read numerous posts and discovered that I need CreateNamedPipes with WaitForSingleObject but I can't seem to get it to work. This is my working code with anonymous pipe but blocked and I'm missing the end of the output
#include <QDebug>
#include <QString>
#include <windows.h>
#include <sstream>
#include <iostream>
#include <random>
int main()
{
#define BUFFERSIZE 256
std::string program = "\"C:\\Program Files (x86)\\PostgreSQL\\10\\bin\\pg_dump.exe\"" +
std::string( " --dbname=postgresql://postgresUser:PostGresql13#127.0.0.1:5432/employee -j1 -Fd -b -v -f "
"C:\\development\\myproject\\Build\\debug\\1-export.psql");
HANDLE hReadStdOut = NULL;
HANDLE hWriteStdOut = NULL;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
ZeroMemory( &saAttr, sizeof( SECURITY_ATTRIBUTES ));
ZeroMemory( &piProcInfo, sizeof( PROCESS_INFORMATION ));
ZeroMemory( &siStartInfo, sizeof( STARTUPINFO ));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
OVERLAPPED sOverlap;
if( !CreatePipe(&hReadStdOut, &hWriteStdOut, &saAttr, 0) )
{
std::ostringstream os;
os << GetLastError();
qDebug() << "create pipe error : " << QString::fromStdString( os.str());
}
TCHAR* szCmdline = new TCHAR[ program.size() + 1];
szCmdline[ program.size()] = 0;
std::copy( program.begin(), program.end(), szCmdline );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hWriteStdOut;
siStartInfo.hStdOutput = hWriteStdOut;
siStartInfo.hStdInput = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
BOOL bSuccess = CreateProcess( NULL, szCmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo,&piProcInfo );
if ( ! bSuccess )
{
std::ostringstream os;
os << GetLastError();
qDebug() << "create process error : " << QString::fromStdString( os.str());
}
else
{
CloseHandle( hWriteStdOut );
DWORD err;
DWORD nBytesRead;
char buf[BUFFERSIZE + 1];
int i(1);
for(;;)
{
std::cout << "iteration " << std::to_string( i ) << std::endl;
if( !ReadFile( hReadStdOut, buf, sizeof( buf), &nBytesRead, NULL) || !nBytesRead )
{}
if( GetLastError() == ERROR_SUCCESS )
{
}
std::cout.flush();
buf[nBytesRead] = '\0';
std::string string_ = buf;
std::cout << string_ << std::endl;
std::size_t found = string_.find("contents of");
if( !nBytesRead )
break;
i++;
}
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
return 0;
}
For example, if your pg_dump.exe just write one line to standard output. There are several ways:
std::cout << "Hello World!\n";
printf("Hello World!\n");
std::cout << "Hello World!\n"<< std::endl;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "Hello World!\n", 14, &dwWritten, NULL);
If the pg_dump.exe exit after write the line, the ReadFile will not blocked and return with the line "Hello World!".
However, if the pg_dump.exe doesn't exit after write the line and continue other work. The first two write ways (1., 2.) will result in ReadFile blocked. But if you use the third or fourth way the ReadFile will block and return with the line "Hello World!".
What's the difference here is the first two write ways (std::cout << "Hello World!\n"; and printf("Hello World!\n");) has no flush operation at end of write but the third or fourth way has. std::endl and WriteFile flush the output buffer.
Summary:
Flush the output to cause it to be written to the underlying stream (which may be a file, a terminal or a pipe). Standard output is flushed at the following conditions:
When the program ends normally.
Use std::endl at the end.
Use WriteFile.
You can check if it is your case.

CreateProcess and redirecting output

There are 2 apps.
AppCMD is a command line app and AppMAIN starts AppCMD with some command line args.
Unfortunately AppMAIN does not seem to handle the output off AppCMD very well and something is going wrong.
I'd like to log the calls to AppCMD and its output to see what is going on.
In order to do so I want to replace AppCMD with another binary AppWRAP that forwards the calls to a renamed AppCMD and logs it's output.
AppWRAP should act like a transparent Man-In-The-Middle.
For testing purposes I wrote a simple AppCMD that just outputs it's command line args:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "#### Hello, I'm the test binary that wants to be wrapped." << endl;
if (argc < 2) {
cout << "#### There where no command line arguments." << endl;
}
else {
cout << "#### These are my command line arguments:";
for (int i = 1; i < argc; ++i) cout << " " << argv[i];
cout << endl;
}
cout << "#### That's pretty much everything I do ... yet ;)" << endl;
return 0;
}
I followed MSDN: Creating a Child Process with Redirected Input and Output to implement AppWrap but I got stuck since it does not return and I cant figure out why:
#include <iostream>
#include <sstream>
#include <Windows.h>
using namespace std;
const string TARGET_BINARY("TestBinary.exe");
const size_t BUFFSIZE = 4096;
HANDLE in_read = 0;
HANDLE in_write = 0;
HANDLE out_read = 0;
HANDLE out_write = 0;
int main(int argc, char *argv[])
{
stringstream call;
cout << "Hello, I'm BinTheMiddle." << endl;
//-------------------------- CREATE COMMAND LINE CALL --------------------------
call << TARGET_BINARY;
for (int i = 1; i < argc; ++i) {
call << " " << argv[i];
}
cout << "Attempting to call '" << call.str() << "'" << endl;
//------------------------------ ARRANGE IO PIPES ------------------------------
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.bInheritHandle = NULL;
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;
if (!CreatePipe(&out_read, &out_write, &security, 0)) {
cout << "Error: StdoutRd CreatePipe" << endl;
return -1;
}
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdout SetHandleInformation" << endl;
return -2;
}
if (!CreatePipe(&in_read, &in_write, &security, 0)) {
cout << "Stdin CreatePipe" << endl;
return -3;
}
if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) {
cout << "Stdin SetHandleInformation" << endl;
return -4;
}
//------------------------------ START TARGET APP ------------------------------
STARTUPINFO start;
PROCESS_INFORMATION proc;
ZeroMemory(&start, sizeof(start));
start.cb = sizeof(start);
start.hStdError = out_write;
start.hStdOutput = out_write;
start.hStdInput = in_read;
start.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&proc, sizeof(proc));
// Start the child process.
if (!CreateProcess(NULL, (LPSTR) call.str().c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &start, &proc))
{
cout << "CreateProcess failed (" << GetLastError() << ")" << endl;
return -1;
}
// Wait until child process exits.
WaitForSingleObject(proc.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(proc.hProcess);
CloseHandle(proc.hThread);
//----------------------------------- OUTPUT -----------------------------------
DWORD dwRead;
CHAR chBuf[127];
while (ReadFile(out_read, chBuf, 127, &dwRead, NULL)) {
cout << "Wrapped: " << chBuf << endl;
}
return 0;
}
It seems like it is waiting for ReadFile to return. Can anybody spot what I'm doing wrong?
I call the binary this way:
> shell_cmd_wrapper.exe param1 param2
This is the console output but the binary does not return.
Hello, I'm BinTheMiddle.
Attempting to call 'TestBinary.exe param1 param2'
Wrapped:#### Hello, I'm the test binary that wants to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
Wrapped: pretty much everything I do ... yet ;)
s to be wrapped.
#### These are my command line arguments: param1 param2
#### That'sD
(Please ignore that I don't clear the buffer)
Close the out_write and in_read handles after calling CreateProcess. Otherwise ReadFile on out_read will block when the pipe is empty because there's still a potential writer even after the child has exited -- the out_write handle in the current process.
Also, as noted by Harry Johnston in a comment, waiting for the process to exit before reading from the pipe can potentially cause a deadlock. The child will block on WriteFile if the pipe fills up.

Two way communication between a C++ program and an unidentified program

I would like to create a C++ program that can launch another program and communicate with it like a standard user.
Here is a basic example :
Say, I have program A in any kind of language (say Python for the exampe, but it sould be any kind of program). That program is launch via a console with a specific command (like "./myprogram.exe" or "python ./myprogram.py" or "java ./myprogram.jar"). It wait for the input of the user and give the sum of all the precedent inputs.
Example :
./myprogram.exe
Please enter a numer.
User > 4
4
User > 2
6
User > 9
15
So the program has a memory.
Now, what I want is automatize the user inputs and the output reading within a C++ program B. So my program B will automaticaly send an input, wait for the other program A to give an output before sending another input and so on... Without closing and starting again program A because program A has a memory of inputs.
Note : the program A (which is tested) is non changeable. I just want to benchmark it without modifying it.
Do you know how I can perform such communication ?
Thank you
Thank you for your advices, but I read that pipe(), fork() and so on worked only in UNIX environment, and I work on Windows (sorry for forgetting to say that...).
So after searching a bit, finding documentations and creating some codes, I finally managed to get what I wanted.
WARNING : I give this solution for people who face the same problem as me and want the thing just to work. It may not be safe, it may not be the best solution, use this at your own risk. And remember that it is very specific to windows.
#include "iostream"
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
using namespace std;
void CreateChildProcess(string cmdLine);
void WriteToPipe(string input);
string ReadFromPipe();
void ErrorExit(PTSTR);
int main()
{
SECURITY_ATTRIBUTES saAttr;
cout << "Starting pipes..." << endl;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
cout << "Error : StdoutRd CreatePipe" << endl;
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
cout << "Error : Stdout SetHandleInformation" << endl;
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
cout << "Error : Stdin CreatePipe" << endl;
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
cout << "Error : Stdin SetHandleInformation" << endl;
cout << "Creating child process..." << endl;
CreateChildProcess("python C:/Users/Me/Desktop/Benchmark/test.py");
WriteToPipe("5");
ReadFromPipe();
ReadFromPipe();
getchar();
return 0;
}
void CreateChildProcess(string cmdLine)
{ TCHAR *szCmdline = new TCHAR[cmdLine.size() + 1];
szCmdline[cmdLine.size()] = 0;
std::copy(cmdLine.begin(), cmdLine.end(), szCmdline);
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
{
cout << "Error : CreateProcess" << endl;
}
else
{
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(string input)
{
DWORD dwWritten;
CHAR chBuf[BUFSIZE];
cout << "> " << input.c_str() << endl;
input += "\n";
if (!WriteFile(g_hChildStd_IN_Wr, input.c_str(), strlen(input.c_str()), &dwWritten, NULL))
{
cout << "Error : WriteFile" << endl;
}
}
string ReadFromPipe()
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
string output = "";
bool flag = false;
for (;;)
{
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
for (int i = 0; i < dwRead; i++)
{
if (chBuf[i] == '\n')
{
flag = true;
break;
}
output += chBuf[i];
}
if (flag)
{
break;
}
}
cout << "< " << output.c_str() << endl;
return output;
}
And the program test.py is :
import sys
sys.stdout.write('Loading module\n')
test = int(input())
sys.stdout.write(str(test+1))
sys.stdout.write('\n')
What this will do is send the string "5\n" to test.py and read the output (which is "6"). It will work with any command like java test.jar or python test.py or just test.exe.
void CreateChildProcess(string cmdLine) allow you to create the child process with a specific command line.
void WriteToPipe(string input) allow you to send anything to the child process (a '\n' is automaticaly added)
string ReadFromPipe() is a synchronous function which output a line of the ouput of the child process (the last char '\n' is automaticaly deleted)
This solution is partially based on this well documented code : https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx

Sending Message to other process

for (int i = 0; i < n; i++)
{
const char* cstr = strings[i].c_str();
swprintf_s(fullCommandLine, L"\"%s\" \"%s\" %S", pathToModule, pathToFile, cstr);
if(CreateProcess(NULL,
(LPWSTR)fullCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi))
{
cout << "succes";
}
else cout << "fail";
}
I'm creating n procesess to find string in given file like this, and In my module(wchich looks for given string in file) I want to send messages to other n-1 processes to quit
while (file >> readout)
{
if (readout == search)
{
cout << "I found string";
SendMessage(/*what should be here*/);
}
}
From where I could get handles to those other processes?
Please see my PostThreadMessage to Console Application.
I created that because it certainly is possible to send a message to a console program, we just must make a message loop, just as it is possible to show a window from a console program.
Note that PostThreadMessage needs a thread id, not a process id. Every process also has a thread id and a process's thread id is in the PROCESS_INFORMATION from CreateProcess.
The following is a larger example but easier to use for demonstrating that PostThreadMessage works in console programs. This program will call itself (passing its thread id) if there is no argument for it then it will wait for the new process to send messages. If there is an argument then it will assume the argument is a thread id and send a message to that thread followed by a WM_QUIT.
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szCmdline[300];
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = NULL;
siStartInfo.hStdOutput = NULL;
siStartInfo.hStdInput = NULL;
DWORD dwThread;
MSG Msg;
TCHAR ThreadIdBuffer[40];
// if no argument then execute ourself then wait for a message from that thread
if (argc == 1) {
_itot_s(GetCurrentThreadId(), ThreadIdBuffer, 40, 10);
szCmdline[0] = '"';
szCmdline[1] = 0;
_tcscat_s(szCmdline, 300, argv[0]); // ourself
int n = _tcslen(szCmdline);
szCmdline[n++] = '"';
szCmdline[n++] = ' ';
szCmdline[n++] = 0;
_tcscat_s(szCmdline, 300, ThreadIdBuffer); // our thread id
bSuccess = CreateProcess(argv[0], // execute ourself
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!bSuccess) {
std::cout << "Process not started\n";
return 0;
}
std::cout << "Waiting\n";
// Now wait for the other process to send us a message
while (GetMessage(&Msg, NULL, 0, WM_USER)) {
if (Msg.message == WM_COMMAND)
std::cout << "WM_COMMAND\n";
else
std::cout << "Message: " << Msg.message << '\n';
}
std::cout << "End of message loop\n";
return 0;
}
// if there is an argument then assume it is a threadid of another one of us
std::cout << "Press Enter to send the message\n";
if (std::wcin.get() != '\n')
return 0;
dwThread = _wtoi(argv[1]);
if (!PostThreadMessage(dwThread, WM_COMMAND, (WPARAM)0, (LPARAM)0))
std::cout << GetLastError() << " PostThreadMessage error\n";
if (!PostThreadMessage(dwThread, WM_QUIT, (WPARAM)0, (LPARAM)0))
std::cout << GetLastError() << " PostThreadMessage error\n";
return 0;
}