1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
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"));