I have written a function that attempts to read a child process's command line output via a pipe. This should be a simple subset of the MSDN Creating a Child Process with Redirected Input and Output article, but I am clearly making an error of some sort.
The ReadFile(...) call below blocks forever no matter if I place it before or after the WaitForSingleObject(...) call that should signal the end of the child process.
I have read all the answers that suggest "Use asynchronous ReadFile" and I am open to that suggestion if someone could give me some idea how that is accomplished on a pipe. Although I don't understand why asynchronous I/O should be needed for this case.
#include "stdafx.h"
#include <string>
#include <windows.h>
unsigned int launch( const std::string & cmdline );
int _tmain(int argc, _TCHAR* argv[])
{
launch( std::string("C:/windows/system32/help.exe") );
return 0;
}
void print_error( unsigned int err )
{
char* msg = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msg,
0, NULL );
std::cout << "------ Begin Error Msg ------" << std::endl;
std::cout << msg << std::endl;
std::cout << "------ End Error Msg ------" << std::endl;
LocalFree( msg );
}
unsigned int launch( const std::string & cmdline )
{
TCHAR cl[_MAX_PATH*sizeof(TCHAR)];
memset( cl, 0, sizeof(cl) );
cmdline.copy( cl, (_MAX_PATH*sizeof(TCHAR)) - 1);
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
SECURITY_ATTRIBUTES saAttr;
memset( &saAttr, 0, sizeof(saAttr) );
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000) )
throw std::runtime_error( "StdoutRd CreatePipe" );
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0) )
throw std::runtime_error( "Stdout SetHandleInformation" );
STARTUPINFO startupInfo;
memset( &startupInfo, 0, sizeof(startupInfo) );
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
char* rawEnvVars = GetEnvironmentStringsA();
//__asm _emit 0xcc;
PROCESS_INFORMATION processInfo;
memset( &processInfo, 0, sizeof(processInfo) );
std::cout << "Start [" << cmdline << "]" << std::endl;
if ( CreateProcessA( 0, &cl[0], 0, 0, false,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
rawEnvVars, 0, &startupInfo, &processInfo ) )
{
//CloseHandle( stdoutWriteHandle );
DWORD wordsRead;
char tBuf[257] = {'\0'};
bool success = true;
std::string outBuf("");
unsigned int t;
while(success) {
//__asm _emit 0xcc;
std::cout << "Just before ReadFile(...)" << std::endl;
success = ReadFile( stdoutReadHandle, tBuf, 256, &wordsRead, NULL);
(t=GetLastError())?print_error(t):t=t;
std::cout << "Just after ReadFile(...) | read " << wordsRead<< std::endl;
std::cout << ".";
if( success == false ) break;
outBuf += tBuf;
tBuf[0] = '\0';
}
std::cout << "output = [" << outBuf << "]" << std::endl;
if ( WaitForSingleObject( processInfo.hProcess, INFINITE ) == WAIT_OBJECT_0 )
{
unsigned int exitcode = 0;
GetExitCodeProcess( processInfo.hProcess, (LPDWORD)&exitcode );
std::cout << "exitcode = [" << exitcode << "]" << std::endl;
//__asm _emit 0xcc;
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return exitcode;
}
}
else
{
DWORD procErr = GetLastError();
std::cout << "FAILED TO CREATE PROCESS!" << std::endl;
print_error( procErr );
}
return -1;
} // end launch()
There are a few bugs in your code, but the most important is that you've specified FALSE for the bInheritHandles argument to CreateProcess. The new process can't use the pipe if it doesn't inherit the handle to it. In order for a handle to be inherited, the bInheritHandles argument must be TRUE and the handle must have inheritance enabled.
Other issues:
You're specifying CREATE_UNICODE_ENVIRONMENT but passing an ANSI environment block. Note that it is easier to pass NULL for lpEnvironment and let the system copy the environment block for you. You should still specify CREATE_UNICODE_ENVIRONMENT in this case, as described in the documentation, because your environment block might contain Unicode characters.
Similarly, if you're calling CreateProcessA you should be using STARTUPINFOA.
You don't zero-terminate tBuf each time around the loop, so you'll get spurious extra characters in your output buffer.
You need to close stdoutWriteHandle before you enter your read loop, or you won't know when the subprocess exits. (Or you could use asynchronous IO and check for process exit explicitly.)
GetLastError() is undefined if an API function succeeds, so you should only be calling it if ReadFile returns FALSE. (Of course, in this case this is purely cosmetic since you aren't acting on the error code.)
For reference, here is my corrected version of your code. I've turned it into plain C (sorry!) because that's what I'm familiar with. I compiled and tested in Unicode mode, but I think it should work without modification in ANSI mode too.
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <windows.h>
#include <stdio.h>
void launch(const char * cmdline_in)
{
PROCESS_INFORMATION processInfo;
STARTUPINFOA startupInfo;
SECURITY_ATTRIBUTES saAttr;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
char cmdline[256];
char outbuf[32768];
DWORD bytes_read;
char tBuf[257];
DWORD exitcode;
strcpy_s(cmdline, sizeof(cmdline), cmdline_in);
memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000))
{
printf("CreatePipe: %u\n", GetLastError());
return;
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
{
printf("SetHandleInformation: %u\n", GetLastError());
return;
}
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// memset(&processInfo, 0, sizeof(processInfo)); // Not actually necessary
printf("Starting.\n");
if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, NULL, 0, &startupInfo, &processInfo))
{
printf("CreateProcessA: %u\n", GetLastError());
return;
}
CloseHandle(stdoutWriteHandle);
strcpy_s(outbuf, sizeof(outbuf), "");
for (;;) {
printf("Just before ReadFile(...)\n");
if (!ReadFile(stdoutReadHandle, tBuf, 256, &bytes_read, NULL))
{
printf("ReadFile: %u\n", GetLastError());
break;
}
printf("Just after ReadFile, read %u byte(s)\n", bytes_read);
if (bytes_read > 0)
{
tBuf[bytes_read] = '\0';
strcat_s(outbuf, sizeof(outbuf), tBuf);
}
}
printf("Output: %s\n", outbuf);
if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
{
printf("WaitForSingleObject: %u\n", GetLastError());
return;
}
if (!GetExitCodeProcess(processInfo.hProcess, &exitcode))
{
printf("GetExitCodeProcess: %u\n", GetLastError());
return;
}
printf("Exit code: %u\n", exitcode);
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return;
}
int main(int argc, char** argv)
{
launch("C:\\windows\\system32\\help.exe");
return 0;
}
There is an "LPOVERLAPPED lpOverlapped" parameter to ReadFile() which you have set to NULL. Looks like the only way to go is to allow overlapped I/O on your pipe and then use the WaitForSingleObject() for the "overlapped.hEvent".
Another way is to use the ConnectNamedPipe function and create the OVERLAPPED struct for the pipe.
Related
I'm trying to do something like echo {a:1} | prettier --stdin-filepath index.js and return the value returned in stdout.
But I also want to make this program platform independant.
Referencing this and this, I managed to write code like this:
#include <Windows.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <stdexcept>
HANDLE hChildStd_IN_Rd = NULL;
HANDLE hChildStd_IN_Wr = NULL;
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
int main() {
const char source_code[] = "{a:1}";
FILE *fd_OUT = nullptr, *fd_IN = nullptr;
#ifdef _WIN32
try {
SECURITY_ATTRIBUTES saAttr{};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))
throw std::runtime_error("StdoutRd CreatePipe");
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
throw std::runtime_error("Stdout SetHandleInformation");
if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0))
throw std::runtime_error("Stdin CreatePipe");
if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
throw std::runtime_error("Stdin SetHandleInformation");
TCHAR szCmdline[] = TEXT(
"C:\\Users\\yuto0214w\\AppData\\Roaming\\npm\\prettier.cmd "
"--stdin-filepath index.js");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_OUT_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.hStdInput = hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL,
&siStartInfo, &piProcInfo))
throw std::runtime_error("CreateProcess");
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(hChildStd_OUT_Wr);
CloseHandle(hChildStd_IN_Rd);
fd_OUT = _fdopen(_open_osfhandle((intptr_t)hChildStd_OUT_Rd, _O_RDONLY), "r");
fd_IN = _fdopen(_open_osfhandle((intptr_t)hChildStd_IN_Wr, _O_WRONLY), "w");
} catch (std::exception& e) {
std::cout << std::string("Encountered an error when running: ") + e.what()
<< std::endl;
return 1;
}
#else
// pipe fork dup2 execlp fdopen
#endif
fputs(source_code, fd_IN);
std::string formatted_code;
int c;
while ((c = fgetc(fd_OUT)) != EOF) {
formatted_code += c;
std::cout << "ch: " << c << std::endl; // For testing
}
fclose(fd_OUT);
fclose(fd_IN);
std::cout << formatted_code << std::endl;
return 0;
}
This will compile, but stops completely on fgetc. I mean stops completely by it doesn't show any ch:.
I built program using MSVC v143.
Thanks
The problem was I wasn't closing fd_IN before reading fd_OUT.
This causes deadlock because prettier is awaiting input from program, and program is awaiting output from prettier.
To fix this,
fputs(source_code, fd_IN);
fclose(fd_IN);
std::string formatted_code;
int c;
while ((c = fgetc(fd_OUT)) != EOF) {
formatted_code += c;
std::cout << "ch: " << c << std::endl; // For testing
}
fclose(fd_OUT);
Greetings StackOverflow comrades. Last time I inquired about environment variables. Thanks to Remy for informing me.
Thanks to him I completed my Process class. Now the real problem was connecting to and communicating with MariaDb. I successfully launched MariaDb; but for some reason, reading from MariaDb deadlocks my program. I know before hand that, once connected to MariaDb using, mysql --user=root, MariaDb writes MariaDb[NONE]> to the console. And expects an SQL query input. But I my application deadlocks when trying to read.
I am wondering if MariaDb is using the handles I passed it in CreateProcess StartUpInfo. I did some search on google and found a library on MariaDb website which allows C/C++ programs to connect to MariaDb. So probably they are coercing us to use there library to connect to MariaDb.
Edit:
#Secumen I am trying to communicate with MariaDb via win32 CreateProcess; you know that popular database program? I am using the one shipped with Xampp software.
I want to be able to automate the tasks of adding tables, data, users, etc.
I created the pipes with CreatePipe(...). Then I launched MariaDb using CreateProcess(...). The second argument to CreateProcess was the command line, mysql --user=root. Note that Xampp calls MariaDb MySql. Now I am connected to MariaDb and expect it to write MariaDb[NONE]> to the console. Which means that I should have data to read via ReadFile(...). However ReadFile deadlocks and PeekNamedFile shows that there was zero bytes available to be read.
How the heck then would I communicate with MariaDb if it is not writing to the handles I passed it in CreateProcess?
Edit - Minimal Example
SECURITY_ATTRIBUTES sa = {};
sa.bInheritHandle = true;
sa.lpSecurityDescriptor =NULL;
sa.nLength = sizeof(sa);
HANDLE r,w;
HANDLE r1,w1;
if(!CreatePipe(&r,&w,&sa,0)) throw "Failed to create pipe\n";
if(!CreatePipe(&r1,&w1,&sa,0)) throw "Failed to create pipe\n";
auto cmd = "MYSQL --user=root";
auto current_dir = R"(C:\Program Files\xampp\mysql\bin)";
SetCurrentDirectoryA(current_dir);
STARTUPINFOA si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = w;
si.hStdOutput = w;
si.hStdInput = r1;
if(!CreateProcessA(NULL,cmd,NULL,NULL,true,0,NULL,NULL,&si,&pi))
throw "Failed to create process";
CloseHandle(w);
CloseHandle(r1);
{
DWORD sz, avail;
char *buf = new char[1024];
PeekNamedPipe(r,NULL,0,NULL,&avail,NULL);
printf("available %i",avail);
ReadFile(r,buf,1023,&sz,NULL);
buf[sz] = 0;
printf("%s",buf);
delete[] buf;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
I have written the following code by referring to MSDN. I am using visual studio 2017 and test with win32 application.
I have passed several SQL statements through PIPE for testing, and confirmed that the results were exactly obtained through PIPE.
#include <string>
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hChildOutRd = NULL;
HANDLE hChildOutWr = NULL;
HANDLE hChildInRd = NULL;
HANDLE hChildInWr = NULL;
//. Internal functions.
int CreatePipes();
int CreateChildProcess();
int PipeIO(string & request, string & response);
int main()
{
if (CreatePipes() != ERROR_SUCCESS)
{
cout << "Failed to create pipe. error: " << GetLastError() << endl;
return -1;
}
//. Create the child process.
if (CreateChildProcess() != ERROR_SUCCESS)
{
cout << "Failed to create child process. error: " << GetLastError() << endl;
return -2;
}
//. Write and Read.
string request, response;
request = "use test_db; select count(*) from test_table;";
PipeIO(request, response);
cout << "[Request]: " << request << "\n[Response]: \n" << response << endl << endl;
return 0;
}
int CreatePipes()
{
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
//. Create a pipe for the child process's output.
if (!CreatePipe(&hChildOutRd, &hChildOutWr, &sa, 0))
{
return -1;
}
if (!SetHandleInformation(hChildOutRd, HANDLE_FLAG_INHERIT, 0))
{
return -2;
}
//. Create a pipe for the child process's input.
if (!CreatePipe(&hChildInRd, &hChildInWr, &sa, 0))
{
return -3;
}
if (!SetHandleInformation(hChildInWr, HANDLE_FLAG_INHERIT, 0))
{
return -4;
}
return ERROR_SUCCESS;
}
int CreateChildProcess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.hStdError = hChildOutWr;
si.hStdOutput = hChildOutWr;
si.hStdInput = hChildInRd;
si.dwFlags |= STARTF_USESTDHANDLES;
wchar_t cmd[] = L" -uroot -ppassword";
BOOL bRet = CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!bRet)
{
return -5;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hChildInRd);
CloseHandle(hChildOutWr);
}
return ERROR_SUCCESS;
}
int PipeIO(string & request, string & response)
{
int nRet = ERROR_SUCCESS;
DWORD dwRead = 0, dwWrite = 0;
response.clear();
if (!WriteFile(hChildInWr, request.c_str(), request.length(), &dwWrite, NULL))
{
cout << "ERROR: failed to write pipe. error: " << GetLastError() << endl;
return -1;
}
CloseHandle(hChildInWr);
while (true)
{
char buffer[1024] = { 0 };
if (!ReadFile(hChildOutRd, buffer, 1024, &dwRead, NULL) || dwRead == 0)
{
break;
}
response += buffer;
}
CloseHandle(hChildOutRd);
return ERROR_SUCCESS;
}
Then, you can do this asynchronously.
I referred to RbMm's answer at this article.
#include <malloc.h>
#include <windows.h>
#include <winternl.h>
#include <array>
#include <string>
#include <iostream>
typedef ULONG(__stdcall *RTLNTSTATUSTODOSERROR)(NTSTATUS);
RTLNTSTATUSTODOSERROR pRtlNtStatusToDosError = NULL;
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
ULONG Create(HANDLE hFile);
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL is_ok)
{
if (is_ok)
{
OnIoComplete(NOERROR, InternalHigh);
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer) - 1, 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
_buffer[dwNumberOfBytesTransfered] = 0;
std::cout << _buffer;
}
Read();
}
break;
case write:
break;
}
Release();
}
static VOID WINAPI _OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(pRtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
int main()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
pRtlNtStatusToDosError = (RTLNTSTATUSTODOSERROR)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError");
HANDLE hFile = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_READ_DATA | FILE_WRITE_DATA | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return -1;
}
IO_COUNT obj;
if (obj.Create(hFile) != NOERROR)
{
CloseHandle(hFile);
return -2;
}
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si) };
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFile(name, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError == INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return -3;
}
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR param[] = L" -uroot -ppassword";
if (!CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", param, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(hFile);
return -4;
}
//. Close unneeded handles.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(si.hStdError);
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
std::array<std::string, 5> commands = {
"show databases;\n",
"use test_db;\n",
"select count(*) from test_table;\n",
"select * from test_table;\n",
"exit\n"
};
for (auto & iter : commands)
{
if (p = new U_IRP(&obj))
{
p->Write(iter.c_str(), iter.length());
p->Release();
}
}
obj.Wait();
CloseHandle(hFile);
DisconnectNamedPipe(hFile);
return 0;
}
I'm attempting to make a remote administrator tool, so I can control my home computer, and I have the server working. I can send commands across the network fine, but I'm having trouble executing them in the cmd. I have tried to use the CreateProcess() function to start the cmd and then write commands through a pipe, and read the result. I would like to do this multiple times without closing the cmd, so that I can use cd, etc.
It seems like it is at least partially working because it prints out the welcome message for the cmd when the startCmd() function is called. After this however, when I try to write commands to the cmd it never gives me any output. When I check the out pipe, it says that it has read 0 bytes, except when it first starts.
Does this mean that I can only execute 1 command, or do I need to manipulate the pipes in some way after using them once, or something else like that? Also, I apologize if the code is sloppy, I have just been trying a bunch of different solutions and I havent been worrying about the cleanliness of my code.
#define BUFSIZE 4096
#define PATHMAX 400
bool running = false;
HANDLE hChildStdInR = NULL;
HANDLE hChildStdInW = NULL;
HANDLE hChildStdOutR = NULL;
HANDLE hChildStdOutW = NULL;
PROCESS_INFORMATION piProcInfo;
void ErrorExit(const char*);
bool startCmd()
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStdOutR, &hChildStdOutW, &saAttr, 0))
ErrorExit("StdoutRd CreatePipe");
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(hChildStdOutR, HANDLE_FLAG_INHERIT, 0))
ErrorExit("Stdout SetHandleInformation");
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&hChildStdInR, &hChildStdInW, &saAttr, 0))
ErrorExit("Stdin CreatePipe");
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(hChildStdInW, HANDLE_FLAG_INHERIT, 0))
ErrorExit("Stdin SetHandleInformation");
char cmdPath[PATHMAX];
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure. This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStdOutW;
siStartInfo.hStdOutput = hChildStdOutW;
siStartInfo.hStdInput = hChildStdInR;
siStartInfo.wShowWindow = SW_HIDE;
siStartInfo.dwFlags |= STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
GetEnvironmentVariableA("ComSpec", cmdPath, sizeof(cmdPath));
// Create the child process.
bSuccess = CreateProcess(
cmdPath,
NULL, // command line (NULL because application itsself is cmd)
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
CREATE_NEW_CONSOLE, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// Close un-needed pipes
/*CloseHandle(hChildStdOutW);
CloseHandle(hChildStdInR);*/ // Doesn't change anything why I uncomment these lines
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit("CreateProcess");
else
{
// Close handles to the child process and its primary thread. Some applications might keep these handles to monitor the status of the child process, for example.
/*CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);*/
}
return true;
}
bool writeToCmd(const string& s)
{
DWORD dwWritten;
const char* cmd = s.c_str();
return WriteFile(hChildStdInW, cmd, sizeof(cmd), &dwWritten, NULL);
}
bool exec(const string& command)
{
if (!writeToCmd(command)) {
return false;
}
else {
cout << "Succesfully Wrote" << endl;
}
return true;
}
void checkPipe()
{
while (running) {
while (1) {
Sleep(50);
DWORD bytesAvail = 0;
if (!PeekNamedPipe(hChildStdOutR, NULL, 0, NULL, &bytesAvail, NULL)) {
cout << "Failed to call PeekNamedPipe" << endl;
}
if (bytesAvail) {
CHAR buf[BUFSIZE];
DWORD n;
BOOL success = ReadFile(hChildStdOutR, buf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
cout << "Failed to call ReadFile" << endl;
break;
}
string s = string(buf, buf + n);
cout << s << endl;
break;
}
}
}
}
int main(int argc, char** argv)
{
if (argc != 2) {
cout << "Usage: " << argv[0] << " <ADRESS>" << endl;
return 1;
}
ClientSocket client(argv[1], DEFAULT_PORT);
// Wait for initial response
string w = client.recieveLine();
if (w == "welcome") {
cout << "Connection Successful! " << endl;
}
running = true;
if (startCmd()) cout << "Cmd Started" << endl;
thread checkLoop(&checkPipe);
while (true) {
vector<string> command = split(client.recieveLine());
if (command[0] == "run") {
exec(command[1]);
}
else if (command[0] == "exit") {
running = false;
client.sendLine("exit");
break;
}
}
if (!CloseHandle(hChildStdInW))
ErrorExit("StdInWr CloseHandle");
checkLoop.join();
client.close();
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;
}
i have written a method to get all process id's inside a c++ project.
Int CProcessHelper::GetAllPIDs(const char *processName, _STL_NAMESPACE_::vector<DWORD> &pids)
{
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
HANDLE hProcess;
BOOL bContinue;
pids.clear();
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hProcessSnap == INVALID_HANDLE_VALUE )
{
return 0;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( PROCESSENTRY32 );
// Retrieve information about the first process,
// and exit if unsuccessful
bContinue = Process32First( hProcessSnap, &pe32 );
while(bContinue)
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pe32.th32ProcessID);
if( hProcess != NULL )
{
TCHAR szModName[MAX_PATH];
// Get the full path to the module's file.
if(GetModuleFileNameEx( hProcess, NULL, szModName,
sizeof(szModName)))
{
char *ptr = strrchr(szModName, '\\');
if( (ptr && stricmp(ptr+1, processName) == 0) ||
stricmp(szModName, processName) == 0)
{
pids.push_back(pe32.th32ProcessID);
/*CloseHandle( hProcess );
pid = pe32.th32ProcessID;
break;*/
}
}
CloseHandle( hProcess );
}
bContinue = Process32Next( hProcessSnap, &pe32 );
}
CloseHandle( hProcessSnap );
return (int) pids.size();
}
When i run this code under Dr Memory, i am getting following "Uninitialized Read" errors:
Uninitialized Read Error 1
at this line:
if(GetModuleFileNameEx( hProcess, NULL, szModName,
sizeof(szModName)))
2nd error is :
Uninitialized Read Error 2
at this line -
bContinue = Process32First( hProcessSnap, &pe32 );
i am not getting by Dr Memory reporting me these errors. Are these only false positive errors?
Please help me to identify which part is uninitialized here.
If you look at the attached Dr Memory error screenshot carefully, you will notice that both the errors have call to the
KERNELBASE.dll!WideCharToMultiByte
Since i am using TCHAR(1.e of char type, rather then of WCHAR type(wide char)) for szFileName var.
TCHAR szModName[MAX_PATH];
I am not getting why it is calling WideCharToMultiByte() method as i am using a method which accept char type rather then a wide char type.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID);
pe32.th32ProcessID is not set yet, it's uninitialized memory. OpenProcess() is also not needed for your purpose.
if (GetModuleFileNameEx(hProcess, NULL, szModName, sizeof(szModName)))
this function is unnecessary, the executable name is exposed by the PROCESSENTRY32 structure. In addition, the last argument should use strlen, not sizeof because you want number of characters not size of buffer.
There are other issues with this code as well, rather than trying to reinvent the wheel, MSDN has a complete commented source code for doing what you want to do using the ToolHelp32Snapshot collection of functions, you can find it here.
Here is a concise example I have modified to fit your needs without any problems:
#include <windows.h>
#include <iostream>
#include <vector>
#include <TlHelp32.h>
#include <Psapi.h>
int GetAllPIDs(const char* processName, std::vector<DWORD>& pids)
{
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);
if (Process32First(hSnap, &procEntry))
{
do
{
if (!_stricmp(procEntry.szExeFile, processName))
{
pids.push_back(procEntry.th32ProcessID);
}
} while (Process32Next(hSnap, &procEntry));
}
}
CloseHandle(hSnap);
return (int)pids.size();
}
int main()
{
std::vector<DWORD> piddies;
GetAllPIDs("calc.exe", piddies);
for (auto p : piddies)
{
std::cout << "0x" << std::hex << p << std::endl;
}
std::getchar();
return 0;
}
I have written a class to handle named pipe connections, and if I create an instance, close it, and then try to create another instance the call to CreateFile() returns INVALID_HANDLE_VALUE, and GetLastError() returns ERROR_PIPE_BUSY. What's going on here? What can I do to insure the call to Connect() succeeds?
PipeAsync A, B;
A.Connect("\\\\.\\pipe\\test",5000);
A.Close();
cout << GetLastError(); // some random value
B.Connect("\\\\.\\pipe\\test",5000);
cout << GetLastError(); // 231 (ERROR_PIPE_BUSY)
B.Close();
Here are my implementations of Connect() and Close()
BOOL PipeAsync::Connect(LPCSTR pszPipeName, DWORD dwTimeout)
{
this->pszPipeName = pszPipeName;
this->fExisting = TRUE;
DWORD dwMode = this->fMessageMode ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE;
hPipe = CreateFile(
this->pszPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if( INVALID_HANDLE_VALUE == hPipe )
return FALSE; /* set break point here ; breaks here on second call to Connect() */
if( GetLastError() == ERROR_PIPE_BUSY )
if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
return FALSE; /* set break point here */
if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) )
return FALSE; /* set break point here */
return TRUE;
}
VOID PipeAsync::Close()
{
if( fExisting )
DisconnectNamedPipe( hPipe );
CloseHandle( hPipe );
}
EDIT: I forgot to tell you how I concluded this... I set break points indicated in the comments. When run, it stops on the first break point.
EDIT: This is my updated code
if( INVALID_HANDLE_VALUE == hPipe )
if( GetLastError() == ERROR_PIPE_BUSY )
{
if(!WaitNamedPipe( this->pszPipeName, dwTimeout ))
return FALSE; /* break-point: breaks here on second call */
}
else
return FALSE; /* break-point /*
Now, WaitNamedPipe() is returning false on the second call to Connect() and GetLastError() is returning 2, or ERROR_FILE_NOT_FOUND ?
From Named Pipe Client:
If the pipe exists but all of its instances are busy, CreateFile
returns INVALID_HANDLE_VALUE and the GetLastError function returns
ERROR_PIPE_BUSY. When this happens, the named pipe client uses the
WaitNamedPipe function to wait for an instance of the named pipe to
become available.
The link has example code on coping with ERROR_PIPE_BUSY.
EDIT:
Small compilable example that demonstrates accepting and connecting on a named pipe:
const char* const PIPE_NAME = "\\\\.\\pipe\\test";
const int MAX_CONNECTIONS = 10;
void client_main()
{
DWORD last_error;
unsigned int elapsed_seconds = 0;
const unsigned int timeout_seconds = 5;
HANDLE handle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
while (INVALID_HANDLE_VALUE == handle &&
elapsed_seconds < timeout_seconds)
{
last_error = GetLastError();
if (last_error != ERROR_PIPE_BUSY)
{
break;
}
Sleep(1 * 1000);
elapsed_seconds++;
handle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
}
if (INVALID_HANDLE_VALUE == handle)
{
std::cerr << "Failed to connect to pipe " << PIPE_NAME <<
": last_error=" << last_error << "\n";
}
else
{
std::cout << "Connected to pipe " << PIPE_NAME << "\n";
CloseHandle(handle);
}
}
HANDLE _get_server_handle()
{
// Error handling omitted for security descriptor creation.
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
// Create a bi-directional message pipe.
HANDLE handle = CreateNamedPipe(PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_NOWAIT,
PIPE_UNLIMITED_INSTANCES,
4096,
4096,
0,
&sa);
if (INVALID_HANDLE_VALUE == handle)
{
std::cerr << "Failed to create named pipe handle: last_error=" <<
GetLastError() << "\n";
}
return handle;
}
void server_main()
{
HANDLE handle = _get_server_handle();
if (INVALID_HANDLE_VALUE != handle)
{
int count = 0;
while (count < MAX_CONNECTIONS)
{
BOOL result = ConnectNamedPipe(handle, 0);
const DWORD last_error = GetLastError();
if (ERROR_NO_DATA == last_error)
{
count++;
std::cout << "A client connected and disconnected: count=" <<
count << "\n";
CloseHandle(handle);
handle = _get_server_handle();
}
else if (ERROR_PIPE_CONNECTED == last_error)
{
count++;
std::cout << "A client connected before call to " <<
"ConnectNamedPipe(): count=" << count << "\n";
CloseHandle(handle);
handle = _get_server_handle();
}
else if (ERROR_PIPE_LISTENING != last_error)
{
std::cerr << "Failed to wait for connection: last_error=" <<
GetLastError() << "\n";
CloseHandle(handle);
break;
}
Sleep(100);
}
}
}
int main(int a_argc, char** a_argv)
{
if (2 == a_argc)
{
if (std::string("client") == *(a_argv + 1))
{
for (int i = 0; i < MAX_CONNECTIONS; i++)
{
client_main();
}
}
else if (std::string("server") == *(a_argv + 1))
{
server_main();
}
}
return 0;
}
Execute server-side first:
pipetest.exe server
Then execute client-side:
pipetest.exe client
I could not tell what the problem was from the posted code. Hopefully this small example will assist you in finding the issue.