Using ShellExecute to send sqlite3.exe a shell script - c++

I've got the following shell script in file c:/SQLiteData/Cmnds.txt
.open c:/SQLiteData/LGMeta.db
create table Temp {f INTEGER primary key};
insert into Temp values(-1);
.output c:/SQLiteData/Out.txt
select * from Temp;
I tried running it inside a c++ programme using
ShellExecute(NULL, L"open", L"c:/SQLiteData/sqlite3.exe",
L".read c:/SQLiteData/Cmnds.txt", NULL, 0);
ShellExecute returns 42 which suggests success but nothing happens. Neither Temp or Out.txt are created. Can anyone tell me what I'm missing?
EDIT
Thanks for the replies. I've spent the last 3 days tearing my hair out with this stuff. I patched this together from several posts on the subject
unsigned int RunCmnd(String CmndLine)
{
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
memset(&ProcessInfo,0,sizeof(ProcessInfo)); // setup memory blocks
memset(&StartupInfo,0,sizeof(StartupInfo));
StartupInfo.cb=sizeof(StartupInfo); // set structure size
StartupInfo.wShowWindow=SW_HIDE; // hide window
if (CreateProcess(NULL,CmndLine.c_str(),
NULL,NULL,false,0,NULL,NULL,&StartupInfo,&ProcessInfo))
{
WaitForSingleObject(ProcessInfo.hThread,INFINITE);
return 0;
}
else return GetLastError();
}
If I start up the command shell and enter the following line
c:/SQLiteData/sqlite3.exe < c:/SQLiteData/Cmnds.txt
everything works as expected BUT if use
RunCmnd("c:/SQLiteData/sqlite3.exe < c:/SQLiteData/Cmnds.txt")
from within my c++ builder app nothing happens. I'm missing something fundamental here. Can anyone tell me what?

