1
/////////////////////////////////////////////////////////////////////////////
3
// Purpose: exec sample demonstrates wxExecute and related functions
4
// Author: Vadim Zeitlin
7
// RCS-ID: $Id: exec.cpp 70412 2012-01-20 16:51:09Z DS $
8
// Copyright: (c) Vadim Zeitlin
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
// ============================================================================
14
// ============================================================================
16
// ----------------------------------------------------------------------------
18
// ----------------------------------------------------------------------------
20
// For compilers that support precompilation, includes "wx/wx.h".
21
#include "wx/wxprec.h"
27
// for all others, include the necessary headers (this file is usually all you
28
// need because it includes almost all "standard" wxWidgets headers
40
#include "wx/msgdlg.h"
41
#include "wx/textdlg.h"
42
#include "wx/filedlg.h"
43
#include "wx/choicdlg.h"
45
#include "wx/button.h"
46
#include "wx/checkbox.h"
47
#include "wx/stattext.h"
48
#include "wx/textctrl.h"
49
#include "wx/listbox.h"
54
#include "wx/txtstrm.h"
55
#include "wx/numdlg.h"
56
#include "wx/textdlg.h"
58
#include "wx/stopwatch.h"
60
#include "wx/process.h"
62
#include "wx/mimetype.h"
68
// ----------------------------------------------------------------------------
69
// the usual application and main frame classes
70
// ----------------------------------------------------------------------------
72
// Define a new application type, each program should derive a class from wxApp
73
class MyApp : public wxApp
76
// override base class virtuals
77
// ----------------------------
79
// this one is called on application startup and is a good place for the app
80
// initialization (doing it here and not in the ctor allows to have an error
81
// return: if OnInit() returns false, the application terminates)
82
virtual bool OnInit();
85
// Define an array of process pointers used by MyFrame
87
WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyPipedProcessesArray);
90
WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
92
// Define a new frame type: this is going to be our main frame
93
class MyFrame : public wxFrame
97
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
100
// event handlers (these functions should _not_ be virtual)
101
void OnQuit(wxCommandEvent& event);
103
void OnKill(wxCommandEvent& event);
105
void OnClear(wxCommandEvent& event);
107
void OnBeginBusyCursor(wxCommandEvent& event);
108
void OnEndBusyCursor(wxCommandEvent& event);
110
void OnSyncExec(wxCommandEvent& event);
111
void OnAsyncExec(wxCommandEvent& event);
112
void OnShell(wxCommandEvent& event);
113
void OnExecWithRedirect(wxCommandEvent& event);
114
void OnExecWithPipe(wxCommandEvent& event);
116
void OnPOpen(wxCommandEvent& event);
118
void OnFileExec(wxCommandEvent& event);
119
void OnFileLaunch(wxCommandEvent& event);
120
void OnOpenURL(wxCommandEvent& event);
122
void OnAbout(wxCommandEvent& event);
124
// polling output of async processes
125
void OnIdleTimer(wxTimerEvent& event);
126
void OnIdle(wxIdleEvent& event);
128
// for MyPipedProcess
129
void OnProcessTerminated(MyPipedProcess *process);
130
wxListBox *GetLogListBox() const { return m_lbox; }
133
void OnAsyncTermination(MyProcess *process);
135
// timer updating a counter in the background
136
void OnBgTimer(wxTimerEvent& event);
139
void ShowOutput(const wxString& cmd,
140
const wxArrayString& output,
141
const wxString& title);
143
int GetExecFlags() const;
145
void DoAsyncExec(const wxString& cmd);
147
void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
149
void AddPipedProcess(MyPipedProcess *process);
150
void RemovePipedProcess(MyPipedProcess *process);
153
// the PID of the last process we launched asynchronously
156
// last command we executed
160
void OnDDEExec(wxCommandEvent& event);
161
void OnDDERequest(wxCommandEvent& event);
165
// last params of a DDE transaction
169
#endif // __WINDOWS__
173
// array of running processes with redirected IO
174
MyPipedProcessesArray m_running;
176
// array of all asynchrously running processes
177
MyProcessesArray m_allAsync;
179
// the idle event wake up timer
180
wxTimer m_timerIdleWakeUp;
182
// a background timer allowing to easily check visually whether the
183
// messages are processed or not
186
// any class wishing to process wxWidgets events must use this macro
187
DECLARE_EVENT_TABLE()
190
// ----------------------------------------------------------------------------
191
// MyPipeFrame: allows the user to communicate with the child process
192
// ----------------------------------------------------------------------------
194
class MyPipeFrame : public wxFrame
197
MyPipeFrame(wxFrame *parent,
202
void OnTextEnter(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
203
void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
204
void OnBtnSendFile(wxCommandEvent& WXUNUSED(event));
205
void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); }
206
void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); }
208
void OnClose(wxCloseEvent& event);
210
void OnProcessTerm(wxProcessEvent& event);
214
wxString s(m_textOut->GetValue());
216
m_out.Write(s.c_str(), s.length());
226
void DoGetFromStream(wxTextCtrl *text, wxInputStream& in);
228
void DisableOutput();
231
wxProcess *m_process;
233
wxOutputStream &m_out;
237
wxTextCtrl *m_textOut,
241
DECLARE_EVENT_TABLE()
244
// ----------------------------------------------------------------------------
245
// wxProcess-derived classes
246
// ----------------------------------------------------------------------------
248
// This is the handler for process termination events
249
class MyProcess : public wxProcess
252
MyProcess(MyFrame *parent, const wxString& cmd)
253
: wxProcess(parent), m_cmd(cmd)
258
// instead of overriding this virtual function we might as well process the
259
// event from it in the frame class - this might be more convenient in some
261
virtual void OnTerminate(int pid, int status);
268
// A specialization of MyProcess for redirecting the output
269
class MyPipedProcess : public MyProcess
272
MyPipedProcess(MyFrame *parent, const wxString& cmd)
273
: MyProcess(parent, cmd)
278
virtual void OnTerminate(int pid, int status);
280
virtual bool HasInput();
283
// A version of MyPipedProcess which also sends input to the stdin of the
285
class MyPipedProcess2 : public MyPipedProcess
288
MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
289
: MyPipedProcess(parent, cmd), m_input(input)
293
virtual bool HasInput();
299
// ----------------------------------------------------------------------------
301
// ----------------------------------------------------------------------------
303
// IDs for the controls and the menu commands
314
Exec_BeginBusyCursor,
327
Exec_Flags_HideConsole,
328
Exec_Flags_ShowConsole,
330
Exec_About = wxID_ABOUT,
333
Exec_Btn_Send = 1000,
339
static const wxChar *DIALOG_TITLE = wxT("Exec sample");
341
// ----------------------------------------------------------------------------
342
// event tables and other macros for wxWidgets
343
// ----------------------------------------------------------------------------
345
// the event tables connect the wxWidgets events with the functions (event
346
// handlers) which process them. It can be also done at run-time, but for the
347
// simple menu events like this the static method is much simpler.
348
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
349
EVT_MENU(Exec_Quit, MyFrame::OnQuit)
350
EVT_MENU(Exec_Kill, MyFrame::OnKill)
351
EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
352
EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor)
353
EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor)
355
EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
356
EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
357
EVT_MENU(Exec_Shell, MyFrame::OnShell)
358
EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
359
EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
361
EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
363
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
364
EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
365
EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
368
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
369
EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
370
#endif // __WINDOWS__
372
EVT_MENU(Exec_About, MyFrame::OnAbout)
374
EVT_IDLE(MyFrame::OnIdle)
376
EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
377
EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
380
BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
381
EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
382
EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile)
383
EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
384
EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose)
386
EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter)
388
EVT_CLOSE(MyPipeFrame::OnClose)
390
EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm)
393
// Create a new application object: this macro will allow wxWidgets to create
394
// the application object during program execution (it's better than using a
395
// static object for many reasons) and also declares the accessor function
396
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
400
// ============================================================================
402
// ============================================================================
404
// ----------------------------------------------------------------------------
405
// the application class
406
// ----------------------------------------------------------------------------
408
// `Main program' equivalent: the program execution "starts" here
411
if ( !wxApp::OnInit() )
414
// Create the main application window
415
MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"),
416
wxDefaultPosition, wxSize(500, 140));
421
// success: wxApp::OnRun() will be called which will enter the main message
422
// loop and the application will run. If we returned false here, the
423
// application would exit immediately.
427
// ----------------------------------------------------------------------------
429
// ----------------------------------------------------------------------------
432
#pragma warning(disable: 4355) // this used in base member initializer list
436
MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
437
: wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
438
m_timerIdleWakeUp(this, Exec_TimerIdle),
439
m_timerBg(this, Exec_TimerBg)
444
// we need this in order to allow the about menu relocation, since ABOUT is
445
// not the default id of the about menu
446
wxApp::s_macAboutMenuItemId = Exec_About;
450
wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
451
menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
452
wxT("Kill a process by PID"));
453
menuFile->AppendSeparator();
454
menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
455
wxT("Clear the log window"));
456
menuFile->AppendSeparator();
457
menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
458
menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
459
menuFile->AppendSeparator();
460
menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
462
wxMenu *flagsMenu = new wxMenu;
463
flagsMenu->AppendCheckItem(Exec_Flags_HideConsole, "Always &hide console");
464
flagsMenu->AppendCheckItem(Exec_Flags_ShowConsole, "Always &show console");
465
flagsMenu->AppendCheckItem(Exec_Flags_NoEvents, "Disable &events",
466
"This flag is valid for sync execution only");
468
wxMenu *execMenu = new wxMenu;
469
execMenu->AppendSubMenu(flagsMenu, "Execution flags");
470
execMenu->AppendSeparator();
471
execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
472
wxT("Launch a program and return when it terminates"));
473
execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
474
wxT("Launch a program and return immediately"));
475
execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
476
wxT("Launch a shell and execute a command in it"));
477
execMenu->AppendSeparator();
478
execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
479
wxT("Launch a program and capture its output"));
480
execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
481
wxT("Pipe a string through a filter"));
482
execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
483
wxT("Open a pipe to and from another program"));
485
execMenu->AppendSeparator();
486
execMenu->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
487
wxT("Launch the command to open this kind of files"));
488
execMenu->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
489
wxT("Launch the default application associated with the file"));
490
execMenu->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
491
wxT("Launch the default browser with the given URL"));
493
execMenu->AppendSeparator();
494
execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
495
execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
498
wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
499
helpMenu->Append(Exec_About, wxT("&About\tF1"), wxT("Show about dialog"));
501
// now append the freshly created menu to the menu bar...
502
wxMenuBar *menuBar = new wxMenuBar();
503
menuBar->Append(menuFile, wxT("&File"));
504
menuBar->Append(execMenu, wxT("&Exec"));
505
menuBar->Append(helpMenu, wxT("&Help"));
507
// ... and attach this menu bar to the frame
510
// create the listbox in which we will show misc messages as they come
511
m_lbox = new wxListBox(this, wxID_ANY);
512
wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
513
wxFONTWEIGHT_NORMAL);
515
m_lbox->SetFont(font);
518
// create a status bar just for fun (by default with 1 pane only)
520
SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
521
#endif // wxUSE_STATUSBAR
523
m_timerBg.Start(1000);
528
// any processes left until now must be deleted manually: normally this is
529
// done when the associated process terminates but it must be still running
530
// if this didn't happen until now
531
for ( size_t n = 0; n < m_allAsync.size(); n++ )
533
delete m_allAsync[n];
537
// ----------------------------------------------------------------------------
538
// event handlers: file and help menu
539
// ----------------------------------------------------------------------------
541
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
543
// true is to force the frame to close
547
void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
552
void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
557
void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
562
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
564
wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
565
wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
568
void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
570
long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
572
wxT("Exec question"),
574
// we need the full unsigned int range
585
static const wxString signalNames[] =
587
wxT("Just test (SIGNONE)"),
588
wxT("Hangup (SIGHUP)"),
589
wxT("Interrupt (SIGINT)"),
590
wxT("Quit (SIGQUIT)"),
591
wxT("Illegal instruction (SIGILL)"),
592
wxT("Trap (SIGTRAP)"),
593
wxT("Abort (SIGABRT)"),
594
wxT("Emulated trap (SIGEMT)"),
595
wxT("FP exception (SIGFPE)"),
596
wxT("Kill (SIGKILL)"),
598
wxT("Segment violation (SIGSEGV)"),
599
wxT("System (SIGSYS)"),
600
wxT("Broken pipe (SIGPIPE)"),
601
wxT("Alarm (SIGALRM)"),
602
wxT("Terminate (SIGTERM)"),
605
static int s_sigLast = wxSIGNONE;
606
int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
607
wxT("Exec question"),
608
WXSIZEOF(signalNames), signalNames,
614
wxFAIL_MSG( wxT("unexpected return value") );
642
if ( sig == wxSIGNONE )
644
// This simply calls Kill(wxSIGNONE) but using it is more convenient.
645
if ( wxProcess::Exists(pid) )
647
wxLogStatus(wxT("Process %ld is running."), pid);
651
wxLogStatus(wxT("No process with pid = %ld."), pid);
656
wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
657
if ( rc == wxKILL_OK )
659
wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
663
static const wxChar *errorText[] =
666
wxT("signal not supported"),
667
wxT("permission denied"),
668
wxT("no such process"),
669
wxT("unspecified error"),
672
wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
673
pid, sig, errorText[rc]);
678
// ----------------------------------------------------------------------------
679
// execution options dialog
680
// ----------------------------------------------------------------------------
682
enum ExecQueryDialogID
689
class ExecQueryDialog : public wxDialog
692
ExecQueryDialog(const wxString& cmd);
694
wxString GetExecutable() const
696
return m_executable->GetValue();
699
wxString GetWorkDir() const
701
return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
704
void GetEnvironment(wxEnvVariableHashMap& env);
707
void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
709
event.Enable(m_useCWD->GetValue());
712
void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
714
event.Enable(m_useEnv->GetValue());
717
wxTextCtrl* m_executable;
718
wxTextCtrl* m_cwdtext;
719
wxTextCtrl* m_envtext;
720
wxCheckBox* m_useCWD;
721
wxCheckBox* m_useEnv;
723
DECLARE_EVENT_TABLE()
726
BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
727
EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
728
EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
731
ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
732
: wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
733
wxDefaultPosition, wxDefaultSize,
734
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
736
wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
738
m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
739
m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
740
m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
741
wxDefaultPosition, wxSize(300, 200),
742
wxTE_MULTILINE|wxHSCROLL);
744
const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
745
globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
747
globalSizer->Add(m_executable, flagsExpand);
749
m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
750
globalSizer->Add(m_useCWD, flagsExpand);
751
globalSizer->Add(m_cwdtext, flagsExpand);
753
m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
754
globalSizer->Add(m_useEnv, flagsExpand);
755
globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
757
globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
758
SetSizerAndFit(globalSizer);
761
m_executable->SetValue(cmd);
762
m_cwdtext->SetValue(wxGetCwd());
763
wxEnvVariableHashMap env;
764
if ( wxGetEnvMap(&env) )
766
for ( wxEnvVariableHashMap::iterator it = env.begin();
770
m_envtext->AppendText(it->first + '=' + it->second + '\n');
773
m_useCWD->SetValue(false);
774
m_useEnv->SetValue(false);
777
void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
780
if ( m_useEnv->GetValue() )
785
const int nb = m_envtext->GetNumberOfLines();
786
for ( int l = 0; l < nb; l++ )
788
const wxString line = m_envtext->GetLineText(l).Trim();
792
name = line.BeforeFirst('=', &value);
795
wxLogWarning("Skipping invalid environment line \"%s\".", line);
805
static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
807
ExecQueryDialog dialog(cmd);
809
if ( dialog.ShowModal() != wxID_OK )
812
cmd = dialog.GetExecutable();
813
env.cwd = dialog.GetWorkDir();
814
dialog.GetEnvironment(env.env);
819
// ----------------------------------------------------------------------------
820
// event handlers: exec menu
821
// ----------------------------------------------------------------------------
823
int MyFrame::GetExecFlags() const
825
wxMenuBar* const mbar = GetMenuBar();
829
if ( mbar->IsChecked(Exec_Flags_HideConsole) )
830
flags |= wxEXEC_HIDE_CONSOLE;
831
if ( mbar->IsChecked(Exec_Flags_ShowConsole) )
832
flags |= wxEXEC_SHOW_CONSOLE;
833
if ( mbar->IsChecked(Exec_Flags_NoEvents) )
834
flags |= wxEXEC_NOEVENTS;
839
void MyFrame::DoAsyncExec(const wxString& cmd)
841
MyProcess * const process = new MyProcess(this, cmd);
842
m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | GetExecFlags(), process);
845
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
851
wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
855
// the parent frame keeps track of all async processes as it needs to
856
// free them if we exit before the child process terminates
857
AddAsyncProcess(process);
861
void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
865
if ( !QueryExec(cmd, env) )
868
wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
870
int code = wxExecute(cmd, wxEXEC_SYNC | GetExecFlags(), NULL, &env);
872
wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
878
void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
880
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
890
void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
892
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
899
int code = wxShell(cmd);
900
wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
905
void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
910
m_cmdLast = "type Makefile.in";
912
m_cmdLast = "cat -n Makefile";
916
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
924
switch ( wxMessageBox(wxT("Execute it synchronously?"),
925
wxT("Exec question"),
926
wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
942
wxLogStatus("\"%s\" is running please wait...", cmd);
946
wxArrayString output, errors;
947
int code = wxExecute(cmd, output, errors);
949
wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
950
cmd, sw.Time(), code);
954
ShowOutput(cmd, output, wxT("Output"));
955
ShowOutput(cmd, errors, wxT("Errors"));
960
MyPipedProcess *process = new MyPipedProcess(this, cmd);
961
if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
963
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
969
AddPipedProcess(process);
976
void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
979
m_cmdLast = wxT("tr [a-z] [A-Z]");
981
wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
988
wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
993
// always execute the filter asynchronously
994
MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
995
long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
998
wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
1000
AddPipedProcess(process);
1004
wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
1012
void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
1014
wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
1020
wxProcess *process = wxProcess::Open(cmd);
1023
wxLogError(wxT("Failed to launch the command."));
1027
wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
1029
wxOutputStream *out = process->GetOutputStream();
1032
wxLogError(wxT("Failed to connect to child stdin"));
1036
wxInputStream *in = process->GetInputStream();
1039
wxLogError(wxT("Failed to connect to child stdout"));
1043
new MyPipeFrame(this, cmd, process);
1046
static wxString gs_lastFile;
1048
static bool AskUserForFileName()
1053
filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
1054
#else // !wxUSE_FILEDLG
1055
filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1057
#endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1059
if ( filename.empty() )
1062
gs_lastFile = filename;
1067
void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1069
if ( !AskUserForFileName() )
1072
wxString ext = gs_lastFile.AfterLast(wxT('.'));
1073
wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1076
wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1082
bool ok = ft->GetOpenCommand(&cmd,
1083
wxFileType::MessageParameters(gs_lastFile));
1087
wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1095
void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1097
if ( !AskUserForFileName() )
1100
if ( !wxLaunchDefaultApplication(gs_lastFile) )
1102
wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1106
void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1108
static wxString s_url(wxT("http://www.wxwidgets.org/"));
1110
wxString filename = wxGetTextFromUser
1112
wxT("Enter the URL"),
1118
if ( filename.empty() )
1123
if ( !wxLaunchDefaultBrowser(s_url) )
1125
wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
1129
// ----------------------------------------------------------------------------
1131
// ----------------------------------------------------------------------------
1135
bool MyFrame::GetDDEServer()
1137
wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
1138
DIALOG_TITLE, m_server);
1144
wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
1150
wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
1159
void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1161
if ( !GetDDEServer() )
1165
wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1168
wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1173
if ( !conn->Execute(m_cmdDde) )
1175
wxLogError(wxT("Failed to execute command '%s' via DDE."),
1180
wxLogStatus(wxT("Successfully executed DDE command"));
1185
void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1187
if ( !GetDDEServer() )
1191
wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1194
wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1199
if ( !conn->Request(m_cmdDde) )
1201
wxLogError(wxT("Failed to send request '%s' via DDE."),
1206
wxLogStatus(wxT("Successfully sent DDE request."));
1211
#endif // __WINDOWS__
1213
// ----------------------------------------------------------------------------
1215
// ----------------------------------------------------------------------------
1218
void MyFrame::OnIdle(wxIdleEvent& event)
1220
size_t count = m_running.GetCount();
1221
for ( size_t n = 0; n < count; n++ )
1223
if ( m_running[n]->HasInput() )
1225
event.RequestMore();
1230
void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1235
void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1237
static unsigned long s_ticks = 0;
1238
SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1241
void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1243
RemovePipedProcess(process);
1246
void MyFrame::OnAsyncTermination(MyProcess *process)
1248
m_allAsync.Remove(process);
1253
void MyFrame::AddPipedProcess(MyPipedProcess *process)
1255
if ( m_running.IsEmpty() )
1257
// we want to start getting the timer events to ensure that a
1258
// steady stream of idle events comes in -- otherwise we
1259
// wouldn't be able to poll the child process input
1260
m_timerIdleWakeUp.Start(100);
1262
//else: the timer is already running
1264
m_running.Add(process);
1265
m_allAsync.Add(process);
1268
void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1270
m_running.Remove(process);
1272
if ( m_running.IsEmpty() )
1274
// we don't need to get idle events all the time any more
1275
m_timerIdleWakeUp.Stop();
1279
void MyFrame::ShowOutput(const wxString& cmd,
1280
const wxArrayString& output,
1281
const wxString& title)
1283
size_t count = output.GetCount();
1287
m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1288
title.c_str(), cmd.c_str()));
1290
for ( size_t n = 0; n < count; n++ )
1292
m_lbox->Append(output[n]);
1295
m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1296
title.Lower().c_str()));
1299
// ----------------------------------------------------------------------------
1301
// ----------------------------------------------------------------------------
1303
void MyProcess::OnTerminate(int pid, int status)
1305
wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1306
pid, m_cmd.c_str(), status);
1308
m_parent->OnAsyncTermination(this);
1311
// ----------------------------------------------------------------------------
1313
// ----------------------------------------------------------------------------
1315
bool MyPipedProcess::HasInput()
1317
bool hasInput = false;
1319
if ( IsInputAvailable() )
1321
wxTextInputStream tis(*GetInputStream());
1323
// this assumes that the output is always line buffered
1325
msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1327
m_parent->GetLogListBox()->Append(msg);
1332
if ( IsErrorAvailable() )
1334
wxTextInputStream tis(*GetErrorStream());
1336
// this assumes that the output is always line buffered
1338
msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1340
m_parent->GetLogListBox()->Append(msg);
1348
void MyPipedProcess::OnTerminate(int pid, int status)
1350
// show the rest of the output
1351
while ( HasInput() )
1354
m_parent->OnProcessTerminated(this);
1356
MyProcess::OnTerminate(pid, status);
1359
// ----------------------------------------------------------------------------
1361
// ----------------------------------------------------------------------------
1363
bool MyPipedProcess2::HasInput()
1365
if ( !m_input.empty() )
1367
wxTextOutputStream os(*GetOutputStream());
1368
os.WriteString(m_input);
1373
// call us once again - may be we'll have output
1377
return MyPipedProcess::HasInput();
1380
// ============================================================================
1381
// MyPipeFrame implementation
1382
// ============================================================================
1384
MyPipeFrame::MyPipeFrame(wxFrame *parent,
1385
const wxString& cmd,
1387
: wxFrame(parent, wxID_ANY, cmd),
1389
// in a real program we'd check that the streams are !NULL here
1390
m_out(*process->GetOutputStream()),
1391
m_in(*process->GetInputStream()),
1392
m_err(*process->GetErrorStream())
1394
m_process->SetNextHandler(this);
1396
wxPanel *panel = new wxPanel(this, wxID_ANY);
1398
m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1399
wxDefaultPosition, wxDefaultSize,
1400
wxTE_PROCESS_ENTER);
1401
m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1402
wxDefaultPosition, wxDefaultSize,
1403
wxTE_MULTILINE | wxTE_RICH);
1404
m_textIn->SetEditable(false);
1405
m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1406
wxDefaultPosition, wxDefaultSize,
1407
wxTE_MULTILINE | wxTE_RICH);
1408
m_textErr->SetEditable(false);
1410
wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1411
sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1413
wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1415
Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1417
Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1419
Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1421
Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1423
sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1424
sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1425
sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1427
panel->SetSizer(sizerTop);
1428
sizerTop->Fit(this);
1433
void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1436
wxFileDialog filedlg(this, wxT("Select file to send"));
1437
if ( filedlg.ShowModal() != wxID_OK )
1440
wxFFile file(filedlg.GetFilename(), wxT("r"));
1442
if ( !file.IsOpened() || !file.ReadAll(&data) )
1445
// can't write the entire string at once, this risk overflowing the pipe
1446
// and we would dead lock
1447
size_t len = data.length();
1448
const wxChar *pc = data.c_str();
1451
const size_t CHUNK_SIZE = 4096;
1452
m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1454
// note that not all data could have been written as we don't block on
1455
// the write end of the pipe
1456
const size_t lenChunk = m_out.LastWrite();
1463
#endif // wxUSE_FILEDLG
1466
void MyPipeFrame::DoGet()
1468
// we don't have any way to be notified when any input appears on the
1469
// stream so we have to poll it :-(
1470
DoGetFromStream(m_textIn, m_in);
1471
DoGetFromStream(m_textErr, m_err);
1474
void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1476
while ( in.CanRead() )
1479
buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
1481
text->AppendText(buffer);
1485
void MyPipeFrame::DoClose()
1487
m_process->CloseOutput();
1492
void MyPipeFrame::DisableInput()
1494
m_textOut->SetEditable(false);
1495
FindWindow(Exec_Btn_Send)->Disable();
1496
FindWindow(Exec_Btn_SendFile)->Disable();
1497
FindWindow(Exec_Btn_Close)->Disable();
1500
void MyPipeFrame::DisableOutput()
1502
FindWindow(Exec_Btn_Get)->Disable();
1505
void MyPipeFrame::OnClose(wxCloseEvent& event)
1509
// we're not interested in getting the process termination notification
1510
// if we are closing it ourselves
1511
wxProcess *process = m_process;
1513
process->SetNextHandler(NULL);
1515
process->CloseOutput();
1521
void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1525
wxDELETE(m_process);
1527
wxLogWarning(wxT("The other process has terminated, closing"));