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/scopedptr.h"
59 #include "wx/stopwatch.h"
61 #include "wx/process.h"
63 #include "wx/mimetype.h"
69 #ifndef wxHAS_IMAGES_IN_RESOURCES
70 #include "../sample.xpm"
73 // ----------------------------------------------------------------------------
74 // the usual application and main frame classes
75 // ----------------------------------------------------------------------------
77 // Define a new application type, each program should derive a class from wxApp
78 class MyApp
: public wxApp
81 // override base class virtuals
82 // ----------------------------
84 // this one is called on application startup and is a good place for the app
85 // initialization (doing it here and not in the ctor allows to have an error
86 // return: if OnInit() returns false, the application terminates)
87 virtual bool OnInit();
90 // Define an array of process pointers used by MyFrame
92 WX_DEFINE_ARRAY_PTR(MyPipedProcess
*, MyPipedProcessesArray
);
95 WX_DEFINE_ARRAY_PTR(MyProcess
*, MyProcessesArray
);
97 // Define a new frame type: this is going to be our main frame
98 class MyFrame
: public wxFrame
102 MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
);
105 // event handlers (these functions should _not_ be virtual)
106 void OnQuit(wxCommandEvent
& event
);
108 void OnKill(wxCommandEvent
& event
);
110 void OnClear(wxCommandEvent
& event
);
112 void OnBeginBusyCursor(wxCommandEvent
& event
);
113 void OnEndBusyCursor(wxCommandEvent
& event
);
115 void OnSyncExec(wxCommandEvent
& event
);
116 void OnAsyncExec(wxCommandEvent
& event
);
117 void OnShell(wxCommandEvent
& event
);
118 void OnExecWithRedirect(wxCommandEvent
& event
);
119 void OnExecWithPipe(wxCommandEvent
& event
);
121 void OnPOpen(wxCommandEvent
& event
);
123 void OnFileExec(wxCommandEvent
& event
);
124 void OnFileLaunch(wxCommandEvent
& event
);
125 void OnOpenURL(wxCommandEvent
& event
);
126 void OnShowCommandForExt(wxCommandEvent
& event
);
128 void OnAbout(wxCommandEvent
& event
);
130 // polling output of async processes
131 void OnIdleTimer(wxTimerEvent
& event
);
132 void OnIdle(wxIdleEvent
& event
);
134 // for MyPipedProcess
135 void OnProcessTerminated(MyPipedProcess
*process
);
136 wxListBox
*GetLogListBox() const { return m_lbox
; }
139 void OnAsyncTermination(MyProcess
*process
);
141 // timer updating a counter in the background
142 void OnBgTimer(wxTimerEvent
& event
);
145 void ShowOutput(const wxString
& cmd
,
146 const wxArrayString
& output
,
147 const wxString
& title
);
149 int GetExecFlags() const;
151 void DoAsyncExec(const wxString
& cmd
);
153 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
155 void AddPipedProcess(MyPipedProcess
*process
);
156 void RemovePipedProcess(MyPipedProcess
*process
);
159 // the PID of the last process we launched asynchronously
162 // last command we executed
166 void OnDDEExec(wxCommandEvent
& event
);
167 void OnDDERequest(wxCommandEvent
& event
);
171 // last params of a DDE transaction
175 #endif // __WINDOWS__
179 // array of running processes with redirected IO
180 MyPipedProcessesArray m_running
;
182 // array of all asynchrously running processes
183 MyProcessesArray m_allAsync
;
185 // the idle event wake up timer
186 wxTimer m_timerIdleWakeUp
;
188 // a background timer allowing to easily check visually whether the
189 // messages are processed or not
192 // any class wishing to process wxWidgets events must use this macro
193 DECLARE_EVENT_TABLE()
196 // ----------------------------------------------------------------------------
197 // MyPipeFrame: allows the user to communicate with the child process
198 // ----------------------------------------------------------------------------
200 class MyPipeFrame
: public wxFrame
203 MyPipeFrame(wxFrame
*parent
,
208 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
209 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
210 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
211 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
212 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
214 void OnClose(wxCloseEvent
& event
);
216 void OnProcessTerm(wxProcessEvent
& event
);
220 wxString
s(m_textOut
->GetValue());
222 m_out
.Write(s
.c_str(), s
.length());
232 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
234 void DisableOutput();
237 wxProcess
*m_process
;
239 wxOutputStream
&m_out
;
243 wxTextCtrl
*m_textOut
,
247 DECLARE_EVENT_TABLE()
250 // ----------------------------------------------------------------------------
251 // wxProcess-derived classes
252 // ----------------------------------------------------------------------------
254 // This is the handler for process termination events
255 class MyProcess
: public wxProcess
258 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
259 : wxProcess(parent
), m_cmd(cmd
)
264 // instead of overriding this virtual function we might as well process the
265 // event from it in the frame class - this might be more convenient in some
267 virtual void OnTerminate(int pid
, int status
);
274 // A specialization of MyProcess for redirecting the output
275 class MyPipedProcess
: public MyProcess
278 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
279 : MyProcess(parent
, cmd
)
284 virtual void OnTerminate(int pid
, int status
);
286 virtual bool HasInput();
289 // A version of MyPipedProcess which also sends input to the stdin of the
291 class MyPipedProcess2
: public MyPipedProcess
294 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
295 : MyPipedProcess(parent
, cmd
), m_input(input
)
299 virtual bool HasInput();
305 // ----------------------------------------------------------------------------
307 // ----------------------------------------------------------------------------
309 // IDs for the controls and the menu commands
319 Exec_BeginBusyCursor
,
326 Exec_ShowCommandForExt
,
333 Exec_Flags_HideConsole
,
334 Exec_Flags_ShowConsole
,
336 Exec_About
= wxID_ABOUT
,
337 Exec_Quit
= wxID_EXIT
,
340 Exec_Btn_Send
= 1000,
346 static const wxChar
*DIALOG_TITLE
= wxT("Exec sample");
348 // ----------------------------------------------------------------------------
349 // event tables and other macros for wxWidgets
350 // ----------------------------------------------------------------------------
352 // the event tables connect the wxWidgets events with the functions (event
353 // handlers) which process them. It can be also done at run-time, but for the
354 // simple menu events like this the static method is much simpler.
355 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
356 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
357 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
358 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
359 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
360 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
362 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
363 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
364 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
365 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
366 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
368 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
370 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
371 EVT_MENU(Exec_ShowCommandForExt
, MyFrame::OnShowCommandForExt
)
372 EVT_MENU(Exec_LaunchFile
, MyFrame::OnFileLaunch
)
373 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
376 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
377 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
378 #endif // __WINDOWS__
380 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
382 EVT_IDLE(MyFrame::OnIdle
)
384 EVT_TIMER(Exec_TimerIdle
, MyFrame::OnIdleTimer
)
385 EVT_TIMER(Exec_TimerBg
, MyFrame::OnBgTimer
)
388 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
389 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
390 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
391 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
392 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
394 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
396 EVT_CLOSE(MyPipeFrame::OnClose
)
398 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
401 // Create a new application object: this macro will allow wxWidgets to create
402 // the application object during program execution (it's better than using a
403 // static object for many reasons) and also declares the accessor function
404 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
408 // ============================================================================
410 // ============================================================================
412 // ----------------------------------------------------------------------------
413 // the application class
414 // ----------------------------------------------------------------------------
416 // `Main program' equivalent: the program execution "starts" here
419 if ( !wxApp::OnInit() )
422 // Create the main application window
423 MyFrame
*frame
= new MyFrame(wxT("Exec wxWidgets sample"),
424 wxDefaultPosition
, wxSize(500, 140));
429 // success: wxApp::OnRun() will be called which will enter the main message
430 // loop and the application will run. If we returned false here, the
431 // application would exit immediately.
435 // ----------------------------------------------------------------------------
437 // ----------------------------------------------------------------------------
440 #pragma warning(disable: 4355) // this used in base member initializer list
444 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
445 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
446 m_timerIdleWakeUp(this, Exec_TimerIdle
),
447 m_timerBg(this, Exec_TimerBg
)
449 SetIcon(wxICON(sample
));
454 // we need this in order to allow the about menu relocation, since ABOUT is
455 // not the default id of the about menu
456 wxApp::s_macAboutMenuItemId
= Exec_About
;
460 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
461 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
462 wxT("Kill a process by PID"));
463 menuFile
->AppendSeparator();
464 menuFile
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
465 wxT("Launch the command to open this kind of files"));
466 menuFile
->Append(Exec_ShowCommandForExt
,
467 "Show association for extension...\tShift-Ctrl-A",
468 "Show the command associated with the given extension");
469 menuFile
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
470 wxT("Launch the default application associated with the file"));
471 menuFile
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
472 wxT("Launch the default browser with the given URL"));
473 menuFile
->AppendSeparator();
474 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
475 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
476 menuFile
->AppendSeparator();
477 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
478 wxT("Clear the log window"));
479 menuFile
->AppendSeparator();
480 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
482 wxMenu
*flagsMenu
= new wxMenu
;
483 flagsMenu
->AppendCheckItem(Exec_Flags_HideConsole
, "Always &hide console");
484 flagsMenu
->AppendCheckItem(Exec_Flags_ShowConsole
, "Always &show console");
485 flagsMenu
->AppendCheckItem(Exec_Flags_NoEvents
, "Disable &events",
486 "This flag is valid for sync execution only");
488 wxMenu
*execMenu
= new wxMenu
;
489 execMenu
->AppendSubMenu(flagsMenu
, "Execution flags");
490 execMenu
->AppendSeparator();
491 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
492 wxT("Launch a program and return when it terminates"));
493 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
494 wxT("Launch a program and return immediately"));
495 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
496 wxT("Launch a shell and execute a command in it"));
497 execMenu
->AppendSeparator();
498 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
499 wxT("Launch a program and capture its output"));
500 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
501 wxT("Pipe a string through a filter"));
502 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
503 wxT("Open a pipe to and from another program"));
506 execMenu
->AppendSeparator();
507 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
508 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
511 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
512 helpMenu
->Append(Exec_About
, wxT("&About\tF1"), wxT("Show about dialog"));
514 // now append the freshly created menu to the menu bar...
515 wxMenuBar
*menuBar
= new wxMenuBar();
516 menuBar
->Append(menuFile
, wxT("&File"));
517 menuBar
->Append(execMenu
, wxT("&Exec"));
518 menuBar
->Append(helpMenu
, wxT("&Help"));
520 // ... and attach this menu bar to the frame
523 // create the listbox in which we will show misc messages as they come
524 m_lbox
= new wxListBox(this, wxID_ANY
);
525 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
526 wxFONTWEIGHT_NORMAL
);
528 m_lbox
->SetFont(font
);
531 // create a status bar just for fun (by default with 1 pane only)
533 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
534 #endif // wxUSE_STATUSBAR
536 m_timerBg
.Start(1000);
541 // any processes left until now must be deleted manually: normally this is
542 // done when the associated process terminates but it must be still running
543 // if this didn't happen until now
544 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
546 delete m_allAsync
[n
];
550 // ----------------------------------------------------------------------------
551 // event handlers: file and help menu
552 // ----------------------------------------------------------------------------
554 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
556 // true is to force the frame to close
560 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
565 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
570 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
575 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
577 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
578 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
581 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
583 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
585 wxT("Exec question"),
587 // we need the full unsigned int range
598 static const wxString signalNames
[] =
600 wxT("Just test (SIGNONE)"),
601 wxT("Hangup (SIGHUP)"),
602 wxT("Interrupt (SIGINT)"),
603 wxT("Quit (SIGQUIT)"),
604 wxT("Illegal instruction (SIGILL)"),
605 wxT("Trap (SIGTRAP)"),
606 wxT("Abort (SIGABRT)"),
607 wxT("Emulated trap (SIGEMT)"),
608 wxT("FP exception (SIGFPE)"),
609 wxT("Kill (SIGKILL)"),
611 wxT("Segment violation (SIGSEGV)"),
612 wxT("System (SIGSYS)"),
613 wxT("Broken pipe (SIGPIPE)"),
614 wxT("Alarm (SIGALRM)"),
615 wxT("Terminate (SIGTERM)"),
618 static int s_sigLast
= wxSIGNONE
;
619 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
620 wxT("Exec question"),
621 WXSIZEOF(signalNames
), signalNames
,
627 wxFAIL_MSG( wxT("unexpected return value") );
655 if ( sig
== wxSIGNONE
)
657 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
658 if ( wxProcess::Exists(pid
) )
660 wxLogStatus(wxT("Process %ld is running."), pid
);
664 wxLogStatus(wxT("No process with pid = %ld."), pid
);
669 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
670 if ( rc
== wxKILL_OK
)
672 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
676 static const wxChar
*errorText
[] =
679 wxT("signal not supported"),
680 wxT("permission denied"),
681 wxT("no such process"),
682 wxT("unspecified error"),
685 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
686 pid
, sig
, errorText
[rc
]);
691 // ----------------------------------------------------------------------------
692 // execution options dialog
693 // ----------------------------------------------------------------------------
695 enum ExecQueryDialogID
702 class ExecQueryDialog
: public wxDialog
705 ExecQueryDialog(const wxString
& cmd
);
707 wxString
GetExecutable() const
709 return m_executable
->GetValue();
712 wxString
GetWorkDir() const
714 return m_useCWD
->GetValue() ? m_cwdtext
->GetValue() : wxString();
717 void GetEnvironment(wxEnvVariableHashMap
& env
);
720 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent
& event
)
722 event
.Enable(m_useCWD
->GetValue());
725 void OnUpdateEnvironmentUI(wxUpdateUIEvent
& event
)
727 event
.Enable(m_useEnv
->GetValue());
730 wxTextCtrl
* m_executable
;
731 wxTextCtrl
* m_cwdtext
;
732 wxTextCtrl
* m_envtext
;
733 wxCheckBox
* m_useCWD
;
734 wxCheckBox
* m_useEnv
;
736 DECLARE_EVENT_TABLE()
739 BEGIN_EVENT_TABLE(ExecQueryDialog
, wxDialog
)
740 EVT_UPDATE_UI(TEXT_CWD
, ExecQueryDialog::OnUpdateWorkingDirectoryUI
)
741 EVT_UPDATE_UI(TEXT_ENVIRONMENT
, ExecQueryDialog::OnUpdateEnvironmentUI
)
744 ExecQueryDialog::ExecQueryDialog(const wxString
& cmd
)
745 : wxDialog(NULL
, wxID_ANY
, DIALOG_TITLE
,
746 wxDefaultPosition
, wxDefaultSize
,
747 wxDEFAULT_DIALOG_STYLE
| wxRESIZE_BORDER
)
749 wxSizer
* globalSizer
= new wxBoxSizer(wxVERTICAL
);
751 m_executable
= new wxTextCtrl(this, TEXT_EXECUTABLE
, wxString());
752 m_cwdtext
= new wxTextCtrl(this, TEXT_CWD
, wxString());
753 m_envtext
= new wxTextCtrl(this, TEXT_ENVIRONMENT
, wxString(),
754 wxDefaultPosition
, wxSize(300, 200),
755 wxTE_MULTILINE
|wxHSCROLL
);
757 const wxSizerFlags flagsExpand
= wxSizerFlags().Expand().Border();
758 globalSizer
->Add(new wxStaticText(this, wxID_ANY
, "Enter the command: "),
760 globalSizer
->Add(m_executable
, flagsExpand
);
762 m_useCWD
= new wxCheckBox(this, wxID_ANY
, "Working directory: ");
763 globalSizer
->Add(m_useCWD
, flagsExpand
);
764 globalSizer
->Add(m_cwdtext
, flagsExpand
);
766 m_useEnv
= new wxCheckBox(this, wxID_ANY
, "Environment: ");
767 globalSizer
->Add(m_useEnv
, flagsExpand
);
768 globalSizer
->Add(m_envtext
, wxSizerFlags(flagsExpand
).Proportion(1));
770 globalSizer
->Add(CreateStdDialogButtonSizer(wxOK
|wxCANCEL
), flagsExpand
);
771 SetSizerAndFit(globalSizer
);
774 m_executable
->SetValue(cmd
);
775 m_cwdtext
->SetValue(wxGetCwd());
776 wxEnvVariableHashMap env
;
777 if ( wxGetEnvMap(&env
) )
779 for ( wxEnvVariableHashMap::iterator it
= env
.begin();
783 m_envtext
->AppendText(it
->first
+ '=' + it
->second
+ '\n');
786 m_useCWD
->SetValue(false);
787 m_useEnv
->SetValue(false);
790 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap
& env
)
793 if ( m_useEnv
->GetValue() )
798 const int nb
= m_envtext
->GetNumberOfLines();
799 for ( int l
= 0; l
< nb
; l
++ )
801 const wxString line
= m_envtext
->GetLineText(l
).Trim();
805 name
= line
.BeforeFirst('=', &value
);
808 wxLogWarning("Skipping invalid environment line \"%s\".", line
);
818 static bool QueryExec(wxString
& cmd
, wxExecuteEnv
& env
)
820 ExecQueryDialog
dialog(cmd
);
822 if ( dialog
.ShowModal() != wxID_OK
)
825 cmd
= dialog
.GetExecutable();
826 env
.cwd
= dialog
.GetWorkDir();
827 dialog
.GetEnvironment(env
.env
);
832 // ----------------------------------------------------------------------------
833 // event handlers: exec menu
834 // ----------------------------------------------------------------------------
836 int MyFrame::GetExecFlags() const
838 wxMenuBar
* const mbar
= GetMenuBar();
842 if ( mbar
->IsChecked(Exec_Flags_HideConsole
) )
843 flags
|= wxEXEC_HIDE_CONSOLE
;
844 if ( mbar
->IsChecked(Exec_Flags_ShowConsole
) )
845 flags
|= wxEXEC_SHOW_CONSOLE
;
846 if ( mbar
->IsChecked(Exec_Flags_NoEvents
) )
847 flags
|= wxEXEC_NOEVENTS
;
852 void MyFrame::DoAsyncExec(const wxString
& cmd
)
854 MyProcess
* const process
= new MyProcess(this, cmd
);
855 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
| GetExecFlags(), process
);
858 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
864 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
868 // the parent frame keeps track of all async processes as it needs to
869 // free them if we exit before the child process terminates
870 AddAsyncProcess(process
);
874 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
878 if ( !QueryExec(cmd
, env
) )
881 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
883 int code
= wxExecute(cmd
, wxEXEC_SYNC
| GetExecFlags(), NULL
, &env
);
885 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
891 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
893 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
903 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
905 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
912 int code
= wxShell(cmd
);
913 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
918 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
923 m_cmdLast
= "type Makefile.in";
925 m_cmdLast
= "cat -n Makefile";
929 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
937 switch ( wxMessageBox(wxT("Execute it synchronously?"),
938 wxT("Exec question"),
939 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
955 wxLogStatus("\"%s\" is running please wait...", cmd
);
959 wxArrayString output
, errors
;
960 int code
= wxExecute(cmd
, output
, errors
);
962 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
963 cmd
, sw
.Time(), code
);
967 ShowOutput(cmd
, output
, wxT("Output"));
968 ShowOutput(cmd
, errors
, wxT("Errors"));
973 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
974 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
976 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
982 AddPipedProcess(process
);
989 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
992 m_cmdLast
= wxT("tr [a-z] [A-Z]");
994 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
1001 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
1006 // always execute the filter asynchronously
1007 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
1008 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
1011 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
1013 AddPipedProcess(process
);
1017 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1025 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1027 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1033 wxProcess
*process
= wxProcess::Open(cmd
);
1036 wxLogError(wxT("Failed to launch the command."));
1040 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1042 wxOutputStream
*out
= process
->GetOutputStream();
1045 wxLogError(wxT("Failed to connect to child stdin"));
1049 wxInputStream
*in
= process
->GetInputStream();
1052 wxLogError(wxT("Failed to connect to child stdout"));
1056 new MyPipeFrame(this, cmd
, process
);
1059 static wxString gs_lastFile
;
1061 static bool AskUserForFileName()
1066 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1067 #else // !wxUSE_FILEDLG
1068 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1070 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1072 if ( filename
.empty() )
1075 gs_lastFile
= filename
;
1080 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1082 if ( !AskUserForFileName() )
1085 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1086 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1089 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1095 bool ok
= ft
->GetOpenCommand(&cmd
,
1096 wxFileType::MessageParameters(gs_lastFile
));
1100 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1108 void MyFrame::OnShowCommandForExt(wxCommandEvent
& WXUNUSED(event
))
1110 static wxString s_ext
;
1112 wxString ext
= wxGetTextFromUser
1114 "Enter the extension without leading dot",
1124 wxScopedPtr
<wxFileType
>
1125 ft(wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
));
1128 wxLogError("Information for extension \"%s\" not found", ext
);
1132 const wxString cmd
= ft
->GetOpenCommand("file." + ext
);
1135 wxLogWarning("Open command for extension \"%s\" not defined.", ext
);
1139 wxLogMessage("Open command for files of extension \"%s\" is\n%s",
1143 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1145 if ( !AskUserForFileName() )
1148 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1150 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1154 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1156 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1158 wxString filename
= wxGetTextFromUser
1160 wxT("Enter the URL"),
1166 if ( filename
.empty() )
1171 if ( !wxLaunchDefaultBrowser(s_url
) )
1173 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1177 // ----------------------------------------------------------------------------
1179 // ----------------------------------------------------------------------------
1183 bool MyFrame::GetDDEServer()
1185 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1186 DIALOG_TITLE
, m_server
);
1192 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1198 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1207 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1209 if ( !GetDDEServer() )
1213 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1216 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1221 if ( !conn
->Execute(m_cmdDde
) )
1223 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1228 wxLogStatus(wxT("Successfully executed DDE command"));
1233 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1235 if ( !GetDDEServer() )
1239 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1242 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1247 if ( !conn
->Request(m_cmdDde
) )
1249 wxLogError(wxT("Failed to send request '%s' via DDE."),
1254 wxLogStatus(wxT("Successfully sent DDE request."));
1259 #endif // __WINDOWS__
1261 // ----------------------------------------------------------------------------
1263 // ----------------------------------------------------------------------------
1266 void MyFrame::OnIdle(wxIdleEvent
& event
)
1268 size_t count
= m_running
.GetCount();
1269 for ( size_t n
= 0; n
< count
; n
++ )
1271 if ( m_running
[n
]->HasInput() )
1273 event
.RequestMore();
1278 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1283 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1285 static unsigned long s_ticks
= 0;
1286 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1289 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1291 RemovePipedProcess(process
);
1294 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1296 m_allAsync
.Remove(process
);
1301 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1303 if ( m_running
.IsEmpty() )
1305 // we want to start getting the timer events to ensure that a
1306 // steady stream of idle events comes in -- otherwise we
1307 // wouldn't be able to poll the child process input
1308 m_timerIdleWakeUp
.Start(100);
1310 //else: the timer is already running
1312 m_running
.Add(process
);
1313 m_allAsync
.Add(process
);
1316 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1318 m_running
.Remove(process
);
1320 if ( m_running
.IsEmpty() )
1322 // we don't need to get idle events all the time any more
1323 m_timerIdleWakeUp
.Stop();
1327 void MyFrame::ShowOutput(const wxString
& cmd
,
1328 const wxArrayString
& output
,
1329 const wxString
& title
)
1331 size_t count
= output
.GetCount();
1335 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1336 title
.c_str(), cmd
.c_str()));
1338 for ( size_t n
= 0; n
< count
; n
++ )
1340 m_lbox
->Append(output
[n
]);
1343 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1344 title
.Lower().c_str()));
1347 // ----------------------------------------------------------------------------
1349 // ----------------------------------------------------------------------------
1351 void MyProcess::OnTerminate(int pid
, int status
)
1353 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1354 pid
, m_cmd
.c_str(), status
);
1356 m_parent
->OnAsyncTermination(this);
1359 // ----------------------------------------------------------------------------
1361 // ----------------------------------------------------------------------------
1363 bool MyPipedProcess::HasInput()
1365 bool hasInput
= false;
1367 if ( IsInputAvailable() )
1369 wxTextInputStream
tis(*GetInputStream());
1371 // this assumes that the output is always line buffered
1373 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1375 m_parent
->GetLogListBox()->Append(msg
);
1380 if ( IsErrorAvailable() )
1382 wxTextInputStream
tis(*GetErrorStream());
1384 // this assumes that the output is always line buffered
1386 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1388 m_parent
->GetLogListBox()->Append(msg
);
1396 void MyPipedProcess::OnTerminate(int pid
, int status
)
1398 // show the rest of the output
1399 while ( HasInput() )
1402 m_parent
->OnProcessTerminated(this);
1404 MyProcess::OnTerminate(pid
, status
);
1407 // ----------------------------------------------------------------------------
1409 // ----------------------------------------------------------------------------
1411 bool MyPipedProcess2::HasInput()
1413 if ( !m_input
.empty() )
1415 wxTextOutputStream
os(*GetOutputStream());
1416 os
.WriteString(m_input
);
1421 // call us once again - may be we'll have output
1425 return MyPipedProcess::HasInput();
1428 // ============================================================================
1429 // MyPipeFrame implementation
1430 // ============================================================================
1432 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1433 const wxString
& cmd
,
1435 : wxFrame(parent
, wxID_ANY
, cmd
),
1437 // in a real program we'd check that the streams are !NULL here
1438 m_out(*process
->GetOutputStream()),
1439 m_in(*process
->GetInputStream()),
1440 m_err(*process
->GetErrorStream())
1442 m_process
->SetNextHandler(this);
1444 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1446 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1447 wxDefaultPosition
, wxDefaultSize
,
1448 wxTE_PROCESS_ENTER
);
1449 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1450 wxDefaultPosition
, wxDefaultSize
,
1451 wxTE_MULTILINE
| wxTE_RICH
);
1452 m_textIn
->SetEditable(false);
1453 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1454 wxDefaultPosition
, wxDefaultSize
,
1455 wxTE_MULTILINE
| wxTE_RICH
);
1456 m_textErr
->SetEditable(false);
1458 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1459 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1461 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1463 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1465 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1467 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1469 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1471 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1472 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1473 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1475 panel
->SetSizer(sizerTop
);
1476 sizerTop
->Fit(this);
1481 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1484 wxFileDialog
filedlg(this, wxT("Select file to send"));
1485 if ( filedlg
.ShowModal() != wxID_OK
)
1488 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1490 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1493 // can't write the entire string at once, this risk overflowing the pipe
1494 // and we would dead lock
1495 size_t len
= data
.length();
1496 const wxChar
*pc
= data
.c_str();
1499 const size_t CHUNK_SIZE
= 4096;
1500 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1502 // note that not all data could have been written as we don't block on
1503 // the write end of the pipe
1504 const size_t lenChunk
= m_out
.LastWrite();
1511 #endif // wxUSE_FILEDLG
1514 void MyPipeFrame::DoGet()
1516 // we don't have any way to be notified when any input appears on the
1517 // stream so we have to poll it :-(
1518 DoGetFromStream(m_textIn
, m_in
);
1519 DoGetFromStream(m_textErr
, m_err
);
1522 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1524 while ( in
.CanRead() )
1527 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = '\0';
1529 text
->AppendText(buffer
);
1533 void MyPipeFrame::DoClose()
1535 m_process
->CloseOutput();
1540 void MyPipeFrame::DisableInput()
1542 m_textOut
->SetEditable(false);
1543 FindWindow(Exec_Btn_Send
)->Disable();
1544 FindWindow(Exec_Btn_SendFile
)->Disable();
1545 FindWindow(Exec_Btn_Close
)->Disable();
1548 void MyPipeFrame::DisableOutput()
1550 FindWindow(Exec_Btn_Get
)->Disable();
1553 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1557 // we're not interested in getting the process termination notification
1558 // if we are closing it ourselves
1559 wxProcess
*process
= m_process
;
1561 process
->SetNextHandler(NULL
);
1563 process
->CloseOutput();
1569 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1573 wxDELETE(m_process
);
1575 wxLogWarning(wxT("The other process has terminated, closing"));