Finally came up with this which was adapted from an excellent post by TarmoPikaro on this thread
How to execute a command and get output of command within C++ using POSIX?.
String TempFileName(bool ForwardSlash) // ForwardSlash default = true
{
wchar_t Nm[MAX_PATH],Path[MAX_PATH];
GetTempPath(MAX_PATH,Path);
if (!GetTempFileName(Path,L"",0,Nm))
throw Exception(String("TempFileName failed - ")+
SysErrorMessage(GetLastError()));
String Name=Nm;
if (ForwardSlash)
for (int Len=Name.Length(),i=1; i<=Len; i++) if (Name[i]=='\\') Name[i]='/';
return Name;
}
//---------------------------------------------------------------------------
String SQLiteExe(String DB,String OutFile,String Cmds)
{
// Returns first error message if process doesn't execute cleanly. Otherwise
// if OutFile=="" it returns the output
// if Outfile>"" the output is sent to OutFile and return is NULL.
String Output;
HANDLE hPipeRead,hPipeWrite;
SECURITY_ATTRIBUTES saAttr={sizeof(SECURITY_ATTRIBUTES)};
saAttr.bInheritHandle=TRUE; //Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor=NULL;
// Create a pipe to get output from child's stdout.
if (!CreatePipe(&hPipeRead,&hPipeWrite,&saAttr,0))
return "Error: Unable to create pipe";
STARTUPINFO si={sizeof(STARTUPINFO)};
si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput=hPipeWrite;
si.hStdError=hPipeWrite;
si.wShowWindow=SW_HIDE;
// Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
TStringList *Batch=new TStringList;
Batch->StrictDelimiter=true;
Batch->Delimiter=';';
Batch->DelimitedText=Cmds;
if (OutFile>"") Batch->Insert(0,".output "+OutFile);
String S;
for (int i=0; i<Batch->Count; i++)
{
Batch->Strings[i]=Batch->Strings[i].Trim();
// .commands must have dot as first char on line
if (Batch->Strings[i]=="") continue;
S+=Batch->Strings[i]+(Batch->Strings[i][1]=='.' ? "" : ";")+"\r\n";
}
Batch->Text=S;
String BatchFile=TempFileName();
Batch->SaveToFile(BatchFile);
delete Batch;
String Cmd="sqlite3 "+DB+" \".read "+BatchFile+"\""; // assumes sqlite3.exe in PATH
PROCESS_INFORMATION pi={0};
BOOL fSuccess=CreateProcessW(NULL,Cmd.c_str(),NULL,NULL,TRUE,CREATE_NEW_CONSOLE,
NULL,NULL, &si, &pi);
if (! fSuccess)
{
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
DeleteFile(BatchFile);
return "Error: Failed to create process";
}
bool bProcessEnded=false;
while (!bProcessEnded)
{
// Give some timeslice (50ms), so we won't waste 100% cpu.
bProcessEnded=WaitForSingleObject(pi.hProcess,50)==WAIT_OBJECT_0;
// Even if process exited - we continue reading, if there is some data available
// over pipe.
while (true)
{
char buf[1024];
DWORD dwRead=0;
DWORD dwAvail=0;
if (!::PeekNamedPipe(hPipeRead,NULL,0,NULL,&dwAvail,NULL)) break;
if (!dwAvail) break; // no data available, return
if (!::ReadFile(hPipeRead,buf,std::min(sizeof(buf)-1,
(unsigned int)(dwAvail)),&dwRead,NULL) || !dwRead)
break; // error, the child process might ended
buf[dwRead]=0;
Output+=buf;
}
int p=Output.Pos("Error:"); // if (p) return first error message
if (p) {Output.Delete(1,p-1); Output.Delete(Output.Pos("\r\n"),Output.Length());}
}
DeleteFile(BatchFile); // NB can't be deleted until ProcessEnded
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return Output;
}
//---------------------------------------------------------------------------
Usage
String Err=SQLiteExe("c:/SQLiteData/MyDB.db","c:/SQLiteData/MyTblDump.txt"",
".mode csv; select * from MyTbl;");
if (Err>"") throw Exception(Err);
else ........
Multiple commands should be separated by semi-colons. SQLiteExe automatically removes the redundant semi-colons at the end of lines starting with a dot.

Related

Do input redirection and capture command output (Custom shell-like program)

I'm writing a custom shell where I try to add support for input, output redirections and pipes just like standard shell. I stuck at point where I cannot do input redirection, but output redirection is perfectly working. My implementation is something like this (only related part), you can assume that (string) input is non-empty
void execute() {
... // stuff before execution and initialization of variables
int *fds;
std::string content;
std::string input = readFromAFile(in_file); // for input redirection
for (int i = 0; i < commands.size(); i++) {
fds = subprocess(commands[i]);
dprintf(fds[1], "%s", input.data()); // write to write-end of pipe
close(fds[1]);
content += readFromFD(fds[0]); // read from read-end of pipe
close(fds[0]);
}
... // stuff after execution
}
int *subprocess(std::string &cmd) {
std::string s;
int *fds = new int[2];
pipe(fds);
pid_t pid = fork();
if (pid == -1) {
std::cerr << "Fork failed.";
}
if (pid == 0) {
dup2(fds[1], STDOUT_FILENO);
dup2(fds[0], STDIN_FILENO);
close(fds[1]);
close(fds[0]);
system(cmd.data());
exit(0); // child terminates
}
return fds;
}
My thought is subprocess returns a pipe (fd_in, fd_out) and parent can write to write-end and read-from read-end afterwards. However when I try an input redirection something like sort < in.txt, the program just hangs. I think there is a deadlock because one waiting other to write, and other one to read, however, after parent writes to write-end it closes, and then read from read-end. How should I consider this case ?
When I did a bit of searching, I saw this answer, which my original thinking was similar except that in the answer it mentions creating two pipes. I did not quite understand this part. Why do we need two separate pipes ?

Execute command from cpp

