How to pass message to child process through named pipes in Windows - c++

I know how to do this through an anonymous pipe, just create an anonymous pipe and before executing createprocess(), set the startupInfo as follow:
startupInfo.hStdInput = hstdinRead;//hstdinRead is the handle of the pipe
startupInfo.hStdOutput = hstdoutWrite;
But can I do the same when I use a named pipe?
I have tried to create a named pipe through CreateNamedPipeA function and do similar above, but things don't seem to work at all.
Should I do some pre-settings or is it just impossible to communicate between a process and it's child process through a named pipe?
And : My child process is an unchangable .exe, so I must do all these things before createprocess() function in the parent process.
20180804: thanks for your comments, I am still polishing my English, and my codes using named pipes are like these.
First I create two named pipes and I have confirmed they succeed:
inFileWrite = CreateNamedPipeA("\\\\.\\pipe\\cmder_in", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 10, 4096, 4096, 0, &saAttr);//for writting to the child process
outFileRead = CreateNamedPipeA("\\\\.\\pipe\\cmder_out", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 10, 4096, 4096, 0, &saAttr);//for reading from the child process
Then I try to connect to these pipes in the same process(I am a bit confused about this step)
inFileRead = CreateFileA("\\\\.\\pipe\\cmder_in", GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, &saAttr)
outFileWrite = CreateFileA("\\\\.\\pipe\\cmder_out", GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, &saAttr);
After some other init steps, I create a STARTUPINFO struct for childprocess, in which I try to redirect its std input/output:
startupInfo.hStdInput = inFileRead;
startupInfo.hStdOutput = outFileWrite;
Finally it's the createprocess:
char cmdLine[] = { "cmd" };
CreateProcess(NULL,
cmdLine,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startupInfo,
&procInfo);
These all codes seem to work well, but when I write an command like "cd .." and try to read the result from childprocess through the pipe, it reads nothing. I think the problem is I didn't redirect childprocess's stdin/out successfully.
I succeeded in doing similar things by using an anonymous pipe or named pipes in Linux. I am new to windows programming, and this problem has stuck me for a long time.

Related

Is there a method to get hStdOutput into an string buffer?

I've successfully made an handle that gets the output from an external program and then exports out to a file. I want to do this with an stringstream or equivalent instead. Here's a code snippet to show what I want to do
//std::string OUTPATH = _FilePath + L"out";
std::string filestream{};
//HANDLE h = CreateFileA(LuaBuffer.data(),
// FILE_APPEND_DATA,
// FILE_SHARE_WRITE | FILE_SHARE_READ,
// &sa,
// CREATE_ALWAYS,
// FILE_ATTRIBUTE_NORMAL,
// NULL);
si.hStdOutput = filestream.data();
si.dwFlags |= STARTF_USESTDHANDLES;
If this is impossible, then what is the fastest approach to getting an output from an external console program? Many thanks.
You cannot use a std::string/stream with the STARTUPINFO::hStdOut field. What you CAN do is create a pipe via CreatePipe() instead, and then your app can read from the pipe and append the data to your std::string/stream.
See MSDN's documentation for an example of using CreatePipe() with CreateProcess() to capture a child process's output.
Creating a Child Process with Redirected Input and Output
Simply change the code to write the captured output to your std::string/stream instead of to the parent process's stdout.

MiniDumpWriteDump() into a named pipe then ReadFile()

