wxWidgets/samples/exec/exec.cpp
Vadim Zeitlin d1c063b90f Don't ignore child process output if it exits with -1 exit code.
While this code is used by us if the program couldn't be launched at all, it
doesn't mean that it didn't run as -1 could also be returned by the child
process to indicate an error after outputting something, so we should still
read its output in this case.

Closes #15205.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74352 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-03 00:33:04 +00:00

1577 lines
44 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: exec.cpp
// Purpose: exec sample demonstrates wxExecute and related functions
// Author: Vadim Zeitlin
// Modified by:
// Created: 15.01.00
// RCS-ID: $Id$
// Copyright: (c) Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/log.h"
#include "wx/frame.h"
#include "wx/panel.h"
#include "wx/timer.h"
#include "wx/utils.h"
#include "wx/menu.h"
#include "wx/msgdlg.h"
#include "wx/textdlg.h"
#include "wx/filedlg.h"
#include "wx/choicdlg.h"
#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/stattext.h"
#include "wx/textctrl.h"
#include "wx/listbox.h"
#include "wx/sizer.h"
#endif
#include "wx/txtstrm.h"
#include "wx/numdlg.h"
#include "wx/textdlg.h"
#include "wx/ffile.h"
#include "wx/scopedptr.h"
#include "wx/stopwatch.h"
#include "wx/process.h"
#include "wx/mimetype.h"
#ifdef __WINDOWS__
#include "wx/dde.h"
#endif // __WINDOWS__
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#endif
// ----------------------------------------------------------------------------
// the usual application and main frame classes
// ----------------------------------------------------------------------------
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
// override base class virtuals
// ----------------------------
// this one is called on application startup and is a good place for the app
// initialization (doing it here and not in the ctor allows to have an error
// return: if OnInit() returns false, the application terminates)
virtual bool OnInit();
};
// Define an array of process pointers used by MyFrame
class MyPipedProcess;
WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
class MyProcess;
WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
// ctor and dtor
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
virtual ~MyFrame();
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
void OnKill(wxCommandEvent& event);
void OnClear(wxCommandEvent& event);
void OnBeginBusyCursor(wxCommandEvent& event);
void OnEndBusyCursor(wxCommandEvent& event);
void OnSyncExec(wxCommandEvent& event);
void OnAsyncExec(wxCommandEvent& event);
void OnShell(wxCommandEvent& event);
void OnExecWithRedirect(wxCommandEvent& event);
void OnExecWithPipe(wxCommandEvent& event);
void OnPOpen(wxCommandEvent& event);
void OnFileExec(wxCommandEvent& event);
void OnFileLaunch(wxCommandEvent& event);
void OnOpenURL(wxCommandEvent& event);
void OnShowCommandForExt(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
// polling output of async processes
void OnIdleTimer(wxTimerEvent& event);
void OnIdle(wxIdleEvent& event);
// for MyPipedProcess
void OnProcessTerminated(MyPipedProcess *process);
wxListBox *GetLogListBox() const { return m_lbox; }
// for MyProcess
void OnAsyncTermination(MyProcess *process);
// timer updating a counter in the background
void OnBgTimer(wxTimerEvent& event);
private:
void ShowOutput(const wxString& cmd,
const wxArrayString& output,
const wxString& title);
int GetExecFlags() const;
void DoAsyncExec(const wxString& cmd);
void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
void AddPipedProcess(MyPipedProcess *process);
void RemovePipedProcess(MyPipedProcess *process);
// the PID of the last process we launched asynchronously
long m_pidLast;
// last command we executed
wxString m_cmdLast;
#ifdef __WINDOWS__
void OnDDEExec(wxCommandEvent& event);
void OnDDERequest(wxCommandEvent& event);
bool GetDDEServer();
// last params of a DDE transaction
wxString m_server,
m_topic,
m_cmdDde;
#endif // __WINDOWS__
wxListBox *m_lbox;
// array of running processes with redirected IO
MyPipedProcessesArray m_running;
// array of all asynchrously running processes
MyProcessesArray m_allAsync;
// the idle event wake up timer
wxTimer m_timerIdleWakeUp;
// a background timer allowing to easily check visually whether the
// messages are processed or not
wxTimer m_timerBg;
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
};
// ----------------------------------------------------------------------------
// MyPipeFrame: allows the user to communicate with the child process
// ----------------------------------------------------------------------------
class MyPipeFrame : public wxFrame
{
public:
MyPipeFrame(wxFrame *parent,
const wxString& cmd,
wxProcess *process);
protected:
void OnTextEnter(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
void OnBtnSendFile(wxCommandEvent& WXUNUSED(event));
void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); }
void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); }
void OnClose(wxCloseEvent& event);
void OnProcessTerm(wxProcessEvent& event);
void DoSend()
{
wxString s(m_textOut->GetValue());
s += wxT('\n');
m_out.Write(s.c_str(), s.length());
m_textOut->Clear();
DoGet();
}
void DoGet();
void DoClose();
private:
void DoGetFromStream(wxTextCtrl *text, wxInputStream& in);
void DisableInput();
void DisableOutput();
wxProcess *m_process;
wxOutputStream &m_out;
wxInputStream &m_in,
&m_err;
wxTextCtrl *m_textOut,
*m_textIn,
*m_textErr;
DECLARE_EVENT_TABLE()
};
// ----------------------------------------------------------------------------
// wxProcess-derived classes
// ----------------------------------------------------------------------------
// This is the handler for process termination events
class MyProcess : public wxProcess
{
public:
MyProcess(MyFrame *parent, const wxString& cmd)
: wxProcess(parent), m_cmd(cmd)
{
m_parent = parent;
}
// instead of overriding this virtual function we might as well process the
// event from it in the frame class - this might be more convenient in some
// cases
virtual void OnTerminate(int pid, int status);
protected:
MyFrame *m_parent;
wxString m_cmd;
};
// A specialization of MyProcess for redirecting the output
class MyPipedProcess : public MyProcess
{
public:
MyPipedProcess(MyFrame *parent, const wxString& cmd)
: MyProcess(parent, cmd)
{
Redirect();
}
virtual void OnTerminate(int pid, int status);
virtual bool HasInput();
};
// A version of MyPipedProcess which also sends input to the stdin of the
// child process
class MyPipedProcess2 : public MyPipedProcess
{
public:
MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
: MyPipedProcess(parent, cmd), m_input(input)
{
}
virtual bool HasInput();
private:
wxString m_input;
};
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// IDs for the controls and the menu commands
enum
{
// timer ids
Exec_TimerIdle = 10,
Exec_TimerBg,
// menu items
Exec_Kill = 100,
Exec_ClearLog,
Exec_BeginBusyCursor,
Exec_EndBusyCursor,
Exec_SyncExec = 200,
Exec_AsyncExec,
Exec_Shell,
Exec_POpen,
Exec_OpenFile,
Exec_ShowCommandForExt,
Exec_LaunchFile,
Exec_OpenURL,
Exec_DDEExec,
Exec_DDERequest,
Exec_Redirect,
Exec_Pipe,
Exec_Flags_HideConsole,
Exec_Flags_ShowConsole,
Exec_Flags_NoEvents,
Exec_About = wxID_ABOUT,
Exec_Quit = wxID_EXIT,
// control ids
Exec_Btn_Send = 1000,
Exec_Btn_SendFile,
Exec_Btn_Get,
Exec_Btn_Close
};
static const wxChar *DIALOG_TITLE = wxT("Exec sample");
// ----------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// ----------------------------------------------------------------------------
// the event tables connect the wxWidgets events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Exec_Quit, MyFrame::OnQuit)
EVT_MENU(Exec_Kill, MyFrame::OnKill)
EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor)
EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor)
EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
EVT_MENU(Exec_Shell, MyFrame::OnShell)
EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
EVT_MENU(Exec_ShowCommandForExt, MyFrame::OnShowCommandForExt)
EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
#ifdef __WINDOWS__
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
#endif // __WINDOWS__
EVT_MENU(Exec_About, MyFrame::OnAbout)
EVT_IDLE(MyFrame::OnIdle)
EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile)
EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose)
EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter)
EVT_CLOSE(MyPipeFrame::OnClose)
EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm)
END_EVENT_TABLE()
// Create a new application object: this macro will allow wxWidgets to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also declares the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)
IMPLEMENT_APP(MyApp)
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------
// `Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
// Create the main application window
MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"),
wxDefaultPosition, wxSize(500, 140));
// Show it
frame->Show(true);
// success: wxApp::OnRun() will be called which will enter the main message
// loop and the application will run. If we returned false here, the
// application would exit immediately.
return true;
}
// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------
#ifdef __VISUALC__
#pragma warning(disable: 4355) // this used in base member initializer list
#endif
// frame constructor
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
m_timerIdleWakeUp(this, Exec_TimerIdle),
m_timerBg(this, Exec_TimerBg)
{
SetIcon(wxICON(sample));
m_pidLast = 0;
#ifdef __WXMAC__
// we need this in order to allow the about menu relocation, since ABOUT is
// not the default id of the about menu
wxApp::s_macAboutMenuItemId = Exec_About;
#endif
// create a menu bar
wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
wxT("Kill a process by PID"));
menuFile->AppendSeparator();
menuFile->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
wxT("Launch the command to open this kind of files"));
menuFile->Append(Exec_ShowCommandForExt,
"Show association for extension...\tShift-Ctrl-A",
"Show the command associated with the given extension");
menuFile->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
wxT("Launch the default application associated with the file"));
menuFile->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
wxT("Launch the default browser with the given URL"));
menuFile->AppendSeparator();
menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
menuFile->AppendSeparator();
menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
wxT("Clear the log window"));
menuFile->AppendSeparator();
menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
wxMenu *flagsMenu = new wxMenu;
flagsMenu->AppendCheckItem(Exec_Flags_HideConsole, "Always &hide console");
flagsMenu->AppendCheckItem(Exec_Flags_ShowConsole, "Always &show console");
flagsMenu->AppendCheckItem(Exec_Flags_NoEvents, "Disable &events",
"This flag is valid for sync execution only");
wxMenu *execMenu = new wxMenu;
execMenu->AppendSubMenu(flagsMenu, "Execution flags");
execMenu->AppendSeparator();
execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
wxT("Launch a program and return when it terminates"));
execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
wxT("Launch a program and return immediately"));
execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
wxT("Launch a shell and execute a command in it"));
execMenu->AppendSeparator();
execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
wxT("Launch a program and capture its output"));
execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
wxT("Pipe a string through a filter"));
execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
wxT("Open a pipe to and from another program"));
#ifdef __WINDOWS__
execMenu->AppendSeparator();
execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
#endif
wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
helpMenu->Append(Exec_About, wxT("&About\tF1"), wxT("Show about dialog"));
// now append the freshly created menu to the menu bar...
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(menuFile, wxT("&File"));
menuBar->Append(execMenu, wxT("&Exec"));
menuBar->Append(helpMenu, wxT("&Help"));
// ... and attach this menu bar to the frame
SetMenuBar(menuBar);
// create the listbox in which we will show misc messages as they come
m_lbox = new wxListBox(this, wxID_ANY);
wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
wxFONTWEIGHT_NORMAL);
if ( font.IsOk() )
m_lbox->SetFont(font);
#if wxUSE_STATUSBAR
// create a status bar just for fun (by default with 1 pane only)
CreateStatusBar(2);
SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
#endif // wxUSE_STATUSBAR
m_timerBg.Start(1000);
}
MyFrame::~MyFrame()
{
// any processes left until now must be deleted manually: normally this is
// done when the associated process terminates but it must be still running
// if this didn't happen until now
for ( size_t n = 0; n < m_allAsync.size(); n++ )
{
delete m_allAsync[n];
}
}
// ----------------------------------------------------------------------------
// event handlers: file and help menu
// ----------------------------------------------------------------------------
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
// true is to force the frame to close
Close(true);
}
void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
{
m_lbox->Clear();
}
void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
{
wxBeginBusyCursor();
}
void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
{
wxEndBusyCursor();
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
{
long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
wxT("Enter PID:"),
wxT("Exec question"),
m_pidLast,
// we need the full unsigned int range
-INT_MAX, INT_MAX,
this);
if ( pid == -1 )
{
// cancelled
return;
}
m_pidLast = pid;
static const wxString signalNames[] =
{
wxT("Just test (SIGNONE)"),
wxT("Hangup (SIGHUP)"),
wxT("Interrupt (SIGINT)"),
wxT("Quit (SIGQUIT)"),
wxT("Illegal instruction (SIGILL)"),
wxT("Trap (SIGTRAP)"),
wxT("Abort (SIGABRT)"),
wxT("Emulated trap (SIGEMT)"),
wxT("FP exception (SIGFPE)"),
wxT("Kill (SIGKILL)"),
wxT("Bus (SIGBUS)"),
wxT("Segment violation (SIGSEGV)"),
wxT("System (SIGSYS)"),
wxT("Broken pipe (SIGPIPE)"),
wxT("Alarm (SIGALRM)"),
wxT("Terminate (SIGTERM)"),
};
static int s_sigLast = wxSIGNONE;
int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
wxT("Exec question"),
WXSIZEOF(signalNames), signalNames,
s_sigLast,
this);
switch ( sig )
{
default:
wxFAIL_MSG( wxT("unexpected return value") );
// fall through
case -1:
// cancelled
return;
case wxSIGNONE:
case wxSIGHUP:
case wxSIGINT:
case wxSIGQUIT:
case wxSIGILL:
case wxSIGTRAP:
case wxSIGABRT:
case wxSIGEMT:
case wxSIGFPE:
case wxSIGKILL:
case wxSIGBUS:
case wxSIGSEGV:
case wxSIGSYS:
case wxSIGPIPE:
case wxSIGALRM:
case wxSIGTERM:
break;
}
s_sigLast = sig;
if ( sig == wxSIGNONE )
{
// This simply calls Kill(wxSIGNONE) but using it is more convenient.
if ( wxProcess::Exists(pid) )
{
wxLogStatus(wxT("Process %ld is running."), pid);
}
else
{
wxLogStatus(wxT("No process with pid = %ld."), pid);
}
}
else // not SIGNONE
{
wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
if ( rc == wxKILL_OK )
{
wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
}
else
{
static const wxChar *errorText[] =
{
wxT(""), // no error
wxT("signal not supported"),
wxT("permission denied"),
wxT("no such process"),
wxT("unspecified error"),
};
wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
pid, sig, errorText[rc]);
}
}
}
// ----------------------------------------------------------------------------
// execution options dialog
// ----------------------------------------------------------------------------
enum ExecQueryDialogID
{
TEXT_EXECUTABLE,
TEXT_CWD,
TEXT_ENVIRONMENT
};
class ExecQueryDialog : public wxDialog
{
public:
ExecQueryDialog(const wxString& cmd);
wxString GetExecutable() const
{
return m_executable->GetValue();
}
wxString GetWorkDir() const
{
return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
}
void GetEnvironment(wxEnvVariableHashMap& env);
private:
void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
{
event.Enable(m_useCWD->GetValue());
}
void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
{
event.Enable(m_useEnv->GetValue());
}
wxTextCtrl* m_executable;
wxTextCtrl* m_cwdtext;
wxTextCtrl* m_envtext;
wxCheckBox* m_useCWD;
wxCheckBox* m_useEnv;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
END_EVENT_TABLE()
ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
: wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
wxDefaultPosition, wxSize(300, 200),
wxTE_MULTILINE|wxHSCROLL);
const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
flagsExpand);
globalSizer->Add(m_executable, flagsExpand);
m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
globalSizer->Add(m_useCWD, flagsExpand);
globalSizer->Add(m_cwdtext, flagsExpand);
m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
globalSizer->Add(m_useEnv, flagsExpand);
globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
SetSizerAndFit(globalSizer);
m_executable->SetValue(cmd);
m_cwdtext->SetValue(wxGetCwd());
wxEnvVariableHashMap env;
if ( wxGetEnvMap(&env) )
{
for ( wxEnvVariableHashMap::iterator it = env.begin();
it != env.end();
++it )
{
m_envtext->AppendText(it->first + '=' + it->second + '\n');
}
}
m_useCWD->SetValue(false);
m_useEnv->SetValue(false);
}
void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
{
env.clear();
if ( m_useEnv->GetValue() )
{
wxString name,
value;
const int nb = m_envtext->GetNumberOfLines();
for ( int l = 0; l < nb; l++ )
{
const wxString line = m_envtext->GetLineText(l).Trim();
if ( !line.empty() )
{
name = line.BeforeFirst('=', &value);
if ( name.empty() )
{
wxLogWarning("Skipping invalid environment line \"%s\".", line);
continue;
}
env[name] = value;
}
}
}
}
static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
{
ExecQueryDialog dialog(cmd);
if ( dialog.ShowModal() != wxID_OK )
return false;
cmd = dialog.GetExecutable();
env.cwd = dialog.GetWorkDir();
dialog.GetEnvironment(env.env);
return true;
}
// ----------------------------------------------------------------------------
// event handlers: exec menu
// ----------------------------------------------------------------------------
int MyFrame::GetExecFlags() const
{
wxMenuBar* const mbar = GetMenuBar();
int flags = 0;
if ( mbar->IsChecked(Exec_Flags_HideConsole) )
flags |= wxEXEC_HIDE_CONSOLE;
if ( mbar->IsChecked(Exec_Flags_ShowConsole) )
flags |= wxEXEC_SHOW_CONSOLE;
if ( mbar->IsChecked(Exec_Flags_NoEvents) )
flags |= wxEXEC_NOEVENTS;
return flags;
}
void MyFrame::DoAsyncExec(const wxString& cmd)
{
MyProcess * const process = new MyProcess(this, cmd);
m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | GetExecFlags(), process);
if ( !m_pidLast )
{
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
delete process;
}
else
{
wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
m_cmdLast = cmd;
// the parent frame keeps track of all async processes as it needs to
// free them if we exit before the child process terminates
AddAsyncProcess(process);
}
}
void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
{
wxString cmd;
wxExecuteEnv env;
if ( !QueryExec(cmd, env) )
return;
wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
int code = wxExecute(cmd, wxEXEC_SYNC | GetExecFlags(), NULL, &env);
wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
cmd.c_str(), code);
m_cmdLast = cmd;
}
void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
{
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
DIALOG_TITLE,
m_cmdLast);
if ( !cmd )
return;
DoAsyncExec(cmd);
}
void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
{
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
DIALOG_TITLE,
m_cmdLast);
if ( !cmd )
return;
int code = wxShell(cmd);
wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
cmd.c_str(), code);
m_cmdLast = cmd;
}
void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
{
if ( !m_cmdLast )
{
#ifdef __WXMSW__
m_cmdLast = "type Makefile.in";
#else
m_cmdLast = "cat -n Makefile";
#endif
}
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
DIALOG_TITLE,
m_cmdLast);
if ( !cmd )
return;
bool sync;
switch ( wxMessageBox(wxT("Execute it synchronously?"),
wxT("Exec question"),
wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
{
case wxYES:
sync = true;
break;
case wxNO:
sync = false;
break;
default:
return;
}
if ( sync )
{
wxLogStatus("\"%s\" is running please wait...", cmd);
wxStopWatch sw;
wxArrayString output, errors;
int code = wxExecute(cmd, output, errors);
wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
cmd, sw.Time(), code);
ShowOutput(cmd, output, wxT("Output"));
ShowOutput(cmd, errors, wxT("Errors"));
}
else // async exec
{
MyPipedProcess *process = new MyPipedProcess(this, cmd);
if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
{
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
delete process;
}
else
{
AddPipedProcess(process);
}
}
m_cmdLast = cmd;
}
void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
{
if ( !m_cmdLast )
m_cmdLast = wxT("tr [a-z] [A-Z]");
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
DIALOG_TITLE,
m_cmdLast);
if ( !cmd )
return;
wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
DIALOG_TITLE);
if ( !input )
return;
// always execute the filter asynchronously
MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
if ( pid )
{
wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
AddPipedProcess(process);
}
else
{
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
delete process;
}
m_cmdLast = cmd;
}
void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
{
wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
DIALOG_TITLE,
m_cmdLast);
if ( cmd.empty() )
return;
wxProcess *process = wxProcess::Open(cmd);
if ( !process )
{
wxLogError(wxT("Failed to launch the command."));
return;
}
wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
wxOutputStream *out = process->GetOutputStream();
if ( !out )
{
wxLogError(wxT("Failed to connect to child stdin"));
return;
}
wxInputStream *in = process->GetInputStream();
if ( !in )
{
wxLogError(wxT("Failed to connect to child stdout"));
return;
}
new MyPipeFrame(this, cmd, process);
}
static wxString gs_lastFile;
static bool AskUserForFileName()
{
wxString filename;
#if wxUSE_FILEDLG
filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
#else // !wxUSE_FILEDLG
filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
gs_lastFile);
#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
if ( filename.empty() )
return false;
gs_lastFile = filename;
return true;
}
void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
{
if ( !AskUserForFileName() )
return;
wxString ext = gs_lastFile.AfterLast(wxT('.'));
wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
if ( !ft )
{
wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
ext.c_str());
return;
}
wxString cmd;
bool ok = ft->GetOpenCommand(&cmd,
wxFileType::MessageParameters(gs_lastFile));
delete ft;
if ( !ok )
{
wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
ext.c_str());
return;
}
DoAsyncExec(cmd);
}
void MyFrame::OnShowCommandForExt(wxCommandEvent& WXUNUSED(event))
{
static wxString s_ext;
wxString ext = wxGetTextFromUser
(
"Enter the extension without leading dot",
"Exec sample",
s_ext,
this
);
if ( ext.empty() )
return;
s_ext = ext;
wxScopedPtr<wxFileType>
ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext));
if ( !ft )
{
wxLogError("Information for extension \"%s\" not found", ext);
return;
}
const wxString cmd = ft->GetOpenCommand("file." + ext);
if ( cmd.empty() )
{
wxLogWarning("Open command for extension \"%s\" not defined.", ext);
return;
}
wxLogMessage("Open command for files of extension \"%s\" is\n%s",
ext, cmd);
}
void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
{
if ( !AskUserForFileName() )
return;
if ( !wxLaunchDefaultApplication(gs_lastFile) )
{
wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
}
}
void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
{
static wxString s_url(wxT("http://www.wxwidgets.org/"));
wxString filename = wxGetTextFromUser
(
wxT("Enter the URL"),
wxT("exec sample"),
s_url,
this
);
if ( filename.empty() )
return;
s_url = filename;
if ( !wxLaunchDefaultBrowser(s_url) )
{
wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
}
}
// ----------------------------------------------------------------------------
// DDE stuff
// ----------------------------------------------------------------------------
#ifdef __WINDOWS__
bool MyFrame::GetDDEServer()
{
wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
DIALOG_TITLE, m_server);
if ( !server )
return false;
m_server = server;
wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
if ( !topic )
return false;
m_topic = topic;
wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
if ( !cmd )
return false;
m_cmdDde = cmd;
return true;
}
void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
{
if ( !GetDDEServer() )
return;
wxDDEClient client;
wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
if ( !conn )
{
wxLogError(wxT("Failed to connect to the DDE server '%s'."),
m_server.c_str());
}
else
{
if ( !conn->Execute(m_cmdDde) )
{
wxLogError(wxT("Failed to execute command '%s' via DDE."),
m_cmdDde.c_str());
}
else
{
wxLogStatus(wxT("Successfully executed DDE command"));
}
}
}
void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
{
if ( !GetDDEServer() )
return;
wxDDEClient client;
wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
if ( !conn )
{
wxLogError(wxT("Failed to connect to the DDE server '%s'."),
m_server.c_str());
}
else
{
if ( !conn->Request(m_cmdDde) )
{
wxLogError(wxT("Failed to send request '%s' via DDE."),
m_cmdDde.c_str());
}
else
{
wxLogStatus(wxT("Successfully sent DDE request."));
}
}
}
#endif // __WINDOWS__
// ----------------------------------------------------------------------------
// various helpers
// ----------------------------------------------------------------------------
// input polling
void MyFrame::OnIdle(wxIdleEvent& event)
{
size_t count = m_running.GetCount();
for ( size_t n = 0; n < count; n++ )
{
if ( m_running[n]->HasInput() )
{
event.RequestMore();
}
}
}
void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
{
wxWakeUpIdle();
}
void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
{
static unsigned long s_ticks = 0;
SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
}
void MyFrame::OnProcessTerminated(MyPipedProcess *process)
{
RemovePipedProcess(process);
}
void MyFrame::OnAsyncTermination(MyProcess *process)
{
m_allAsync.Remove(process);
delete process;
}
void MyFrame::AddPipedProcess(MyPipedProcess *process)
{
if ( m_running.IsEmpty() )
{
// we want to start getting the timer events to ensure that a
// steady stream of idle events comes in -- otherwise we
// wouldn't be able to poll the child process input
m_timerIdleWakeUp.Start(100);
}
//else: the timer is already running
m_running.Add(process);
m_allAsync.Add(process);
}
void MyFrame::RemovePipedProcess(MyPipedProcess *process)
{
m_running.Remove(process);
if ( m_running.IsEmpty() )
{
// we don't need to get idle events all the time any more
m_timerIdleWakeUp.Stop();
}
}
void MyFrame::ShowOutput(const wxString& cmd,
const wxArrayString& output,
const wxString& title)
{
size_t count = output.GetCount();
if ( !count )
return;
m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
title.c_str(), cmd.c_str()));
for ( size_t n = 0; n < count; n++ )
{
m_lbox->Append(output[n]);
}
m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
title.Lower().c_str()));
}
// ----------------------------------------------------------------------------
// MyProcess
// ----------------------------------------------------------------------------
void MyProcess::OnTerminate(int pid, int status)
{
wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
pid, m_cmd.c_str(), status);
m_parent->OnAsyncTermination(this);
}
// ----------------------------------------------------------------------------
// MyPipedProcess
// ----------------------------------------------------------------------------
bool MyPipedProcess::HasInput()
{
bool hasInput = false;
if ( IsInputAvailable() )
{
wxTextInputStream tis(*GetInputStream());
// this assumes that the output is always line buffered
wxString msg;
msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
m_parent->GetLogListBox()->Append(msg);
hasInput = true;
}
if ( IsErrorAvailable() )
{
wxTextInputStream tis(*GetErrorStream());
// this assumes that the output is always line buffered
wxString msg;
msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
m_parent->GetLogListBox()->Append(msg);
hasInput = true;
}
return hasInput;
}
void MyPipedProcess::OnTerminate(int pid, int status)
{
// show the rest of the output
while ( HasInput() )
;
m_parent->OnProcessTerminated(this);
MyProcess::OnTerminate(pid, status);
}
// ----------------------------------------------------------------------------
// MyPipedProcess2
// ----------------------------------------------------------------------------
bool MyPipedProcess2::HasInput()
{
if ( !m_input.empty() )
{
wxTextOutputStream os(*GetOutputStream());
os.WriteString(m_input);
CloseOutput();
m_input.clear();
// call us once again - may be we'll have output
return true;
}
return MyPipedProcess::HasInput();
}
// ============================================================================
// MyPipeFrame implementation
// ============================================================================
MyPipeFrame::MyPipeFrame(wxFrame *parent,
const wxString& cmd,
wxProcess *process)
: wxFrame(parent, wxID_ANY, cmd),
m_process(process),
// in a real program we'd check that the streams are !NULL here
m_out(*process->GetOutputStream()),
m_in(*process->GetInputStream()),
m_err(*process->GetErrorStream())
{
m_process->SetNextHandler(this);
wxPanel *panel = new wxPanel(this, wxID_ANY);
m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_PROCESS_ENTER);
m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_RICH);
m_textIn->SetEditable(false);
m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_RICH);
m_textErr->SetEditable(false);
wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
sizerBtns->
Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
sizerBtns->
Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
sizerBtns->
Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
sizerBtns->
Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
panel->SetSizer(sizerTop);
sizerTop->Fit(this);
Show();
}
void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
{
#if wxUSE_FILEDLG
wxFileDialog filedlg(this, wxT("Select file to send"));
if ( filedlg.ShowModal() != wxID_OK )
return;
wxFFile file(filedlg.GetFilename(), wxT("r"));
wxString data;
if ( !file.IsOpened() || !file.ReadAll(&data) )
return;
// can't write the entire string at once, this risk overflowing the pipe
// and we would dead lock
size_t len = data.length();
const wxChar *pc = data.c_str();
while ( len )
{
const size_t CHUNK_SIZE = 4096;
m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
// note that not all data could have been written as we don't block on
// the write end of the pipe
const size_t lenChunk = m_out.LastWrite();
pc += lenChunk;
len -= lenChunk;
DoGet();
}
#endif // wxUSE_FILEDLG
}
void MyPipeFrame::DoGet()
{
// we don't have any way to be notified when any input appears on the
// stream so we have to poll it :-(
DoGetFromStream(m_textIn, m_in);
DoGetFromStream(m_textErr, m_err);
}
void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
{
while ( in.CanRead() )
{
char buffer[4096];
buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
text->AppendText(buffer);
}
}
void MyPipeFrame::DoClose()
{
m_process->CloseOutput();
DisableInput();
}
void MyPipeFrame::DisableInput()
{
m_textOut->SetEditable(false);
FindWindow(Exec_Btn_Send)->Disable();
FindWindow(Exec_Btn_SendFile)->Disable();
FindWindow(Exec_Btn_Close)->Disable();
}
void MyPipeFrame::DisableOutput()
{
FindWindow(Exec_Btn_Get)->Disable();
}
void MyPipeFrame::OnClose(wxCloseEvent& event)
{
if ( m_process )
{
// we're not interested in getting the process termination notification
// if we are closing it ourselves
wxProcess *process = m_process;
m_process = NULL;
process->SetNextHandler(NULL);
process->CloseOutput();
}
event.Skip();
}
void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
{
DoGet();
wxDELETE(m_process);
wxLogWarning(wxT("The other process has terminated, closing"));
DisableInput();
DisableOutput();
}