The application I'm working on needs to execute commands. Commands can be console commands or 'GUI applications' (like notepad).
I need to get the return code in both cases, and in the case of console commands I also need to catch the output from stdin and stderr.
In order to implement this feature, I based my code on the stack overflow question 'How to execute a command and get output of command within C++ using POSIX?'.
My code:
int ExecuteCmdEx(const char* cmd, std::string &result)
{
char buffer[128];
int retCode = -1; // -1 if error ocurs.
std::string command(cmd);
command.append(" 2>&1"); // also redirect stderr to stdout
result = "";
FILE* pipe = _popen(command.c_str(), "r");
if (pipe != NULL) {
try {
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
}
catch (...) {
retCode = _pclose(pipe);
throw;
}
retCode = _pclose(pipe);
}
return retCode;
}
It works perfectly with console applications, but in the case of 'GUI applications' it doesn't work as expected...
With 'GUI applications', code stops on while (!feof(pipe)) expecting to get something from pipe.
I understand that 'GUI applications' like notepad don't finish until someone interacts with them (user closes the app, kills the process, etc.),
but when I launch console applications from Windows Console, prompt comes back immediately.
I would like to obtain the same behavior from 'GUI applications'...
One possible solution would be to add the isGui variable indicating when the code should read from the pipe, but I rejected this option, as I don't want to indicate if it is a 'GUI application' or not.
Well you don't have to indicate isGui yourself but detect it by checking the subsystem of the executable (windows/console) prior to executing the command, and in case of windows skip waiting on the redirected pipes.
For example, using SHGetFileInfo with the SHGFI_EXETYPE flag:
bool isGuiApplication(const std::string& command)
{
auto it = command.find_first_of(" \t");
const std::string& executable = (it == std::string::npos ? command : command.substr(0, it));
DWORD_PTR exetype = SHGetFileInfo(executable.c_str(), 0, nullptr, 0, SHGFI_EXETYPE);
if (!exetype) {
cerr << "Executable check failed\n";
}
return ((uintptr_t)exetype & 0xffff0000);
}
Then later in the code...
if (isGuiApplication(command)) {
cout << "GUI application\n";
system(command.c_str()); // don't wait on stdin
}
else {
cout << "Console application\n";
. . .
// _popen and stuff
}

Reading from pipes blocks and deadlock

