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 OnSyncNoEventsExec(wxCommandEvent
& event
);
112 void OnAsyncExec(wxCommandEvent
& event
);
113 void OnShell(wxCommandEvent
& event
);
114 void OnExecWithRedirect(wxCommandEvent
& event
);
115 void OnExecWithPipe(wxCommandEvent
& event
);
117 void OnPOpen(wxCommandEvent
& event
);
119 void OnFileExec(wxCommandEvent
& event
);
120 void OnFileLaunch(wxCommandEvent
& event
);
121 void OnOpenURL(wxCommandEvent
& event
);
123 void OnAbout(wxCommandEvent
& event
);
125 // polling output of async processes
126 void OnIdleTimer(wxTimerEvent
& event
);
127 void OnIdle(wxIdleEvent
& event
);
129 // for MyPipedProcess
130 void OnProcessTerminated(MyPipedProcess
*process
);
131 wxListBox
*GetLogListBox() const { return m_lbox
; }
134 void OnAsyncTermination(MyProcess
*process
);
136 // timer updating a counter in the background
137 void OnBgTimer(wxTimerEvent
& event
);
140 void ShowOutput(const wxString
& cmd
,
141 const wxArrayString
& output
,
142 const wxString
& title
);
144 void DoAsyncExec(const wxString
& cmd
);
146 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
148 void AddPipedProcess(MyPipedProcess
*process
);
149 void RemovePipedProcess(MyPipedProcess
*process
);
152 // the PID of the last process we launched asynchronously
155 // last command we executed
159 void OnDDEExec(wxCommandEvent
& event
);
160 void OnDDERequest(wxCommandEvent
& event
);
164 // last params of a DDE transaction
168 #endif // __WINDOWS__
172 // array of running processes with redirected IO
173 MyPipedProcessesArray m_running
;
175 // array of all asynchrously running processes
176 MyProcessesArray m_allAsync
;
178 // the idle event wake up timer
179 wxTimer m_timerIdleWakeUp
;
181 // a background timer allowing to easily check visually whether the
182 // messages are processed or not
185 // any class wishing to process wxWidgets events must use this macro
186 DECLARE_EVENT_TABLE()
189 // ----------------------------------------------------------------------------
190 // MyPipeFrame: allows the user to communicate with the child process
191 // ----------------------------------------------------------------------------
193 class MyPipeFrame
: public wxFrame
196 MyPipeFrame(wxFrame
*parent
,
201 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
202 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
203 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
204 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
205 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
207 void OnClose(wxCloseEvent
& event
);
209 void OnProcessTerm(wxProcessEvent
& event
);
213 wxString
s(m_textOut
->GetValue());
215 m_out
.Write(s
.c_str(), s
.length());
225 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
227 void DisableOutput();
230 wxProcess
*m_process
;
232 wxOutputStream
&m_out
;
236 wxTextCtrl
*m_textOut
,
240 DECLARE_EVENT_TABLE()
243 // ----------------------------------------------------------------------------
244 // wxProcess-derived classes
245 // ----------------------------------------------------------------------------
247 // This is the handler for process termination events
248 class MyProcess
: public wxProcess
251 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
252 : wxProcess(parent
), m_cmd(cmd
)
257 // instead of overriding this virtual function we might as well process the
258 // event from it in the frame class - this might be more convenient in some
260 virtual void OnTerminate(int pid
, int status
);
267 // A specialization of MyProcess for redirecting the output
268 class MyPipedProcess
: public MyProcess
271 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
272 : MyProcess(parent
, cmd
)
277 virtual void OnTerminate(int pid
, int status
);
279 virtual bool HasInput();
282 // A version of MyPipedProcess which also sends input to the stdin of the
284 class MyPipedProcess2
: public MyPipedProcess
287 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
288 : MyPipedProcess(parent
, cmd
), m_input(input
)
292 virtual bool HasInput();
298 // ----------------------------------------------------------------------------
300 // ----------------------------------------------------------------------------
302 // IDs for the controls and the menu commands
313 Exec_BeginBusyCursor
,
316 Exec_SyncNoEventsExec
,
327 Exec_About
= wxID_ABOUT
,
330 Exec_Btn_Send
= 1000,
336 static const wxChar
*DIALOG_TITLE
= wxT("Exec sample");
338 // ----------------------------------------------------------------------------
339 // event tables and other macros for wxWidgets
340 // ----------------------------------------------------------------------------
342 // the event tables connect the wxWidgets events with the functions (event
343 // handlers) which process them. It can be also done at run-time, but for the
344 // simple menu events like this the static method is much simpler.
345 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
346 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
347 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
348 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
349 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
350 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
352 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
353 EVT_MENU(Exec_SyncNoEventsExec
, MyFrame::OnSyncNoEventsExec
)
354 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
355 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
356 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
357 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
359 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
361 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
362 EVT_MENU(Exec_LaunchFile
, MyFrame::OnFileLaunch
)
363 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
366 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
367 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
368 #endif // __WINDOWS__
370 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
372 EVT_IDLE(MyFrame::OnIdle
)
374 EVT_TIMER(Exec_TimerIdle
, MyFrame::OnIdleTimer
)
375 EVT_TIMER(Exec_TimerBg
, MyFrame::OnBgTimer
)
378 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
379 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
380 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
381 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
382 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
384 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
386 EVT_CLOSE(MyPipeFrame::OnClose
)
388 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
391 // Create a new application object: this macro will allow wxWidgets to create
392 // the application object during program execution (it's better than using a
393 // static object for many reasons) and also declares the accessor function
394 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
398 // ============================================================================
400 // ============================================================================
402 // ----------------------------------------------------------------------------
403 // the application class
404 // ----------------------------------------------------------------------------
406 // `Main program' equivalent: the program execution "starts" here
409 if ( !wxApp::OnInit() )
412 // Create the main application window
413 MyFrame
*frame
= new MyFrame(wxT("Exec wxWidgets sample"),
414 wxDefaultPosition
, wxSize(500, 140));
416 // Show it and tell the application that it's our main window
420 // success: wxApp::OnRun() will be called which will enter the main message
421 // loop and the application will run. If we returned false here, the
422 // application would exit immediately.
426 // ----------------------------------------------------------------------------
428 // ----------------------------------------------------------------------------
431 #pragma warning(disable: 4355) // this used in base member initializer list
435 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
436 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
437 m_timerIdleWakeUp(this, Exec_TimerIdle
),
438 m_timerBg(this, Exec_TimerBg
)
443 // we need this in order to allow the about menu relocation, since ABOUT is
444 // not the default id of the about menu
445 wxApp::s_macAboutMenuItemId
= Exec_About
;
449 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
450 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
451 wxT("Kill a process by PID"));
452 menuFile
->AppendSeparator();
453 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
454 wxT("Clear the log window"));
455 menuFile
->AppendSeparator();
456 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
457 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
458 menuFile
->AppendSeparator();
459 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
461 wxMenu
*execMenu
= new wxMenu
;
462 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
463 wxT("Launch a program and return when it terminates"));
464 execMenu
->Append(Exec_SyncNoEventsExec
, wxT("Sync execution and &block...\tCtrl-B"),
465 wxT("Launch a program and block until it terminates"));
466 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
467 wxT("Launch a program and return immediately"));
468 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
469 wxT("Launch a shell and execute a command in it"));
470 execMenu
->AppendSeparator();
471 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
472 wxT("Launch a program and capture its output"));
473 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
474 wxT("Pipe a string through a filter"));
475 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
476 wxT("Open a pipe to and from another program"));
478 execMenu
->AppendSeparator();
479 execMenu
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
480 wxT("Launch the command to open this kind of files"));
481 execMenu
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
482 wxT("Launch the default application associated with the file"));
483 execMenu
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
484 wxT("Launch the default browser with the given URL"));
486 execMenu
->AppendSeparator();
487 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
488 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
491 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
492 helpMenu
->Append(Exec_About
, wxT("&About...\tF1"), wxT("Show about dialog"));
494 // now append the freshly created menu to the menu bar...
495 wxMenuBar
*menuBar
= new wxMenuBar();
496 menuBar
->Append(menuFile
, wxT("&File"));
497 menuBar
->Append(execMenu
, wxT("&Exec"));
498 menuBar
->Append(helpMenu
, wxT("&Help"));
500 // ... and attach this menu bar to the frame
503 // create the listbox in which we will show misc messages as they come
504 m_lbox
= new wxListBox(this, wxID_ANY
);
505 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
506 wxFONTWEIGHT_NORMAL
);
508 m_lbox
->SetFont(font
);
511 // create a status bar just for fun (by default with 1 pane only)
513 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
514 #endif // wxUSE_STATUSBAR
516 m_timerBg
.Start(1000);
521 // any processes left until now must be deleted manually: normally this is
522 // done when the associated process terminates but it must be still running
523 // if this didn't happen until now
524 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
526 delete m_allAsync
[n
];
530 // ----------------------------------------------------------------------------
531 // event handlers: file and help menu
532 // ----------------------------------------------------------------------------
534 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
536 // true is to force the frame to close
540 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
545 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
550 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
555 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
557 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
558 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
561 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
563 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
565 wxT("Exec question"),
567 // we need the full unsigned int range
578 static const wxString signalNames
[] =
580 wxT("Just test (SIGNONE)"),
581 wxT("Hangup (SIGHUP)"),
582 wxT("Interrupt (SIGINT)"),
583 wxT("Quit (SIGQUIT)"),
584 wxT("Illegal instruction (SIGILL)"),
585 wxT("Trap (SIGTRAP)"),
586 wxT("Abort (SIGABRT)"),
587 wxT("Emulated trap (SIGEMT)"),
588 wxT("FP exception (SIGFPE)"),
589 wxT("Kill (SIGKILL)"),
591 wxT("Segment violation (SIGSEGV)"),
592 wxT("System (SIGSYS)"),
593 wxT("Broken pipe (SIGPIPE)"),
594 wxT("Alarm (SIGALRM)"),
595 wxT("Terminate (SIGTERM)"),
598 static int s_sigLast
= wxSIGNONE
;
599 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
600 wxT("Exec question"),
601 WXSIZEOF(signalNames
), signalNames
,
607 wxFAIL_MSG( wxT("unexpected return value") );
635 if ( sig
== wxSIGNONE
)
637 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
638 if ( wxProcess::Exists(pid
) )
640 wxLogStatus(wxT("Process %ld is running."), pid
);
644 wxLogStatus(wxT("No process with pid = %ld."), pid
);
649 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
650 if ( rc
== wxKILL_OK
)
652 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
656 static const wxChar
*errorText
[] =
659 wxT("signal not supported"),
660 wxT("permission denied"),
661 wxT("no such process"),
662 wxT("unspecified error"),
665 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
666 pid
, sig
, errorText
[rc
]);
671 // ----------------------------------------------------------------------------
672 // execution options dialog
673 // ----------------------------------------------------------------------------
675 enum ExecQueryDialogID
682 class ExecQueryDialog
: public wxDialog
685 ExecQueryDialog(const wxString
& cmd
);
687 wxString
GetExecutable() const
689 return m_executable
->GetValue();
692 wxString
GetWorkDir() const
694 return m_useCWD
->GetValue() ? m_cwdtext
->GetValue() : wxString();
697 void GetEnvironment(wxEnvVariableHashMap
& env
);
700 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent
& event
)
702 event
.Enable(m_useCWD
->GetValue());
705 void OnUpdateEnvironmentUI(wxUpdateUIEvent
& event
)
707 event
.Enable(m_useEnv
->GetValue());
710 wxTextCtrl
* m_executable
;
711 wxTextCtrl
* m_cwdtext
;
712 wxTextCtrl
* m_envtext
;
713 wxCheckBox
* m_useCWD
;
714 wxCheckBox
* m_useEnv
;
716 DECLARE_EVENT_TABLE()
719 BEGIN_EVENT_TABLE(ExecQueryDialog
, wxDialog
)
720 EVT_UPDATE_UI(TEXT_CWD
, ExecQueryDialog::OnUpdateWorkingDirectoryUI
)
721 EVT_UPDATE_UI(TEXT_ENVIRONMENT
, ExecQueryDialog::OnUpdateEnvironmentUI
)
724 ExecQueryDialog::ExecQueryDialog(const wxString
& cmd
)
725 : wxDialog(NULL
, wxID_ANY
, DIALOG_TITLE
,
726 wxDefaultPosition
, wxDefaultSize
,
727 wxDEFAULT_DIALOG_STYLE
| wxRESIZE_BORDER
)
729 wxSizer
* globalSizer
= new wxBoxSizer(wxVERTICAL
);
731 m_executable
= new wxTextCtrl(this, TEXT_EXECUTABLE
, wxString());
732 m_cwdtext
= new wxTextCtrl(this, TEXT_CWD
, wxString());
733 m_envtext
= new wxTextCtrl(this, TEXT_ENVIRONMENT
, wxString(),
734 wxDefaultPosition
, wxSize(300, 200),
735 wxTE_MULTILINE
|wxHSCROLL
);
737 const wxSizerFlags flagsExpand
= wxSizerFlags().Expand().Border();
738 globalSizer
->Add(new wxStaticText(this, wxID_ANY
, "Enter the command: "),
740 globalSizer
->Add(m_executable
, flagsExpand
);
742 m_useCWD
= new wxCheckBox(this, wxID_ANY
, "Working directory: ");
743 globalSizer
->Add(m_useCWD
, flagsExpand
);
744 globalSizer
->Add(m_cwdtext
, flagsExpand
);
746 m_useEnv
= new wxCheckBox(this, wxID_ANY
, "Environment: ");
747 globalSizer
->Add(m_useEnv
, flagsExpand
);
748 globalSizer
->Add(m_envtext
, wxSizerFlags(flagsExpand
).Proportion(1));
750 globalSizer
->Add(CreateStdDialogButtonSizer(wxOK
|wxCANCEL
), flagsExpand
);
751 SetSizerAndFit(globalSizer
);
754 m_executable
->SetValue(cmd
);
755 m_cwdtext
->SetValue(wxGetCwd());
756 wxEnvVariableHashMap env
;
757 if ( wxGetEnvMap(&env
) )
759 for ( wxEnvVariableHashMap::iterator it
= env
.begin();
763 m_envtext
->AppendText(it
->first
+ '=' + it
->second
+ '\n');
766 m_useCWD
->SetValue(false);
767 m_useEnv
->SetValue(false);
770 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap
& env
)
773 if ( m_useEnv
->GetValue() )
778 const int nb
= m_envtext
->GetNumberOfLines();
779 for ( int l
= 0; l
< nb
; l
++ )
781 const wxString line
= m_envtext
->GetLineText(l
).Trim();
785 name
= line
.BeforeFirst('=', &value
);
788 wxLogWarning("Skipping invalid environment line \"%s\".", line
);
798 static bool QueryExec(wxString
& cmd
, wxExecuteEnv
& env
)
800 ExecQueryDialog
dialog(cmd
);
802 if ( dialog
.ShowModal() != wxID_OK
)
805 cmd
= dialog
.GetExecutable();
806 env
.cwd
= dialog
.GetWorkDir();
807 dialog
.GetEnvironment(env
.env
);
812 // ----------------------------------------------------------------------------
813 // event handlers: exec menu
814 // ----------------------------------------------------------------------------
816 void MyFrame::DoAsyncExec(const wxString
& cmd
)
818 MyProcess
* const process
= new MyProcess(this, cmd
);
819 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
822 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
828 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
832 // the parent frame keeps track of all async processes as it needs to
833 // free them if we exit before the child process terminates
834 AddAsyncProcess(process
);
838 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
842 if ( !QueryExec(cmd
, env
) )
845 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
847 int code
= wxExecute(cmd
, wxEXEC_SYNC
, NULL
, &env
);
849 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
855 void MyFrame::OnSyncNoEventsExec(wxCommandEvent
& WXUNUSED(event
))
857 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
864 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
866 int code
= wxExecute(cmd
, wxEXEC_BLOCK
);
868 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
874 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
876 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
886 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
888 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
895 int code
= wxShell(cmd
);
896 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
901 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
906 m_cmdLast
= "type Makefile.in";
908 m_cmdLast
= "cat -n Makefile";
912 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
920 switch ( wxMessageBox(wxT("Execute it synchronously?"),
921 wxT("Exec question"),
922 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
938 wxLogStatus("\"%s\" is running please wait...", cmd
);
942 wxArrayString output
, errors
;
943 int code
= wxExecute(cmd
, output
, errors
);
945 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
946 cmd
, sw
.Time(), code
);
950 ShowOutput(cmd
, output
, wxT("Output"));
951 ShowOutput(cmd
, errors
, wxT("Errors"));
956 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
957 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
959 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
965 AddPipedProcess(process
);
972 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
975 m_cmdLast
= wxT("tr [a-z] [A-Z]");
977 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
984 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
989 // always execute the filter asynchronously
990 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
991 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
994 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
996 AddPipedProcess(process
);
1000 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1008 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1010 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1016 wxProcess
*process
= wxProcess::Open(cmd
);
1019 wxLogError(wxT("Failed to launch the command."));
1023 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1025 wxOutputStream
*out
= process
->GetOutputStream();
1028 wxLogError(wxT("Failed to connect to child stdin"));
1032 wxInputStream
*in
= process
->GetInputStream();
1035 wxLogError(wxT("Failed to connect to child stdout"));
1039 new MyPipeFrame(this, cmd
, process
);
1042 static wxString gs_lastFile
;
1044 static bool AskUserForFileName()
1049 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1050 #else // !wxUSE_FILEDLG
1051 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1053 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1055 if ( filename
.empty() )
1058 gs_lastFile
= filename
;
1063 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1065 if ( !AskUserForFileName() )
1068 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1069 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1072 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1078 bool ok
= ft
->GetOpenCommand(&cmd
,
1079 wxFileType::MessageParameters(gs_lastFile
));
1083 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1091 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1093 if ( !AskUserForFileName() )
1096 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1098 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1102 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1104 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1106 wxString filename
= wxGetTextFromUser
1108 wxT("Enter the URL"),
1114 if ( filename
.empty() )
1119 if ( !wxLaunchDefaultBrowser(s_url
) )
1121 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1125 // ----------------------------------------------------------------------------
1127 // ----------------------------------------------------------------------------
1131 bool MyFrame::GetDDEServer()
1133 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1134 DIALOG_TITLE
, m_server
);
1140 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1146 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1155 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1157 if ( !GetDDEServer() )
1161 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1164 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1169 if ( !conn
->Execute(m_cmdDde
) )
1171 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1176 wxLogStatus(wxT("Successfully executed DDE command"));
1181 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1183 if ( !GetDDEServer() )
1187 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1190 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1195 if ( !conn
->Request(m_cmdDde
) )
1197 wxLogError(wxT("Failed to send request '%s' via DDE."),
1202 wxLogStatus(wxT("Successfully sent DDE request."));
1207 #endif // __WINDOWS__
1209 // ----------------------------------------------------------------------------
1211 // ----------------------------------------------------------------------------
1214 void MyFrame::OnIdle(wxIdleEvent
& event
)
1216 size_t count
= m_running
.GetCount();
1217 for ( size_t n
= 0; n
< count
; n
++ )
1219 if ( m_running
[n
]->HasInput() )
1221 event
.RequestMore();
1226 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1231 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1233 static unsigned long s_ticks
= 0;
1234 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1237 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1239 RemovePipedProcess(process
);
1242 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1244 m_allAsync
.Remove(process
);
1249 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1251 if ( m_running
.IsEmpty() )
1253 // we want to start getting the timer events to ensure that a
1254 // steady stream of idle events comes in -- otherwise we
1255 // wouldn't be able to poll the child process input
1256 m_timerIdleWakeUp
.Start(100);
1258 //else: the timer is already running
1260 m_running
.Add(process
);
1261 m_allAsync
.Add(process
);
1264 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1266 m_running
.Remove(process
);
1268 if ( m_running
.IsEmpty() )
1270 // we don't need to get idle events all the time any more
1271 m_timerIdleWakeUp
.Stop();
1275 void MyFrame::ShowOutput(const wxString
& cmd
,
1276 const wxArrayString
& output
,
1277 const wxString
& title
)
1279 size_t count
= output
.GetCount();
1283 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1284 title
.c_str(), cmd
.c_str()));
1286 for ( size_t n
= 0; n
< count
; n
++ )
1288 m_lbox
->Append(output
[n
]);
1291 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1292 title
.Lower().c_str()));
1295 // ----------------------------------------------------------------------------
1297 // ----------------------------------------------------------------------------
1299 void MyProcess::OnTerminate(int pid
, int status
)
1301 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1302 pid
, m_cmd
.c_str(), status
);
1304 m_parent
->OnAsyncTermination(this);
1307 // ----------------------------------------------------------------------------
1309 // ----------------------------------------------------------------------------
1311 bool MyPipedProcess::HasInput()
1313 bool hasInput
= false;
1315 if ( IsInputAvailable() )
1317 wxTextInputStream
tis(*GetInputStream());
1319 // this assumes that the output is always line buffered
1321 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1323 m_parent
->GetLogListBox()->Append(msg
);
1328 if ( IsErrorAvailable() )
1330 wxTextInputStream
tis(*GetErrorStream());
1332 // this assumes that the output is always line buffered
1334 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1336 m_parent
->GetLogListBox()->Append(msg
);
1344 void MyPipedProcess::OnTerminate(int pid
, int status
)
1346 // show the rest of the output
1347 while ( HasInput() )
1350 m_parent
->OnProcessTerminated(this);
1352 MyProcess::OnTerminate(pid
, status
);
1355 // ----------------------------------------------------------------------------
1357 // ----------------------------------------------------------------------------
1359 bool MyPipedProcess2::HasInput()
1361 if ( !m_input
.empty() )
1363 wxTextOutputStream
os(*GetOutputStream());
1364 os
.WriteString(m_input
);
1369 // call us once again - may be we'll have output
1373 return MyPipedProcess::HasInput();
1376 // ============================================================================
1377 // MyPipeFrame implementation
1378 // ============================================================================
1380 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1381 const wxString
& cmd
,
1383 : wxFrame(parent
, wxID_ANY
, cmd
),
1385 // in a real program we'd check that the streams are !NULL here
1386 m_out(*process
->GetOutputStream()),
1387 m_in(*process
->GetInputStream()),
1388 m_err(*process
->GetErrorStream())
1390 m_process
->SetNextHandler(this);
1392 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1394 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1395 wxDefaultPosition
, wxDefaultSize
,
1396 wxTE_PROCESS_ENTER
);
1397 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1398 wxDefaultPosition
, wxDefaultSize
,
1399 wxTE_MULTILINE
| wxTE_RICH
);
1400 m_textIn
->SetEditable(false);
1401 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1402 wxDefaultPosition
, wxDefaultSize
,
1403 wxTE_MULTILINE
| wxTE_RICH
);
1404 m_textErr
->SetEditable(false);
1406 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1407 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1409 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1411 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1413 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1415 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1417 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1419 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1420 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1421 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1423 panel
->SetSizer(sizerTop
);
1424 sizerTop
->Fit(this);
1429 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1432 wxFileDialog
filedlg(this, wxT("Select file to send"));
1433 if ( filedlg
.ShowModal() != wxID_OK
)
1436 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1438 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1441 // can't write the entire string at once, this risk overflowing the pipe
1442 // and we would dead lock
1443 size_t len
= data
.length();
1444 const wxChar
*pc
= data
.c_str();
1447 const size_t CHUNK_SIZE
= 4096;
1448 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1450 // note that not all data could have been written as we don't block on
1451 // the write end of the pipe
1452 const size_t lenChunk
= m_out
.LastWrite();
1459 #endif // wxUSE_FILEDLG
1462 void MyPipeFrame::DoGet()
1464 // we don't have any way to be notified when any input appears on the
1465 // stream so we have to poll it :-(
1466 DoGetFromStream(m_textIn
, m_in
);
1467 DoGetFromStream(m_textErr
, m_err
);
1470 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1472 while ( in
.CanRead() )
1474 wxChar buffer
[4096];
1475 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = wxT('\0');
1477 text
->AppendText(buffer
);
1481 void MyPipeFrame::DoClose()
1483 m_process
->CloseOutput();
1488 void MyPipeFrame::DisableInput()
1490 m_textOut
->SetEditable(false);
1491 FindWindow(Exec_Btn_Send
)->Disable();
1492 FindWindow(Exec_Btn_SendFile
)->Disable();
1493 FindWindow(Exec_Btn_Close
)->Disable();
1496 void MyPipeFrame::DisableOutput()
1498 FindWindow(Exec_Btn_Get
)->Disable();
1501 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1505 // we're not interested in getting the process termination notification
1506 // if we are closing it ourselves
1507 wxProcess
*process
= m_process
;
1509 process
->SetNextHandler(NULL
);
1511 process
->CloseOutput();
1517 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1521 wxDELETE(m_process
);
1523 wxLogWarning(wxT("The other process has terminated, closing"));