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));
419 // success: wxApp::OnRun() will be called which will enter the main message
420 // loop and the application will run. If we returned false here, the
421 // application would exit immediately.
425 // ----------------------------------------------------------------------------
427 // ----------------------------------------------------------------------------
430 #pragma warning(disable: 4355) // this used in base member initializer list
434 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
435 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
436 m_timerIdleWakeUp(this, Exec_TimerIdle
),
437 m_timerBg(this, Exec_TimerBg
)
442 // we need this in order to allow the about menu relocation, since ABOUT is
443 // not the default id of the about menu
444 wxApp::s_macAboutMenuItemId
= Exec_About
;
448 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
449 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
450 wxT("Kill a process by PID"));
451 menuFile
->AppendSeparator();
452 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
453 wxT("Clear the log window"));
454 menuFile
->AppendSeparator();
455 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
456 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
457 menuFile
->AppendSeparator();
458 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
460 wxMenu
*execMenu
= new wxMenu
;
461 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
462 wxT("Launch a program and return when it terminates"));
463 execMenu
->Append(Exec_SyncNoEventsExec
, wxT("Sync execution and &block...\tCtrl-B"),
464 wxT("Launch a program and block until it terminates"));
465 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
466 wxT("Launch a program and return immediately"));
467 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
468 wxT("Launch a shell and execute a command in it"));
469 execMenu
->AppendSeparator();
470 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
471 wxT("Launch a program and capture its output"));
472 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
473 wxT("Pipe a string through a filter"));
474 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
475 wxT("Open a pipe to and from another program"));
477 execMenu
->AppendSeparator();
478 execMenu
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
479 wxT("Launch the command to open this kind of files"));
480 execMenu
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
481 wxT("Launch the default application associated with the file"));
482 execMenu
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
483 wxT("Launch the default browser with the given URL"));
485 execMenu
->AppendSeparator();
486 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
487 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
490 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
491 helpMenu
->Append(Exec_About
, wxT("&About...\tF1"), wxT("Show about dialog"));
493 // now append the freshly created menu to the menu bar...
494 wxMenuBar
*menuBar
= new wxMenuBar();
495 menuBar
->Append(menuFile
, wxT("&File"));
496 menuBar
->Append(execMenu
, wxT("&Exec"));
497 menuBar
->Append(helpMenu
, wxT("&Help"));
499 // ... and attach this menu bar to the frame
502 // create the listbox in which we will show misc messages as they come
503 m_lbox
= new wxListBox(this, wxID_ANY
);
504 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
505 wxFONTWEIGHT_NORMAL
);
507 m_lbox
->SetFont(font
);
510 // create a status bar just for fun (by default with 1 pane only)
512 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
513 #endif // wxUSE_STATUSBAR
515 m_timerBg
.Start(1000);
520 // any processes left until now must be deleted manually: normally this is
521 // done when the associated process terminates but it must be still running
522 // if this didn't happen until now
523 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
525 delete m_allAsync
[n
];
529 // ----------------------------------------------------------------------------
530 // event handlers: file and help menu
531 // ----------------------------------------------------------------------------
533 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
535 // true is to force the frame to close
539 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
544 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
549 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
554 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
556 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
557 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
560 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
562 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
564 wxT("Exec question"),
566 // we need the full unsigned int range
577 static const wxString signalNames
[] =
579 wxT("Just test (SIGNONE)"),
580 wxT("Hangup (SIGHUP)"),
581 wxT("Interrupt (SIGINT)"),
582 wxT("Quit (SIGQUIT)"),
583 wxT("Illegal instruction (SIGILL)"),
584 wxT("Trap (SIGTRAP)"),
585 wxT("Abort (SIGABRT)"),
586 wxT("Emulated trap (SIGEMT)"),
587 wxT("FP exception (SIGFPE)"),
588 wxT("Kill (SIGKILL)"),
590 wxT("Segment violation (SIGSEGV)"),
591 wxT("System (SIGSYS)"),
592 wxT("Broken pipe (SIGPIPE)"),
593 wxT("Alarm (SIGALRM)"),
594 wxT("Terminate (SIGTERM)"),
597 static int s_sigLast
= wxSIGNONE
;
598 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
599 wxT("Exec question"),
600 WXSIZEOF(signalNames
), signalNames
,
606 wxFAIL_MSG( wxT("unexpected return value") );
634 if ( sig
== wxSIGNONE
)
636 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
637 if ( wxProcess::Exists(pid
) )
639 wxLogStatus(wxT("Process %ld is running."), pid
);
643 wxLogStatus(wxT("No process with pid = %ld."), pid
);
648 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
649 if ( rc
== wxKILL_OK
)
651 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
655 static const wxChar
*errorText
[] =
658 wxT("signal not supported"),
659 wxT("permission denied"),
660 wxT("no such process"),
661 wxT("unspecified error"),
664 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
665 pid
, sig
, errorText
[rc
]);
670 // ----------------------------------------------------------------------------
671 // execution options dialog
672 // ----------------------------------------------------------------------------
674 enum ExecQueryDialogID
681 class ExecQueryDialog
: public wxDialog
684 ExecQueryDialog(const wxString
& cmd
);
686 wxString
GetExecutable() const
688 return m_executable
->GetValue();
691 wxString
GetWorkDir() const
693 return m_useCWD
->GetValue() ? m_cwdtext
->GetValue() : wxString();
696 void GetEnvironment(wxEnvVariableHashMap
& env
);
699 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent
& event
)
701 event
.Enable(m_useCWD
->GetValue());
704 void OnUpdateEnvironmentUI(wxUpdateUIEvent
& event
)
706 event
.Enable(m_useEnv
->GetValue());
709 wxTextCtrl
* m_executable
;
710 wxTextCtrl
* m_cwdtext
;
711 wxTextCtrl
* m_envtext
;
712 wxCheckBox
* m_useCWD
;
713 wxCheckBox
* m_useEnv
;
715 DECLARE_EVENT_TABLE()
718 BEGIN_EVENT_TABLE(ExecQueryDialog
, wxDialog
)
719 EVT_UPDATE_UI(TEXT_CWD
, ExecQueryDialog::OnUpdateWorkingDirectoryUI
)
720 EVT_UPDATE_UI(TEXT_ENVIRONMENT
, ExecQueryDialog::OnUpdateEnvironmentUI
)
723 ExecQueryDialog::ExecQueryDialog(const wxString
& cmd
)
724 : wxDialog(NULL
, wxID_ANY
, DIALOG_TITLE
,
725 wxDefaultPosition
, wxDefaultSize
,
726 wxDEFAULT_DIALOG_STYLE
| wxRESIZE_BORDER
)
728 wxSizer
* globalSizer
= new wxBoxSizer(wxVERTICAL
);
730 m_executable
= new wxTextCtrl(this, TEXT_EXECUTABLE
, wxString());
731 m_cwdtext
= new wxTextCtrl(this, TEXT_CWD
, wxString());
732 m_envtext
= new wxTextCtrl(this, TEXT_ENVIRONMENT
, wxString(),
733 wxDefaultPosition
, wxSize(300, 200),
734 wxTE_MULTILINE
|wxHSCROLL
);
736 const wxSizerFlags flagsExpand
= wxSizerFlags().Expand().Border();
737 globalSizer
->Add(new wxStaticText(this, wxID_ANY
, "Enter the command: "),
739 globalSizer
->Add(m_executable
, flagsExpand
);
741 m_useCWD
= new wxCheckBox(this, wxID_ANY
, "Working directory: ");
742 globalSizer
->Add(m_useCWD
, flagsExpand
);
743 globalSizer
->Add(m_cwdtext
, flagsExpand
);
745 m_useEnv
= new wxCheckBox(this, wxID_ANY
, "Environment: ");
746 globalSizer
->Add(m_useEnv
, flagsExpand
);
747 globalSizer
->Add(m_envtext
, wxSizerFlags(flagsExpand
).Proportion(1));
749 globalSizer
->Add(CreateStdDialogButtonSizer(wxOK
|wxCANCEL
), flagsExpand
);
750 SetSizerAndFit(globalSizer
);
753 m_executable
->SetValue(cmd
);
754 m_cwdtext
->SetValue(wxGetCwd());
755 wxEnvVariableHashMap env
;
756 if ( wxGetEnvMap(&env
) )
758 for ( wxEnvVariableHashMap::iterator it
= env
.begin();
762 m_envtext
->AppendText(it
->first
+ '=' + it
->second
+ '\n');
765 m_useCWD
->SetValue(false);
766 m_useEnv
->SetValue(false);
769 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap
& env
)
772 if ( m_useEnv
->GetValue() )
777 const int nb
= m_envtext
->GetNumberOfLines();
778 for ( int l
= 0; l
< nb
; l
++ )
780 const wxString line
= m_envtext
->GetLineText(l
).Trim();
784 name
= line
.BeforeFirst('=', &value
);
787 wxLogWarning("Skipping invalid environment line \"%s\".", line
);
797 static bool QueryExec(wxString
& cmd
, wxExecuteEnv
& env
)
799 ExecQueryDialog
dialog(cmd
);
801 if ( dialog
.ShowModal() != wxID_OK
)
804 cmd
= dialog
.GetExecutable();
805 env
.cwd
= dialog
.GetWorkDir();
806 dialog
.GetEnvironment(env
.env
);
811 // ----------------------------------------------------------------------------
812 // event handlers: exec menu
813 // ----------------------------------------------------------------------------
815 void MyFrame::DoAsyncExec(const wxString
& cmd
)
817 MyProcess
* const process
= new MyProcess(this, cmd
);
818 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
821 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
827 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
831 // the parent frame keeps track of all async processes as it needs to
832 // free them if we exit before the child process terminates
833 AddAsyncProcess(process
);
837 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
841 if ( !QueryExec(cmd
, env
) )
844 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
846 int code
= wxExecute(cmd
, wxEXEC_SYNC
, NULL
, &env
);
848 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
854 void MyFrame::OnSyncNoEventsExec(wxCommandEvent
& WXUNUSED(event
))
856 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
863 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
865 int code
= wxExecute(cmd
, wxEXEC_BLOCK
);
867 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
873 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
875 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
885 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
887 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
894 int code
= wxShell(cmd
);
895 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
900 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
905 m_cmdLast
= "type Makefile.in";
907 m_cmdLast
= "cat -n Makefile";
911 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
919 switch ( wxMessageBox(wxT("Execute it synchronously?"),
920 wxT("Exec question"),
921 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
937 wxLogStatus("\"%s\" is running please wait...", cmd
);
941 wxArrayString output
, errors
;
942 int code
= wxExecute(cmd
, output
, errors
);
944 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
945 cmd
, sw
.Time(), code
);
949 ShowOutput(cmd
, output
, wxT("Output"));
950 ShowOutput(cmd
, errors
, wxT("Errors"));
955 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
956 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
958 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
964 AddPipedProcess(process
);
971 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
974 m_cmdLast
= wxT("tr [a-z] [A-Z]");
976 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
983 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
988 // always execute the filter asynchronously
989 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
990 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
993 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
995 AddPipedProcess(process
);
999 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1007 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1009 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1015 wxProcess
*process
= wxProcess::Open(cmd
);
1018 wxLogError(wxT("Failed to launch the command."));
1022 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1024 wxOutputStream
*out
= process
->GetOutputStream();
1027 wxLogError(wxT("Failed to connect to child stdin"));
1031 wxInputStream
*in
= process
->GetInputStream();
1034 wxLogError(wxT("Failed to connect to child stdout"));
1038 new MyPipeFrame(this, cmd
, process
);
1041 static wxString gs_lastFile
;
1043 static bool AskUserForFileName()
1048 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1049 #else // !wxUSE_FILEDLG
1050 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1052 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1054 if ( filename
.empty() )
1057 gs_lastFile
= filename
;
1062 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1064 if ( !AskUserForFileName() )
1067 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1068 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1071 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1077 bool ok
= ft
->GetOpenCommand(&cmd
,
1078 wxFileType::MessageParameters(gs_lastFile
));
1082 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1090 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1092 if ( !AskUserForFileName() )
1095 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1097 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1101 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1103 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1105 wxString filename
= wxGetTextFromUser
1107 wxT("Enter the URL"),
1113 if ( filename
.empty() )
1118 if ( !wxLaunchDefaultBrowser(s_url
) )
1120 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1124 // ----------------------------------------------------------------------------
1126 // ----------------------------------------------------------------------------
1130 bool MyFrame::GetDDEServer()
1132 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1133 DIALOG_TITLE
, m_server
);
1139 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1145 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1154 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1156 if ( !GetDDEServer() )
1160 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1163 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1168 if ( !conn
->Execute(m_cmdDde
) )
1170 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1175 wxLogStatus(wxT("Successfully executed DDE command"));
1180 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1182 if ( !GetDDEServer() )
1186 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1189 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1194 if ( !conn
->Request(m_cmdDde
) )
1196 wxLogError(wxT("Failed to send request '%s' via DDE."),
1201 wxLogStatus(wxT("Successfully sent DDE request."));
1206 #endif // __WINDOWS__
1208 // ----------------------------------------------------------------------------
1210 // ----------------------------------------------------------------------------
1213 void MyFrame::OnIdle(wxIdleEvent
& event
)
1215 size_t count
= m_running
.GetCount();
1216 for ( size_t n
= 0; n
< count
; n
++ )
1218 if ( m_running
[n
]->HasInput() )
1220 event
.RequestMore();
1225 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1230 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1232 static unsigned long s_ticks
= 0;
1233 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1236 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1238 RemovePipedProcess(process
);
1241 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1243 m_allAsync
.Remove(process
);
1248 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1250 if ( m_running
.IsEmpty() )
1252 // we want to start getting the timer events to ensure that a
1253 // steady stream of idle events comes in -- otherwise we
1254 // wouldn't be able to poll the child process input
1255 m_timerIdleWakeUp
.Start(100);
1257 //else: the timer is already running
1259 m_running
.Add(process
);
1260 m_allAsync
.Add(process
);
1263 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1265 m_running
.Remove(process
);
1267 if ( m_running
.IsEmpty() )
1269 // we don't need to get idle events all the time any more
1270 m_timerIdleWakeUp
.Stop();
1274 void MyFrame::ShowOutput(const wxString
& cmd
,
1275 const wxArrayString
& output
,
1276 const wxString
& title
)
1278 size_t count
= output
.GetCount();
1282 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1283 title
.c_str(), cmd
.c_str()));
1285 for ( size_t n
= 0; n
< count
; n
++ )
1287 m_lbox
->Append(output
[n
]);
1290 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1291 title
.Lower().c_str()));
1294 // ----------------------------------------------------------------------------
1296 // ----------------------------------------------------------------------------
1298 void MyProcess::OnTerminate(int pid
, int status
)
1300 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1301 pid
, m_cmd
.c_str(), status
);
1303 m_parent
->OnAsyncTermination(this);
1306 // ----------------------------------------------------------------------------
1308 // ----------------------------------------------------------------------------
1310 bool MyPipedProcess::HasInput()
1312 bool hasInput
= false;
1314 if ( IsInputAvailable() )
1316 wxTextInputStream
tis(*GetInputStream());
1318 // this assumes that the output is always line buffered
1320 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1322 m_parent
->GetLogListBox()->Append(msg
);
1327 if ( IsErrorAvailable() )
1329 wxTextInputStream
tis(*GetErrorStream());
1331 // this assumes that the output is always line buffered
1333 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1335 m_parent
->GetLogListBox()->Append(msg
);
1343 void MyPipedProcess::OnTerminate(int pid
, int status
)
1345 // show the rest of the output
1346 while ( HasInput() )
1349 m_parent
->OnProcessTerminated(this);
1351 MyProcess::OnTerminate(pid
, status
);
1354 // ----------------------------------------------------------------------------
1356 // ----------------------------------------------------------------------------
1358 bool MyPipedProcess2::HasInput()
1360 if ( !m_input
.empty() )
1362 wxTextOutputStream
os(*GetOutputStream());
1363 os
.WriteString(m_input
);
1368 // call us once again - may be we'll have output
1372 return MyPipedProcess::HasInput();
1375 // ============================================================================
1376 // MyPipeFrame implementation
1377 // ============================================================================
1379 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1380 const wxString
& cmd
,
1382 : wxFrame(parent
, wxID_ANY
, cmd
),
1384 // in a real program we'd check that the streams are !NULL here
1385 m_out(*process
->GetOutputStream()),
1386 m_in(*process
->GetInputStream()),
1387 m_err(*process
->GetErrorStream())
1389 m_process
->SetNextHandler(this);
1391 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1393 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1394 wxDefaultPosition
, wxDefaultSize
,
1395 wxTE_PROCESS_ENTER
);
1396 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1397 wxDefaultPosition
, wxDefaultSize
,
1398 wxTE_MULTILINE
| wxTE_RICH
);
1399 m_textIn
->SetEditable(false);
1400 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1401 wxDefaultPosition
, wxDefaultSize
,
1402 wxTE_MULTILINE
| wxTE_RICH
);
1403 m_textErr
->SetEditable(false);
1405 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1406 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1408 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1410 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1412 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1414 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1416 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1418 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1419 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1420 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1422 panel
->SetSizer(sizerTop
);
1423 sizerTop
->Fit(this);
1428 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1431 wxFileDialog
filedlg(this, wxT("Select file to send"));
1432 if ( filedlg
.ShowModal() != wxID_OK
)
1435 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1437 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1440 // can't write the entire string at once, this risk overflowing the pipe
1441 // and we would dead lock
1442 size_t len
= data
.length();
1443 const wxChar
*pc
= data
.c_str();
1446 const size_t CHUNK_SIZE
= 4096;
1447 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1449 // note that not all data could have been written as we don't block on
1450 // the write end of the pipe
1451 const size_t lenChunk
= m_out
.LastWrite();
1458 #endif // wxUSE_FILEDLG
1461 void MyPipeFrame::DoGet()
1463 // we don't have any way to be notified when any input appears on the
1464 // stream so we have to poll it :-(
1465 DoGetFromStream(m_textIn
, m_in
);
1466 DoGetFromStream(m_textErr
, m_err
);
1469 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1471 while ( in
.CanRead() )
1473 wxChar buffer
[4096];
1474 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = wxT('\0');
1476 text
->AppendText(buffer
);
1480 void MyPipeFrame::DoClose()
1482 m_process
->CloseOutput();
1487 void MyPipeFrame::DisableInput()
1489 m_textOut
->SetEditable(false);
1490 FindWindow(Exec_Btn_Send
)->Disable();
1491 FindWindow(Exec_Btn_SendFile
)->Disable();
1492 FindWindow(Exec_Btn_Close
)->Disable();
1495 void MyPipeFrame::DisableOutput()
1497 FindWindow(Exec_Btn_Get
)->Disable();
1500 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1504 // we're not interested in getting the process termination notification
1505 // if we are closing it ourselves
1506 wxProcess
*process
= m_process
;
1508 process
->SetNextHandler(NULL
);
1510 process
->CloseOutput();
1516 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1520 wxDELETE(m_process
);
1522 wxLogWarning(wxT("The other process has terminated, closing"));