I am developing a remote shell like program. for running commands on remote machine I create a cmd.exe process and I redirect its stdin and stdout to a pair of pipes.
I use these pipes to send commands and get the result.
I used ReadFile function for returning the output of command from cmd.exe but I don't know the
exact amount of data that I must read from pipe. so if the data is not ready the readfile
goes to blocking mode and waiting for data.
I am using MSVC++ 2010 and my OS is Win7. Here is a part of my code as an example:
void CpipesDlg::OnBnClickedBtnredirstd()
{
char Cmd[]="dir *.*\r\n";
char Buff[129];
CString str;
HANDLE hStdIn_Read, hStdIn_Write;
HANDLE hStdOut_Read, hStdOut_Write;
SECURITY_ATTRIBUTES sAttr;
STARTUPINFOA StartInf;
PROCESS_INFORMATION procInf;
DWORD dwBytesToWrite,dwBytesReadFrom;
sAttr.nLength = sizeof(sAttr);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
CreatePipe(&hStdIn_Read,&hStdIn_Write,&sAttr,0);
CreatePipe(&hStdOut_Read,&hStdOut_Write,&sAttr,0);
//SetHandleInformation(hStdIn_Read, HANDLE_FLAG_INHERIT, 0);
//SetHandleInformation(hStdIn_Write, HANDLE_FLAG_INHERIT, 0);
memset(&StartInf,0, sizeof(StartInf));
memset(&procInf,0,sizeof(procInf));
StartInf.cb = sizeof(StartInf);
StartInf.dwFlags = STARTF_USESTDHANDLES;
StartInf.hStdError = hStdOut_Write;
StartInf.hStdOutput = hStdOut_Write;
StartInf.hStdInput = hStdIn_Read;
WriteFile(hStdIn_Write,Cmd,sizeof(Cmd),&dwBytesToWrite,NULL);
if(!CreateProcessA(NULL,"cmd.exe",NULL,NULL,TRUE,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW ,NULL,NULL,&StartInf,&procInf))
{
MessageBoxA(m_hWnd, "Can't Create Process","Error",MB_OK | MB_ICONERROR);
}
WriteFile(hStdIn_Write,Cmd,sizeof(Cmd),&dwBytesToWrite,NULL);
BOOL bSUCCESS =TRUE;
Sleep(100);
while(bSUCCESS)
{
BOOL bResult = ReadFile(hStdOut_Read,Buff,70,&dwBytesReadFrom,NULL);
if(!bResult)
{
break;
}
Buff[dwBytesReadFrom]=0;
str+= Buff;
bSUCCESS = dwBytesReadFrom!=0;
}
m_Disp = str;
UpdateData(FALSE);
CloseHandle(hStdIn_Read);
CloseHandle(hStdIn_Write);
CloseHandle(hStdOut_Read);
CloseHandle(hStdOut_Write);
}
In above code, in debugging mode the first calls to ReadFile function returns
true data but last call of it blocks, because there is no enough data to read.
Here is my Question:
How can I avoid the blocking mode or get the exact number of bytes for reading?
Regards!
You PROBABLY want to use the PeekNamedPipe function, which allows you to interrogate a pipe to see how much data there is available at any given time. If the lpBuffer is NULL then no data is being read.

Executing dos commands with Borland C++ and saving output

