I want to make a simple program that starts a cmd.exe parallely and takes input from the user as a command, which is then passed to the cmd.exe, after execution my program should take the output from cmd.exe and display it to the user. Basically an interface to a command prompt.
I don't want to use methods like system() as they start a new instance of cmd every time and I can't run commands like cd.
I tried it with the following code with which I am able to spawn a cmd and show initial line (copyright....), but passing commands simply returns the same line again.
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
DWORD WINAPI exec(LPVOID inputP){
char* input=(char*) inputP;
HANDLE stdinRd, stdinWr, stdoutRd, stdoutWr;
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD stuff;
char buff[1000];
//Create the main transfer pipe
if(!CreatePipe(&stdinRd, &stdinWr, &sa, 0) || !CreatePipe(&stdoutRd,&stdoutWr, &sa, 0)) {
cout<<"Pipe creation failed"<<endl;
}
//Get Process Startup Info
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
si.hStdOutput = stdoutWr;
si.hStdError = stdoutWr;
si.hStdInput = stdinRd;
//Create the CMD Shell using the process startup info above
if(!CreateProcess("C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
cout<<"Error Spawning Command Prompt."<<endl;
}
//Main while(1) Loop
while(1)
{
Sleep(100);
//Check if cmd.exe has not stoped
GetExitCodeProcess(pi.hProcess, &stuff);
//Stop the while loop if not active
if(stuff != STILL_ACTIVE) break;
//Copy Data from buffer to pipe and vise versa
PeekNamedPipe(stdoutRd, NULL, 0, NULL, &stuff, NULL);
ZeroMemory(buff, sizeof(buff));
//Read Console Output
ReadFile(stdoutRd, buff, 1000, &stuff, NULL);
//output
cout<<buff<<endl;
//Read data from stream and pipe it to cmd.exe
WriteFile(stdinWr, input, strlen(input), &stuff, NULL);
}
return 0;
}
int main() {
while(1){
char a[100];
cin>>a;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec, (LPVOID)a, 0, NULL);
}
}
Found my problem, was quite silly. Just need to pass a new line character so that cmd interprets the data as a command, i.e.,
cin>>a;
strcat(a,"\n");
and obviously make a single instance of cmd by calling the thread only once and passing parameters through global variables.
Related
I started a Visual C++ Desktop Application (win32) and created a button, that creates a process.
Here is the button code:
INT_PTR CALLBACK Btn_Click(HWND hWnd)
{
wchar_t cmd[] = L"cmd.exe /c testprogram.exe";
STARTUPINFOW startInf;
memset(&startInf, 0, sizeof startInf);
PROCESS_INFORMATION procInf;
memset(&procInf, 0, sizeof procInf);
BOOL p = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
CREATE_NO_WINDOW, NULL, NULL, &startInf, &procInf);
if (p)
{
CloseHandle(procInf.hProcess);
CloseHandle(procInf.hThread);
}
return TRUE;
}
I want to read the process's stdout output (cmd.exe or testprogram.exe) while it is 'running' and set it as a string. After setting the content of a created Text input.
I tried this answer, but it freezes the application. (because of ReadFile() and while() loop)
How to read output from cmd.exe using CreateProcess() and CreatePipe()
I searched the forum for a better answer, but every example is for a console app.
Update:
I can read the output of ping.exe but the window stays frozen until ping.exe closes. the for() loop (and ReadFile()) blocks the window .
wchar_t command[] = L"ping localhost";
STARTUPINFOW startInf;
memset(&startInf, 0, sizeof startInf);
PROCESS_INFORMATION procInf;
memset(&procInf, 0, sizeof procInf);
HANDLE cout_r = NULL;
HANDLE cout_w = NULL;
SECURITY_ATTRIBUTES sec_a;
//memset(&sec_a, 1, sizeof sec_a);
sec_a.nLength = sizeof(SECURITY_ATTRIBUTES);
sec_a.lpSecurityDescriptor = NULL;
CreatePipe(&cout_r, &cout_w, &sec_a, 0);
SetHandleInformation(cout_r, HANDLE_FLAG_INHERIT, 0);
//startInf.cb = sizeof(STARTUPINFO);
startInf.hStdOutput = cout_w;
startInf.dwFlags |= STARTF_USESTDHANDLES;
BOOL p = CreateProcess(NULL, command, NULL, NULL, TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL,
&startInf, &procInf);
if (p)
{
CloseHandle(procInf.hProcess);
CloseHandle(procInf.hThread);
CloseHandle(cout_w);
}
else
{
SetWindowTextA(hedit, "Failed");
}
char buf[1024];
CHAR chBuf[4096];
DWORD dwRead;
for (;;)
{
BOOL bSuccess = ReadFile(cout_r, chBuf, 4096, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
std::wstring stemp = std::wstring(s.begin(), s.end());
OutputDebugStringW(stemp.c_str());
}
I searched for asynchronous I/O on ReadFile and pipes.
If anyone can provide me an example on how to do async Readfile without for() loop would be good.
If your UI thread stops pumping messages Windows treats it as frozen. This means you should not perform blocking I/O on this thread.
You could use asynchronous (overlapped) I/O with ReadFile but doing the whole child process operation in another thread is a lot easier. The button press would start the thread and once the thread has read stdout it can signal back to your main window by sending a WM_APP message.
I'm trying to capture my screen using ffmpeg in a different thread (which I create using processthreadsapi::CreateProcess()) so I'd be able to do something else in the main thread, and redirect ffmpeg output, so it wouldn't pop up in the console for the user to see. To stop filming, I send a 'q' input using WriteFile(), and after that I want to save ffmpeg accumulated output using ReadFile().
However, if I set STARTUPINFO::hStdError (note, that ffmpeg output goes to stderr) to a pipe, from which I could read the accumulated data, the inputs I send using WriteFile() are no longer registered and ffmpeg.exe keeps running.
I've tried redirecting ffmpeg output in a simple command line, but I can still stop the process by pressing the q button.
Also, if I record for less than 8 seconds, the input is registered and ffmpeg.exe closes.
Is there something wrong with my code, or is it processthreadsapi issue, any hints will be kindly appreciared!
Here's a minimal code of how I am trying to do it:
#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
int main()
{
//Create IN and OUT pipes
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.lpSecurityDescriptor = NULL;
if (! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
cout<<"StdoutRd CreatePipe error"<<endl;
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0) )
cout<<"Stdin CreatePipe error"<<endl;
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;
//Start recording
if(!CreateProcess(NULL,
"ffmpeg -y -f gdigrab -framerate 2 -i desktop record.avi", // 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
{
cout<<"Error create process"<<endl;
}
else
{
cout<<"Process created successfully"<<endl;
cout<<"Press k to stop recording"<<endl;
}
//Record for a while
while(getch() != 'k'){
cout<<"While press k"<<endl;
};
cout<<"Stop recording called"<<endl;
//Stop recording by emulating a Q button push
DWORD dwWritten;
CHAR chBufW[1] = {'q'};
if ( ! WriteFile(g_hChildStd_IN_Wr, chBufW, 1, &dwWritten, NULL) )
cout<<"Error write file"<<endl;
//Save stdError (ffmpeg) data
DWORD dwRead;
char stdErrorData[4096];
bool bSuccess;
bSuccess = ReadFile( g_hChildStd_OUT_Wr, stdErrorData, 4096, &dwRead, NULL);
if(!bSuccess || dwRead == 0)
cout<<"Read failed"<<endl;
else{
cout<<"Read success"<<endl;
}
cout<<"Press to exit"<<endl;
while(getch() != 'k');
return 0;
}
To answer my own question, the issue was that when the handle pipe, where the ffmpeg output is going to, fills up, it stops the ffmpeg process, until I'd clear some space for new text to come in. This is happening, because ffmpeg continiously outputs recording info.
I hope this will be useful for someone :)
I have got a GUI program which is a "wrapper" to cmd.exe.
From this GUI program I can send and receive command via cmd.exe
I am using pipe redirection, and already read various references:
Creating a Child Process with Redirected Input and Output
Redirect Input and Output of Powershell.exe to Pipes in C++
windows cmd pipe not unicode even with /U switch
First of all I launch an instance of "cmd.exe /U", so that I am telling cmd.exe to generate Unicode output
then I use ReadFile/WriteFile to read and write from/to pipe.
Everything works fine if I use ANSI, But I have got 2 problems related to Unicode:
1) If I want to pass data to the pipe using WriteFile, I have to convert it from Unicode to Ansi first. Passing data in Unicode does not work: specifically, when reading the output after my WriteFile, the cmd outputs a "More?" string.
If I write the input in ANSI, it works fine and cmd correctly outputs the result of the command.
The cmd has been launched with /U switch.
2) While the console output is successfully unicode for internal commands such as cd, dir, etc. when I launch from the cmd an external program such as ping, netstat, ipconfig etc. the output I receive is in ANSI, so I get corrupted data.
I suppose that the /U switch does not have effect when external programs are running through cmd? Is there a possible solution to this, and make everything in the cmd gets output in Unicode?
Here is a sample of my code, I placed comments on lines where the errors are:
bool StartCmdPipe()
{
static SECURITY_ATTRIBUTES SA;
static STARTUPINFOW SI;
static PROCESS_INFORMATION PI;
static HANDLE StdInPipeRead;
static HANDLE StdInPipeWrite;
static HANDLE StdOutPipeRead;
static HANDLE StdOutPipeWrite;
static wstring sWorkDir;
WCHAR* pBuffer;
unsigned long BytesRead;
sWorkDir = _wgetenv(L"SystemDrive");
sWorkDir += L"\\";
bJustOpened = true;
SA.nLength = sizeof(SA);
SA.bInheritHandle = true;
SA.lpSecurityDescriptor = NULL;
if (!CreatePipe(&StdInPipeRead, &StdInPipeWrite, &SA, 0) || !CreatePipe(&StdOutPipeRead, &StdOutPipeWrite, &SA, 0))
{
return false;
}
// Set Pipe for process to create
ZeroMemory(&SI, sizeof(SI));
SI.cb = sizeof(SI);
SI.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
SI.wShowWindow = SW_HIDE;
SI.hStdInput = StdInPipeRead; //GetStdHandle(STD_INPUT_HANDLE);
SI.hStdOutput = StdOutPipeWrite;
SI.hStdError = StdOutPipeWrite;
// Launch cmd process
g_bCreateProcessSuccess = CreateProcessW(
NULL,
L"cmd.exe /U",
NULL,
NULL,
true,
0,
NULL,
(WCHAR*)sWorkDir.c_str(),
&SI,
&PI);
g_sCommandLineInput = L"";
g_bkeepCmdRunning = true;
if (g_bCreateProcessSuccess)
{
do
{
DWORD TotalBytesAvail = 0;
PeekNamedPipe(StdOutPipeRead, 0, 0, 0, &TotalBytesAvail, 0);
pBuffer = (WCHAR*)malloc(TotalBytesAvail);
if (TotalBytesAvail > 0)
{
// First problem is: while internal commands work fine (dir, cd, etc.) and return output in unicode format,
// when I launch another process (ipconfig, netstat, ping, etc.), output is returned in ANSI format.
// Even if I launched cmd.exe with /U switch
ReadFile(StdOutPipeRead, pBuffer, TotalBytesAvail, &BytesRead, NULL);
}
else BytesRead = 0;
if (BytesRead > 0)
{
wprintf(pBuffer);
}
free(pBuffer);
// g_sCommandLineInput is a global wstring variable which gets filled each time user wants to send new command.
if (g_sCommandLineInput.length() > 0)
{
DWORD numberofbyteswritten = 0;
g_sCommandLineInput += L"\n"; //Append /n to make the cmd process interpret the data as a command to launch
// Second problem is, why do I have to send command in ANSI format, even if I launched cmd with the /U switch?
string sCommndLineAnsi = WStringToString(g_sCommandLineInput);
WriteFile(StdInPipeWrite, sCommndLineAnsi.c_str(), sCommndLineAnsi.length() * sizeof(CHAR), &numberofbyteswritten, NULL);
//Reset command
g_sCommandLineInput = L"";
}
Sleep(100);
}
while (g_bkeepCmdRunning);
TerminateProcess(PI.hProcess, 0);
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
}
CloseHandle(StdOutPipeRead);
CloseHandle(StdOutPipeWrite);
return true;
}
I am coding a C program in Dev-C++, and I need to use a couple of Windows (CMD) commands. It is easy, but when the command in the system() function is executed, the program runs the console in the execution.
An example:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
system("if not exist c:\my_docs\doc.txt (xcopy /Y doc.txt c:\my_docs\)"); // Cmd command
system("pause");
return 0;
}
Exists other function, or a modification that do not shows the console?
Thanks you! Best regards.
You can use WinExec("your cmd command", SW_HIDE); instead of system("cmd command").
You can do it with CreateProcess.
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcessW(command, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
As FigBug stated, CreateProcess() is the way to go, but I don't think that CreateProcess() can execute a shell if statement. You may need to pass it something like this as a command:
"cmd.exe /c \"if not exist c:\my_docs\doc.txt (xcopy /Y doc.txt c:\my_docs\)\""
But a better solution might be to use CreateFile() to test if a file exists and CopyFile() to copy it.
NOTE: My answer is not necessarily tailored to your specific question, but this Q&A is the top Google result for "Windows system without command prompt" and other similar queries.
Here's a way to execute commands without a new cmd.exe window. Based on Roland Rabien's answer and MSDN, I've written a working function.
#include "AtlBase.h"
#include "AtlConv.h"
int windows_system(const char *cmd) {
PROCESS_INFORMATION p_info;
STARTUPINFO s_info;
DWORD ReturnValue;
CA2T programpath(cmd);
memset(&s_info, 0, sizeof(s_info));
memset(&p_info, 0, sizeof(p_info));
s_info.cb = sizeof(s_info);
if (CreateProcess(programpath, NULL, NULL, NULL, 0, 0, NULL, NULL, &s_info, &p_info)) {
WaitForSingleObject(p_info.hProcess, INFINITE);
GetExitCodeProcess(p_info.hProcess, &ReturnValue);
CloseHandle(p_info.hProcess);
CloseHandle(p_info.hThread);
}
return ReturnValue;
}
Works on all Windows platforms. Call just like you would system().
int win_system(const char *command)
{
// Windows has a system() function which works, but it opens a command prompt window.
char *tmp_command, *cmd_exe_path;
int ret_val;
size_t len;
PROCESS_INFORMATION process_info = {0};
STARTUPINFOA startup_info = {0};
len = strlen(command);
tmp_command = malloc(len + 4);
tmp_command[0] = 0x2F; // '/'
tmp_command[1] = 0x63; // 'c'
tmp_command[2] = 0x20; // <space>;
memcpy(tmp_command + 3, command, len + 1);
startup_info.cb = sizeof(STARTUPINFOA);
cmd_exe_path = getenv("COMSPEC");
_flushall(); // required for Windows system() calls, probably a good idea here too
if (CreateProcessA(cmd_exe_path, tmp_command, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &process_info)) {
WaitForSingleObject(process_info.hProcess, INFINITE);
GetExitCodeProcess(process_info.hProcess, &ret_val);
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
free((void *) tmp_command);
return(ret_val);
}
I want to create a Windows console application that will start a child process running the cmd command shell with a command line and display the output created by the child process.
The output from the child process is to be read by the parent process so I need to connect the stdout of the child process to the stdin of the parent process. The parent process will then read from its stdin the output from the child process which the child is writing to its stdout. The parent process will display the child output to the parent's stdout.
The child process will run the Windows dir command with the cmd command shell.
My current version is not displaying the dir command output. The parent process is not displaying any output other than the output from the system("pause");.
My main process:
int main(int argc,char* argv[])
{
HANDLE hStdInRead;
HANDLE hStdInWrite;
HANDLE hStdOutRead;
HANDLE hStdOutWrite;
HANDLE hStdErrWrite;
if(!CreatePipe(&hStdInRead,&hStdInWrite,NULL,0))
return 0;
if(!CreatePipe(&hStdOutRead,&hStdOutWrite,NULL,0))
return 0;
if (!DuplicateHandle(GetCurrentProcess(), hStdOutWrite, GetCurrentProcess(), &hStdErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
return 0;
}
STARTUPINFO si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.dwFlags =STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.hStdOutput = hStdOutWrite;
si.hStdError = hStdErrWrite;
si.hStdInput = hStdInRead;
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
LPSTR cmd = new char[256*sizeof(char)];
strcpy_s(cmd,256,"C:\\Windows\\cmd.exe /c dir");
if(CreateProcess(NULL,cmd,NULL,NULL,true,0,NULL,NULL,&si,&pi))
{
std::cout<<"OK"<<std::endl;
CloseHandle(hStdOutWrite);
CloseHandle(hStdInRead);
char ReadBuff[4096];
DWORD ReadNum ;
ZeroMemory(&ReadBuff,4096);
while(ReadFile(hStdOutRead,ReadBuff,4096,&ReadNum,NULL))
{
std::cout<<ReadBuff<<std::endl;
}
WaitForSingleObject(pi.hProcess,INFINITE);
}
system("pause");
return 0;
}
There are several things wrong with your code, here's a cleaned up example that works.
Changes made: Consolidated the pipe set into one array and created enums to denote which has what purpose, makes it a lot clearer than calling something "StdOutRead" and "StdOutWrite".
Created a SECURITY_ATTRIBUTES structure to let us set up the pipes for inheritance, and added code to prevent the parent-side halves of the pipes being inherited.
Removed the STARTF_USESTDHANDLES flag from the process.
Specified a directory for the process to perform it's DIR on.
Ensured that we close all the handles we're not using once the process is started in the parent.
Lastly, I made it drain the file of io in chunks and append the null terminator to the end of a successful buffer so that it can output properly.
#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>
#include <tchar.h>
#include <iostream>
#include <thread>
#include <cassert>
enum { ParentRead, ParentWrite, ChildWrite, ChildRead, NumPipeTypes };
int main(int /*argc*/, char* /*argv*/[])
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = nullptr;
HANDLE pipes[NumPipeTypes];
if (!CreatePipe(&pipes[ParentWrite], &pipes[ChildRead], &sa, 0))
return 0;
if (!CreatePipe(&pipes[ParentRead], &pipes[ChildWrite], &sa, 0))
return 0;
// make sure the handles the parent will use aren't inherited.
SetHandleInformation(pipes[ParentRead], HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(pipes[ParentWrite], HANDLE_FLAG_INHERIT, 0);
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
si.hStdOutput = pipes[ChildWrite];
si.hStdError = pipes[ChildWrite];
si.hStdInput = pipes[ChildRead];
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
TCHAR cmd[] = _T("C:\\Windows\\System32\\cmd.exe /c dir c:\\");
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
return 0;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(pipes[ChildRead]);
CloseHandle(pipes[ChildWrite]);
CloseHandle(pipes[ParentWrite]);
char ReadBuff[4096 + 1];
DWORD ReadNum;
for (;;) {
auto success = ReadFile(pipes[ParentRead], ReadBuff, sizeof(ReadBuff) - 1, &ReadNum, NULL);
if (!success || !ReadNum)
break;
ReadBuff[ReadNum] = 0;
std::cout << ReadBuff;
}
//system("pause"); use Ctrl+F5 or Debug >> Start Without debugging instead.
return 0;
}