What I would like to do is use MiniDumpWriteDump() write to a named pipe and then read/write it myself. I am able to perform the dump successfully if I write the contents to a file directly. However, while writing to the named pipe has been successful the subsequent read/write operation does not go so well. I can read all the data out of the pipe but then when it's written the DMP file appears to be corrupted.
Here is the ReadFile() logic:
while (ReadFile(hInboundPipe, &vecBuffer[dwOffset], vecBuffer.size() - dwOffset, &dwRead, NULL)) {
dwOffset += dwRead;
while (dwOffset >= vecBuffer.size()) {
vecBuffer.resize(vecBuffer.size() + iBuffer * sizeof(char));
}
}
Here is the WriteFile() logic:
HANDLE hDumpFile = CreateFileW(L"C:\\test.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hDumpFile , &vecBuffer[0], dwOffset, &dwOutBytes, NULL);
CloseHandle(hDumpFile);
I'm not certain if it's applicable to the root cause but here is the named pipe setup:
HANDLE hInboundPipe = CreateNamedPipe(
szPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_WAIT | PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
0,
0,
(DWORD)-1,
&SecAttrib);
There are not any errors being reported back from GetLastError(). Am I missing something obvious?
EDIT: Adding how the MiniDumpWriteDump() is being done in response to a comment.
HANDLE hDump = CreateFile(szPipeName, GENERIC_ALL, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
MiniDumpWriteDump(hProcess, pid, hDump, mdValue, NULL, NULL, NULL);
CloseHandle(hDump);
UPDATE:
I was under the impression that reading by chunks was somehow dropping data. To test this I increased the buffer of the named pipe to accommodate the entire dump without any resizing. I also increased the vecBuffer size to match. Now when performing the ReadFile() operation I receive the entire dump but it is still off. I'm still playing with various named pipe settings trying to figure out what needs to be done to get MiniDumpWriteDump() to provide valid output to a named pipe.
It appears this cannot be done. See the comments for more information. Writing directly to a named pipe from MiniDumpWriteDump() cannot be done because the handle that is passed in must have the ability to seek. Named pipes do not have that functionality, therefore you must use a legitimate file handle.

Redirecting child process input / output using named pipes

On the MSDN documentation, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx a sample is shown using anonymous pipes.
In the description can be read:
"...Note that named pipes can also be used to redirect process I/O."
Now, I am struggling to make this happen
For stdin, I do something like
serverPipe = CreateNamedPipe(
szPipeName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
sizeof(TCHAR) * BUFSIZE,
sizeof(TCHAR) * BUFSIZE,
PIPE_TIMEOUT,
lpSa ); //lpSa has its inherited attribute set, just like in the msdn sample.
clientPipe = CreateFile(szPipeName, GENERIC_READ | GENERIC_WRITE, 0, lpSa, OPEN_EXISTING, 0, NULL);
Both pipes create fine, later I close the server (parent process) pipe
CloseHandle(serverPipe);
So the call to ConnectNamedPipe does not return error "there is already a process at the other end of the pipe".
Here is the code for ConnectNamedPipe
do
{
bSuccess = ConnectNamedPipe(serverPipe, &ol);
if (!bSuccess)
{
DWORD dwCode = GetLastError();
if (dwCode == ERROR_NO_DATA) continue;
if (dwCode == ERROR_PIPE_CONNECTED) break;
report_error(NULL);
}
} while (!bSuccess);
The problem is that it always returns error with ERROR_NO_DATA (The pipe is being closed). So never finished the loop.
NOTE: I need to inherit to the child process the handles to the pipes already opened, because I dont have the source of the children and thus cannot change it.
So, can someone point the right way to do this? or the a full working sample using named pipes for IO redirection for a child process using Win32 API?
PS. The reason I need named pipes is because I want to use Overlapped IO, which doesnt seem to be configurable in anonymous pipes.

What is the easiest way to send text from a child GUI process to a console parent?

I've coded two processes using C++. One is the GUI process that is called by my console app using CreateProcess API. I need to pass text from the GUI app (child) to the console app (parent.) The amount of text could be arbitrary -- from a few lines to KBs of text.
What is the easiest way to do it?
PS. I have access to the source code of both processes.
Console application can create a WinAPI window (non-visible), such that it can receive messages (idea taken from AllocateHWND function in Delphi).
Another solution is to use named pipes.
Another solution is to send data locally via TCP/IP.
If these strings are only a debug ones, consider using OutputDebugString function from WinAPI and capturing them with a program like SysInternals' DbgView.
If the GUI application is truly graphical only, you don't really use the standard output stream (i.e. std::cout). This can the be reused for output to your console application.
First you need to create an anonymous pipe using CreatePipe:
HANDLE hPipeRead;
HANDLE hPipeWrite;
CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0);
Now you have to handles that can be used as a normal file handle; One to read from and the other to write to. The write-handle should be set as the standard output for the new process you create:
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = hPipeWrite; // Standard output of the new process
// is set to the write end of the pipe
CreateProcess(
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&startupInfo, // Use our startup information
&processInfo);
Now whenever the child process needs to write to the parent, it only have to use standard output:
std::cout << "In child process, are you getting this parent?";
The parent uses ReadFile to read from the read-end of the pipe:
char buffer[256];
DWORD bytesRead = 0;
ReadFile(hPipeRead, buffer, sizeof(buffer), &bytesRead, NULL);
Note: I haven't done WIN32 programming in some time, so might be wrong on some details. But should hopefully be enough to get you started.
There are of course many other ways if Inter Process Communications (IPC), including (but not limited to) sockets, files, shared memory, etc.
The easy way is probably to make the child actually a console application, even though it also creates windows.
In that case, you can have your parent spawn the child using _popen, and the child can just write the output to its normal stdout/std::cout. _popen returns a FILE *, so the parent can read the child's output about like it'd normally read a file (well, normally for C anyway).
Various methods can be used, some of them were given above. Which one is simplest depends on you task.
I can also suggest you filemapping technics which is widely used in IPC, and for ex. dll are implemented using filemapping.
It allows mutliply processes to share the same resources simultaniously, access is random not consequntial.
Here are main steps of implementation:
1. Process A creates a file;
2. Process A creates a named system object mappedfile to the file (mappedfile allocates memory);
3. Process A creates a system object a viewOfMapped file(this allows to map some area of the process A to the pages in the main memory which were allocated by mappedFile);
4. Process B creates the named system object mappedfile(name should be similar to the one of process A used), viewOfMapped file;
5. By pointers returned by viewOfMapped processes can share the same memory.
Example:
Process A:
/* 1. file creation */
hFile = CreateFile(0, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
Process B:
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
This method is rather simple and also powerfull.

Memory mapping physical disks and volumes

In Windows it's possible to open devices and volumes via CreateFile(). I've used this successfully before to ReadFile() from devices, but now I want to switch to memory-mapping. In the following code, I receive INVALID_HANDLE_VALUE for the value of b, and c is set to 87, ERROR_INVALID_PARAMETER.
HANDLE a = ::CreateFileA("\\\\.\\h:", GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_FLAG_NO_BUFFERING, NULL);
HANDLE b = ::CreateFileMappingA(a, NULL, PAGE_READONLY, 0, 0, NULL);
DWORD c = ::GetLastError();
How can I get this to work?
You can't. CreateFileMapping can only create a mapping to a file. Take a look at the difference in the documentation between the hFile parameter for ReadFile and for CreateFileMapping. For ReadFile it lists all the different types of handles it accepts (which includes devices), for CreateFileMapping it only lists files.