New to C++.
I have been looking most of the afternoon, does anyone know a simple way to execute DOS commands and save to a variable for a windows forms application?
You can use system("dir"); . This will bring up the command prompt and run the dir command.
Alternatively you can use WinExec.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687393(v=vs.85).aspx
You can make the command to redirect to a text file, and read off of it.
bool execDosCommand(char *command, AnsiString &output)
{
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa,sizeof(SECURITY_ATTRIBUTES));
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle=true;
sa.lpSecurityDescriptor=NULL;
HANDLE ReadPipeHandle;
HANDLE WritePipeHandle; // not used here
if(!CreatePipe(&ReadPipeHandle, &WritePipeHandle, &sa, 0))
return false;
STARTUPINFOA si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb=sizeof(STARTUPINFO);
si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow=SW_HIDE;
si.hStdOutput=WritePipeHandle;
si.hStdError=WritePipeHandle;
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
text cmd;
cmd.print("/c %s", command);
char pathbuf[MAX_PATH];
_searchenv("CMD.EXE", "PATH", pathbuf);
if(!CreateProcessA(pathbuf, cmd.t_str(), NULL, NULL, true, 0, NULL, NULL, &si, &pi))
return false;
char Data[1024];
for (;;)
{
DWORD BytesRead;
DWORD TotalBytes;
DWORD BytesLeft;
if(!PeekNamedPipe(ReadPipeHandle,Data,sizeof(Data),&BytesRead, &TotalBytes,&BytesLeft)) return false;
if(BytesRead)
{
if(!ReadFile(ReadPipeHandle,Data,sizeof(Data)-1,&BytesRead,NULL))
return false;
Data[BytesRead]='\0';
output += Data;
}
else
{
if(WaitForSingleObject(pi.hProcess,0)==WAIT_OBJECT_0)
break;
}
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(ReadPipeHandle);
CloseHandle(WritePipeHandle);
return true;
}
for example: execDosCommand("dir C:\", output);
as roymustang mentioned, you can use the system command to execute another command from the system. This could be a batch script for example that pipes the output of the command into a text file. You can then read the text file to actually get the information.
The Problem you have with "returning" the command output is, how does the command output look like? In what data structure would you store it? Most of the time you'll get a bunch of unformatted text, which can't be parsed to easily so there is no real generic way to return the output of an application or script into a C++ data structure.
You might as well want to take a look here:
http://docwiki.embarcadero.com/RADStudio/en/System,_wsystem
Like described above I don't believe there is a way to return the output of an application call to your program, at least none that I ever heard about.
Greets,
Florian

Qt GUI app unexpectedly ending

Hi I am working on Linux and I am trying to create a GUI app to go with my executable I have made.
For some reason it unexpectedly ends. There is no error message, it just says in the Qt console window it unexpectedly ended with exit code 0.
Can someone please have a look at it for me. I am working on Linux.
I will also paste the code here.
void MainWindow::on_pushButton_clicked()
{
QString stringURL = ui->lineEdit->text();
ui->labelError->clear();
if(stringURL.isEmpty() || stringURL.isNull()) {
ui->labelError->setText("You have not entered a URL.");
stringURL.clear();
return;
}
std::string cppString = stringURL.toStdString();
const char* cString = cppString.c_str();
char* output;
//These arrays will hold the file id of each end of two pipes
int fidOut[2];
int fidIn[2];
//Create two uni-directional pipes
int p1 = pipe(fidOut); //populates the array fidOut with read/write fid
int p2 = pipe(fidIn); //populates the array fidIn with read/write fid
if ((p1 == -1) || (p2 == -1)) {
printf("Error\n");
return;
}
//To make this more readable - I'm going to copy each fileid
//into a semantically more meaningful name
int parentRead = fidIn[0];
int parentWrite = fidOut[1];
int childRead = fidOut[0];
int childWrite = fidIn[1];
//////////////////////////
//Fork into two processes/
//////////////////////////
pid_t processId = fork();
//Which process am I?
if (processId == 0) {
/////////////////////////////////////////////////
//CHILD PROCESS - inherits file id's from parent/
/////////////////////////////////////////////////
::close(parentRead); //Don't need these
::close(parentWrite); //
//Map stdin and stdout to pipes
dup2(childRead, STDIN_FILENO);
dup2(childWrite, STDOUT_FILENO);
//Exec - turn child into sort (and inherit file id's)
execlp("htmlstrip", "htmlstrip", "-n", NULL);
} else {
/////////////////
//PARENT PROCESS/
/////////////////
::close(childRead); //Don't need this
::close(childWrite); //
//Write data to child process
//char strMessage[] = cString;
write(parentWrite, cString, strlen(cString));
::close(parentWrite); //this will send an EOF and prompt sort to run
//Read data back from child
char charIn;
while ( read(parentRead, &charIn, 1) > 0 ) {
output = output + (charIn);
printf("%s", output);
}
::close(parentRead); //This will prompt the child process to quit
}
return;
}
EDIT:: DEBUGGING RESULTS
I ran the debugger and this is the error I received:
The inferior stopped because it received a signal from the Operating System.
Signal name : SIGSEGV
Signal meaning : Segmentation fault
You haven't initialized the "output" variable. On the last lines of your code, you do this:
while ( read(parentRead, &charIn, 1) > 0 ) {
output = output + (charIn);
printf("%s", output);
}
Which will do nasty things, since you are adding a byte read from your child process, to the output variable, which is a pointer that contains garbage, and then printing the contents of the "output" variable's address as a string. You probably want "output" to be a std::string, that way your code could make sense:
std::string output;
/* ... */
while ( read(parentRead, &charIn, 1) > 0 ) {
output += (charIn);
}
std::cout << output;
Once you have read all the data your child process has generated, you can write it to stdout.
EDIT: since you want to set the contents of "output" to a QPlainTextEdit, you can use QPlainTextEdit::setPlainText:
while ( read(parentRead, &charIn, 1) > 0 ) {
output += (charIn);
}
plainTextEdit.setPlainText(output.c_str());