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
);
965 ShowOutput(cmd
, output
, wxT("Output"));
966 ShowOutput(cmd
, errors
, wxT("Errors"));
970 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
971 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
973 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
979 AddPipedProcess(process
);
986 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
989 m_cmdLast
= wxT("tr [a-z] [A-Z]");
991 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
998 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
1003 // always execute the filter asynchronously
1004 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
1005 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
1008 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
1010 AddPipedProcess(process
);
1014 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1022 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1024 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1030 wxProcess
*process
= wxProcess::Open(cmd
);
1033 wxLogError(wxT("Failed to launch the command."));
1037 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1039 wxOutputStream
*out
= process
->GetOutputStream();
1042 wxLogError(wxT("Failed to connect to child stdin"));
1046 wxInputStream
*in
= process
->GetInputStream();
1049 wxLogError(wxT("Failed to connect to child stdout"));
1053 new MyPipeFrame(this, cmd
, process
);
1056 static wxString gs_lastFile
;
1058 static bool AskUserForFileName()
1063 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1064 #else // !wxUSE_FILEDLG
1065 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1067 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1069 if ( filename
.empty() )
1072 gs_lastFile
= filename
;
1077 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1079 if ( !AskUserForFileName() )
1082 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1083 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1086 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1092 bool ok
= ft
->GetOpenCommand(&cmd
,
1093 wxFileType::MessageParameters(gs_lastFile
));
1097 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1105 void MyFrame::OnShowCommandForExt(wxCommandEvent
& WXUNUSED(event
))
1107 static wxString s_ext
;
1109 wxString ext
= wxGetTextFromUser
1111 "Enter the extension without leading dot",
1121 wxScopedPtr
<wxFileType
>
1122 ft(wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
));
1125 wxLogError("Information for extension \"%s\" not found", ext
);
1129 const wxString cmd
= ft
->GetOpenCommand("file." + ext
);
1132 wxLogWarning("Open command for extension \"%s\" not defined.", ext
);
1136 wxLogMessage("Open command for files of extension \"%s\" is\n%s",
1140 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1142 if ( !AskUserForFileName() )
1145 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1147 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1151 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1153 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1155 wxString filename
= wxGetTextFromUser
1157 wxT("Enter the URL"),
1163 if ( filename
.empty() )
1168 if ( !wxLaunchDefaultBrowser(s_url
) )
1170 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1174 // ----------------------------------------------------------------------------
1176 // ----------------------------------------------------------------------------
1180 bool MyFrame::GetDDEServer()
1182 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1183 DIALOG_TITLE
, m_server
);
1189 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1195 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1204 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1206 if ( !GetDDEServer() )
1210 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1213 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1218 if ( !conn
->Execute(m_cmdDde
) )
1220 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1225 wxLogStatus(wxT("Successfully executed DDE command"));
1230 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1232 if ( !GetDDEServer() )
1236 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1239 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1244 if ( !conn
->Request(m_cmdDde
) )
1246 wxLogError(wxT("Failed to send request '%s' via DDE."),
1251 wxLogStatus(wxT("Successfully sent DDE request."));
1256 #endif // __WINDOWS__
1258 // ----------------------------------------------------------------------------
1260 // ----------------------------------------------------------------------------
1263 void MyFrame::OnIdle(wxIdleEvent
& event
)
1265 size_t count
= m_running
.GetCount();
1266 for ( size_t n
= 0; n
< count
; n
++ )
1268 if ( m_running
[n
]->HasInput() )
1270 event
.RequestMore();
1275 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1280 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1282 static unsigned long s_ticks
= 0;
1283 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1286 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1288 RemovePipedProcess(process
);
1291 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1293 m_allAsync
.Remove(process
);
1298 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1300 if ( m_running
.IsEmpty() )
1302 // we want to start getting the timer events to ensure that a
1303 // steady stream of idle events comes in -- otherwise we
1304 // wouldn't be able to poll the child process input
1305 m_timerIdleWakeUp
.Start(100);
1307 //else: the timer is already running
1309 m_running
.Add(process
);
1310 m_allAsync
.Add(process
);
1313 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1315 m_running
.Remove(process
);
1317 if ( m_running
.IsEmpty() )
1319 // we don't need to get idle events all the time any more
1320 m_timerIdleWakeUp
.Stop();
1324 void MyFrame::ShowOutput(const wxString
& cmd
,
1325 const wxArrayString
& output
,
1326 const wxString
& title
)
1328 size_t count
= output
.GetCount();
1332 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1333 title
.c_str(), cmd
.c_str()));
1335 for ( size_t n
= 0; n
< count
; n
++ )
1337 m_lbox
->Append(output
[n
]);
1340 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1341 title
.Lower().c_str()));
1344 // ----------------------------------------------------------------------------
1346 // ----------------------------------------------------------------------------
1348 void MyProcess::OnTerminate(int pid
, int status
)
1350 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1351 pid
, m_cmd
.c_str(), status
);
1353 m_parent
->OnAsyncTermination(this);
1356 // ----------------------------------------------------------------------------
1358 // ----------------------------------------------------------------------------
1360 bool MyPipedProcess::HasInput()
1362 bool hasInput
= false;
1364 if ( IsInputAvailable() )
1366 wxTextInputStream
tis(*GetInputStream());
1368 // this assumes that the output is always line buffered
1370 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1372 m_parent
->GetLogListBox()->Append(msg
);
1377 if ( IsErrorAvailable() )
1379 wxTextInputStream
tis(*GetErrorStream());
1381 // this assumes that the output is always line buffered
1383 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1385 m_parent
->GetLogListBox()->Append(msg
);
1393 void MyPipedProcess::OnTerminate(int pid
, int status
)
1395 // show the rest of the output
1396 while ( HasInput() )
1399 m_parent
->OnProcessTerminated(this);
1401 MyProcess::OnTerminate(pid
, status
);
1404 // ----------------------------------------------------------------------------
1406 // ----------------------------------------------------------------------------
1408 bool MyPipedProcess2::HasInput()
1410 if ( !m_input
.empty() )
1412 wxTextOutputStream
os(*GetOutputStream());
1413 os
.WriteString(m_input
);
1418 // call us once again - may be we'll have output
1422 return MyPipedProcess::HasInput();
1425 // ============================================================================
1426 // MyPipeFrame implementation
1427 // ============================================================================
1429 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1430 const wxString
& cmd
,
1432 : wxFrame(parent
, wxID_ANY
, cmd
),
1434 // in a real program we'd check that the streams are !NULL here
1435 m_out(*process
->GetOutputStream()),
1436 m_in(*process
->GetInputStream()),
1437 m_err(*process
->GetErrorStream())
1439 m_process
->SetNextHandler(this);
1441 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1443 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1444 wxDefaultPosition
, wxDefaultSize
,
1445 wxTE_PROCESS_ENTER
);
1446 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1447 wxDefaultPosition
, wxDefaultSize
,
1448 wxTE_MULTILINE
| wxTE_RICH
);
1449 m_textIn
->SetEditable(false);
1450 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1451 wxDefaultPosition
, wxDefaultSize
,
1452 wxTE_MULTILINE
| wxTE_RICH
);
1453 m_textErr
->SetEditable(false);
1455 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1456 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1458 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1460 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1462 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1464 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1466 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1468 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1469 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1470 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1472 panel
->SetSizer(sizerTop
);
1473 sizerTop
->Fit(this);
1478 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1481 wxFileDialog
filedlg(this, wxT("Select file to send"));
1482 if ( filedlg
.ShowModal() != wxID_OK
)
1485 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1487 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1490 // can't write the entire string at once, this risk overflowing the pipe
1491 // and we would dead lock
1492 size_t len
= data
.length();
1493 const wxChar
*pc
= data
.c_str();
1496 const size_t CHUNK_SIZE
= 4096;
1497 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1499 // note that not all data could have been written as we don't block on
1500 // the write end of the pipe
1501 const size_t lenChunk
= m_out
.LastWrite();
1508 #endif // wxUSE_FILEDLG
1511 void MyPipeFrame::DoGet()
1513 // we don't have any way to be notified when any input appears on the
1514 // stream so we have to poll it :-(
1515 DoGetFromStream(m_textIn
, m_in
);
1516 DoGetFromStream(m_textErr
, m_err
);
1519 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1521 while ( in
.CanRead() )
1524 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = '\0';
1526 text
->AppendText(buffer
);
1530 void MyPipeFrame::DoClose()
1532 m_process
->CloseOutput();
1537 void MyPipeFrame::DisableInput()
1539 m_textOut
->SetEditable(false);
1540 FindWindow(Exec_Btn_Send
)->Disable();
1541 FindWindow(Exec_Btn_SendFile
)->Disable();
1542 FindWindow(Exec_Btn_Close
)->Disable();
1545 void MyPipeFrame::DisableOutput()
1547 FindWindow(Exec_Btn_Get
)->Disable();
1550 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1554 // we're not interested in getting the process termination notification
1555 // if we are closing it ourselves
1556 wxProcess
*process
= m_process
;
1558 process
->SetNextHandler(NULL
);
1560 process
->CloseOutput();
1566 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1570 wxDELETE(m_process
);
1572 wxLogWarning(wxT("The other process has terminated, closing"));