1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
7 // Copyright: (c) Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
26 // for all others, include the necessary headers (this file is usually all you
27 // need because it includes almost all "standard" wxWidgets headers
39 #include "wx/msgdlg.h"
40 #include "wx/textdlg.h"
41 #include "wx/filedlg.h"
42 #include "wx/choicdlg.h"
44 #include "wx/button.h"
45 #include "wx/checkbox.h"
46 #include "wx/stattext.h"
47 #include "wx/textctrl.h"
48 #include "wx/listbox.h"
53 #include "wx/txtstrm.h"
54 #include "wx/numdlg.h"
55 #include "wx/textdlg.h"
57 #include "wx/scopedptr.h"
58 #include "wx/stopwatch.h"
60 #include "wx/process.h"
62 #include "wx/mimetype.h"
68 #ifndef wxHAS_IMAGES_IN_RESOURCES
69 #include "../sample.xpm"
72 // ----------------------------------------------------------------------------
73 // the usual application and main frame classes
74 // ----------------------------------------------------------------------------
76 // Define a new application type, each program should derive a class from wxApp
77 class MyApp
: public wxApp
80 // override base class virtuals
81 // ----------------------------
83 // this one is called on application startup and is a good place for the app
84 // initialization (doing it here and not in the ctor allows to have an error
85 // return: if OnInit() returns false, the application terminates)
86 virtual bool OnInit();
89 // Define an array of process pointers used by MyFrame
91 WX_DEFINE_ARRAY_PTR(MyPipedProcess
*, MyPipedProcessesArray
);
94 WX_DEFINE_ARRAY_PTR(MyProcess
*, MyProcessesArray
);
96 // Define a new frame type: this is going to be our main frame
97 class MyFrame
: public wxFrame
101 MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
);
104 // event handlers (these functions should _not_ be virtual)
105 void OnQuit(wxCommandEvent
& event
);
107 void OnKill(wxCommandEvent
& event
);
109 void OnClear(wxCommandEvent
& event
);
111 void OnBeginBusyCursor(wxCommandEvent
& event
);
112 void OnEndBusyCursor(wxCommandEvent
& event
);
114 void OnSyncExec(wxCommandEvent
& event
);
115 void OnAsyncExec(wxCommandEvent
& event
);
116 void OnShell(wxCommandEvent
& event
);
117 void OnExecWithRedirect(wxCommandEvent
& event
);
118 void OnExecWithPipe(wxCommandEvent
& event
);
120 void OnPOpen(wxCommandEvent
& event
);
122 void OnFileExec(wxCommandEvent
& event
);
123 void OnFileLaunch(wxCommandEvent
& event
);
124 void OnOpenURL(wxCommandEvent
& event
);
125 void OnShowCommandForExt(wxCommandEvent
& event
);
127 void OnAbout(wxCommandEvent
& event
);
129 // polling output of async processes
130 void OnIdleTimer(wxTimerEvent
& event
);
131 void OnIdle(wxIdleEvent
& event
);
133 // for MyPipedProcess
134 void OnProcessTerminated(MyPipedProcess
*process
);
135 wxListBox
*GetLogListBox() const { return m_lbox
; }
138 void OnAsyncTermination(MyProcess
*process
);
140 // timer updating a counter in the background
141 void OnBgTimer(wxTimerEvent
& event
);
144 void ShowOutput(const wxString
& cmd
,
145 const wxArrayString
& output
,
146 const wxString
& title
);
148 int GetExecFlags() const;
150 void DoAsyncExec(const wxString
& cmd
);
152 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
154 void AddPipedProcess(MyPipedProcess
*process
);
155 void RemovePipedProcess(MyPipedProcess
*process
);
158 // the PID of the last process we launched asynchronously
161 // last command we executed
165 void OnDDEExec(wxCommandEvent
& event
);
166 void OnDDERequest(wxCommandEvent
& event
);
170 // last params of a DDE transaction
174 #endif // __WINDOWS__
178 // array of running processes with redirected IO
179 MyPipedProcessesArray m_running
;
181 // array of all asynchrously running processes
182 MyProcessesArray m_allAsync
;
184 // the idle event wake up timer
185 wxTimer m_timerIdleWakeUp
;
187 // a background timer allowing to easily check visually whether the
188 // messages are processed or not
191 // any class wishing to process wxWidgets events must use this macro
192 DECLARE_EVENT_TABLE()
195 // ----------------------------------------------------------------------------
196 // MyPipeFrame: allows the user to communicate with the child process
197 // ----------------------------------------------------------------------------
199 class MyPipeFrame
: public wxFrame
202 MyPipeFrame(wxFrame
*parent
,
207 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
208 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
209 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
210 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
211 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
213 void OnClose(wxCloseEvent
& event
);
215 void OnProcessTerm(wxProcessEvent
& event
);
219 wxString
s(m_textOut
->GetValue());
221 m_out
.Write(s
.c_str(), s
.length());
231 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
233 void DisableOutput();
236 wxProcess
*m_process
;
238 wxOutputStream
&m_out
;
242 wxTextCtrl
*m_textOut
,
246 DECLARE_EVENT_TABLE()
249 // ----------------------------------------------------------------------------
250 // wxProcess-derived classes
251 // ----------------------------------------------------------------------------
253 // This is the handler for process termination events
254 class MyProcess
: public wxProcess
257 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
258 : wxProcess(parent
), m_cmd(cmd
)
263 // instead of overriding this virtual function we might as well process the
264 // event from it in the frame class - this might be more convenient in some
266 virtual void OnTerminate(int pid
, int status
);
273 // A specialization of MyProcess for redirecting the output
274 class MyPipedProcess
: public MyProcess
277 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
278 : MyProcess(parent
, cmd
)
283 virtual void OnTerminate(int pid
, int status
);
285 virtual bool HasInput();
288 // A version of MyPipedProcess which also sends input to the stdin of the
290 class MyPipedProcess2
: public MyPipedProcess
293 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
294 : MyPipedProcess(parent
, cmd
), m_input(input
)
298 virtual bool HasInput();
304 // ----------------------------------------------------------------------------
306 // ----------------------------------------------------------------------------
308 // IDs for the controls and the menu commands
318 Exec_BeginBusyCursor
,
325 Exec_ShowCommandForExt
,
332 Exec_Flags_HideConsole
,
333 Exec_Flags_ShowConsole
,
335 Exec_About
= wxID_ABOUT
,
336 Exec_Quit
= wxID_EXIT
,
339 Exec_Btn_Send
= 1000,
345 static const wxChar
*DIALOG_TITLE
= wxT("Exec sample");
347 // ----------------------------------------------------------------------------
348 // event tables and other macros for wxWidgets
349 // ----------------------------------------------------------------------------
351 // the event tables connect the wxWidgets events with the functions (event
352 // handlers) which process them. It can be also done at run-time, but for the
353 // simple menu events like this the static method is much simpler.
354 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
355 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
356 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
357 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
358 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
359 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
361 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
362 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
363 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
364 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
365 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
367 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
369 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
370 EVT_MENU(Exec_ShowCommandForExt
, MyFrame::OnShowCommandForExt
)
371 EVT_MENU(Exec_LaunchFile
, MyFrame::OnFileLaunch
)
372 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
375 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
376 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
377 #endif // __WINDOWS__
379 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
381 EVT_IDLE(MyFrame::OnIdle
)
383 EVT_TIMER(Exec_TimerIdle
, MyFrame::OnIdleTimer
)
384 EVT_TIMER(Exec_TimerBg
, MyFrame::OnBgTimer
)
387 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
388 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
389 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
390 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
391 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
393 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
395 EVT_CLOSE(MyPipeFrame::OnClose
)
397 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
400 // Create a new application object: this macro will allow wxWidgets to create
401 // the application object during program execution (it's better than using a
402 // static object for many reasons) and also declares the accessor function
403 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
407 // ============================================================================
409 // ============================================================================
411 // ----------------------------------------------------------------------------
412 // the application class
413 // ----------------------------------------------------------------------------
415 // `Main program' equivalent: the program execution "starts" here
418 if ( !wxApp::OnInit() )
421 // Create the main application window
422 MyFrame
*frame
= new MyFrame(wxT("Exec wxWidgets sample"),
423 wxDefaultPosition
, wxSize(500, 140));
428 // success: wxApp::OnRun() will be called which will enter the main message
429 // loop and the application will run. If we returned false here, the
430 // application would exit immediately.
434 // ----------------------------------------------------------------------------
436 // ----------------------------------------------------------------------------
439 #pragma warning(disable: 4355) // this used in base member initializer list
443 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
444 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
445 m_timerIdleWakeUp(this, Exec_TimerIdle
),
446 m_timerBg(this, Exec_TimerBg
)
448 SetIcon(wxICON(sample
));
453 // we need this in order to allow the about menu relocation, since ABOUT is
454 // not the default id of the about menu
455 wxApp::s_macAboutMenuItemId
= Exec_About
;
459 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
460 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
461 wxT("Kill a process by PID"));
462 menuFile
->AppendSeparator();
463 menuFile
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
464 wxT("Launch the command to open this kind of files"));
465 menuFile
->Append(Exec_ShowCommandForExt
,
466 "Show association for extension...\tShift-Ctrl-A",
467 "Show the command associated with the given extension");
468 menuFile
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
469 wxT("Launch the default application associated with the file"));
470 menuFile
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
471 wxT("Launch the default browser with the given URL"));
472 menuFile
->AppendSeparator();
473 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
474 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
475 menuFile
->AppendSeparator();
476 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
477 wxT("Clear the log window"));
478 menuFile
->AppendSeparator();
479 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
481 wxMenu
*flagsMenu
= new wxMenu
;
482 flagsMenu
->AppendCheckItem(Exec_Flags_HideConsole
, "Always &hide console");
483 flagsMenu
->AppendCheckItem(Exec_Flags_ShowConsole
, "Always &show console");
484 flagsMenu
->AppendCheckItem(Exec_Flags_NoEvents
, "Disable &events",
485 "This flag is valid for sync execution only");
487 wxMenu
*execMenu
= new wxMenu
;
488 execMenu
->AppendSubMenu(flagsMenu
, "Execution flags");
489 execMenu
->AppendSeparator();
490 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
491 wxT("Launch a program and return when it terminates"));
492 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
493 wxT("Launch a program and return immediately"));
494 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
495 wxT("Launch a shell and execute a command in it"));
496 execMenu
->AppendSeparator();
497 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
498 wxT("Launch a program and capture its output"));
499 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
500 wxT("Pipe a string through a filter"));
501 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
502 wxT("Open a pipe to and from another program"));
505 execMenu
->AppendSeparator();
506 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
507 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
510 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
511 helpMenu
->Append(Exec_About
, wxT("&About\tF1"), wxT("Show about dialog"));
513 // now append the freshly created menu to the menu bar...
514 wxMenuBar
*menuBar
= new wxMenuBar();
515 menuBar
->Append(menuFile
, wxT("&File"));
516 menuBar
->Append(execMenu
, wxT("&Exec"));
517 menuBar
->Append(helpMenu
, wxT("&Help"));
519 // ... and attach this menu bar to the frame
522 // create the listbox in which we will show misc messages as they come
523 m_lbox
= new wxListBox(this, wxID_ANY
);
524 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
525 wxFONTWEIGHT_NORMAL
);
527 m_lbox
->SetFont(font
);
530 // create a status bar just for fun (by default with 1 pane only)
532 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
533 #endif // wxUSE_STATUSBAR
535 m_timerBg
.Start(1000);
540 // any processes left until now must be deleted manually: normally this is
541 // done when the associated process terminates but it must be still running
542 // if this didn't happen until now
543 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
545 delete m_allAsync
[n
];
549 // ----------------------------------------------------------------------------
550 // event handlers: file and help menu
551 // ----------------------------------------------------------------------------
553 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
555 // true is to force the frame to close
559 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
564 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
569 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
574 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
576 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
577 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
580 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
582 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
584 wxT("Exec question"),
586 // we need the full unsigned int range
597 static const wxString signalNames
[] =
599 wxT("Just test (SIGNONE)"),
600 wxT("Hangup (SIGHUP)"),
601 wxT("Interrupt (SIGINT)"),
602 wxT("Quit (SIGQUIT)"),
603 wxT("Illegal instruction (SIGILL)"),
604 wxT("Trap (SIGTRAP)"),
605 wxT("Abort (SIGABRT)"),
606 wxT("Emulated trap (SIGEMT)"),
607 wxT("FP exception (SIGFPE)"),
608 wxT("Kill (SIGKILL)"),
610 wxT("Segment violation (SIGSEGV)"),
611 wxT("System (SIGSYS)"),
612 wxT("Broken pipe (SIGPIPE)"),
613 wxT("Alarm (SIGALRM)"),
614 wxT("Terminate (SIGTERM)"),
617 static int s_sigLast
= wxSIGNONE
;
618 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
619 wxT("Exec question"),
620 WXSIZEOF(signalNames
), signalNames
,
626 wxFAIL_MSG( wxT("unexpected return value") );
654 if ( sig
== wxSIGNONE
)
656 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
657 if ( wxProcess::Exists(pid
) )
659 wxLogStatus(wxT("Process %ld is running."), pid
);
663 wxLogStatus(wxT("No process with pid = %ld."), pid
);
668 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
669 if ( rc
== wxKILL_OK
)
671 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
675 static const wxChar
*errorText
[] =
678 wxT("signal not supported"),
679 wxT("permission denied"),
680 wxT("no such process"),
681 wxT("unspecified error"),
684 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
685 pid
, sig
, errorText
[rc
]);
690 // ----------------------------------------------------------------------------
691 // execution options dialog
692 // ----------------------------------------------------------------------------
694 enum ExecQueryDialogID
701 class ExecQueryDialog
: public wxDialog
704 ExecQueryDialog(const wxString
& cmd
);
706 wxString
GetExecutable() const
708 return m_executable
->GetValue();
711 wxString
GetWorkDir() const
713 return m_useCWD
->GetValue() ? m_cwdtext
->GetValue() : wxString();
716 void GetEnvironment(wxEnvVariableHashMap
& env
);
719 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent
& event
)
721 event
.Enable(m_useCWD
->GetValue());
724 void OnUpdateEnvironmentUI(wxUpdateUIEvent
& event
)
726 event
.Enable(m_useEnv
->GetValue());
729 wxTextCtrl
* m_executable
;
730 wxTextCtrl
* m_cwdtext
;
731 wxTextCtrl
* m_envtext
;
732 wxCheckBox
* m_useCWD
;
733 wxCheckBox
* m_useEnv
;
735 DECLARE_EVENT_TABLE()
738 BEGIN_EVENT_TABLE(ExecQueryDialog
, wxDialog
)
739 EVT_UPDATE_UI(TEXT_CWD
, ExecQueryDialog::OnUpdateWorkingDirectoryUI
)
740 EVT_UPDATE_UI(TEXT_ENVIRONMENT
, ExecQueryDialog::OnUpdateEnvironmentUI
)
743 ExecQueryDialog::ExecQueryDialog(const wxString
& cmd
)
744 : wxDialog(NULL
, wxID_ANY
, DIALOG_TITLE
,
745 wxDefaultPosition
, wxDefaultSize
,
746 wxDEFAULT_DIALOG_STYLE
| wxRESIZE_BORDER
)
748 wxSizer
* globalSizer
= new wxBoxSizer(wxVERTICAL
);
750 m_executable
= new wxTextCtrl(this, TEXT_EXECUTABLE
, wxString());
751 m_cwdtext
= new wxTextCtrl(this, TEXT_CWD
, wxString());
752 m_envtext
= new wxTextCtrl(this, TEXT_ENVIRONMENT
, wxString(),
753 wxDefaultPosition
, wxSize(300, 200),
754 wxTE_MULTILINE
|wxHSCROLL
);
756 const wxSizerFlags flagsExpand
= wxSizerFlags().Expand().Border();
757 globalSizer
->Add(new wxStaticText(this, wxID_ANY
, "Enter the command: "),
759 globalSizer
->Add(m_executable
, flagsExpand
);
761 m_useCWD
= new wxCheckBox(this, wxID_ANY
, "Working directory: ");
762 globalSizer
->Add(m_useCWD
, flagsExpand
);
763 globalSizer
->Add(m_cwdtext
, flagsExpand
);
765 m_useEnv
= new wxCheckBox(this, wxID_ANY
, "Environment: ");
766 globalSizer
->Add(m_useEnv
, flagsExpand
);
767 globalSizer
->Add(m_envtext
, wxSizerFlags(flagsExpand
).Proportion(1));
769 globalSizer
->Add(CreateStdDialogButtonSizer(wxOK
|wxCANCEL
), flagsExpand
);
770 SetSizerAndFit(globalSizer
);
773 m_executable
->SetValue(cmd
);
774 m_cwdtext
->SetValue(wxGetCwd());
775 wxEnvVariableHashMap env
;
776 if ( wxGetEnvMap(&env
) )
778 for ( wxEnvVariableHashMap::iterator it
= env
.begin();
782 m_envtext
->AppendText(it
->first
+ '=' + it
->second
+ '\n');
785 m_useCWD
->SetValue(false);
786 m_useEnv
->SetValue(false);
789 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap
& env
)
792 if ( m_useEnv
->GetValue() )
797 const int nb
= m_envtext
->GetNumberOfLines();
798 for ( int l
= 0; l
< nb
; l
++ )
800 const wxString line
= m_envtext
->GetLineText(l
).Trim();
804 name
= line
.BeforeFirst('=', &value
);
807 wxLogWarning("Skipping invalid environment line \"%s\".", line
);
817 static bool QueryExec(wxString
& cmd
, wxExecuteEnv
& env
)
819 ExecQueryDialog
dialog(cmd
);
821 if ( dialog
.ShowModal() != wxID_OK
)
824 cmd
= dialog
.GetExecutable();
825 env
.cwd
= dialog
.GetWorkDir();
826 dialog
.GetEnvironment(env
.env
);
831 // ----------------------------------------------------------------------------
832 // event handlers: exec menu
833 // ----------------------------------------------------------------------------
835 int MyFrame::GetExecFlags() const
837 wxMenuBar
* const mbar
= GetMenuBar();
841 if ( mbar
->IsChecked(Exec_Flags_HideConsole
) )
842 flags
|= wxEXEC_HIDE_CONSOLE
;
843 if ( mbar
->IsChecked(Exec_Flags_ShowConsole
) )
844 flags
|= wxEXEC_SHOW_CONSOLE
;
845 if ( mbar
->IsChecked(Exec_Flags_NoEvents
) )
846 flags
|= wxEXEC_NOEVENTS
;
851 void MyFrame::DoAsyncExec(const wxString
& cmd
)
853 MyProcess
* const process
= new MyProcess(this, cmd
);
854 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
| GetExecFlags(), process
);
857 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
863 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
867 // the parent frame keeps track of all async processes as it needs to
868 // free them if we exit before the child process terminates
869 AddAsyncProcess(process
);
873 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
877 if ( !QueryExec(cmd
, env
) )
880 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
882 int code
= wxExecute(cmd
, wxEXEC_SYNC
| GetExecFlags(), NULL
, &env
);
884 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
890 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
892 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
902 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
904 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
911 int code
= wxShell(cmd
);
912 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
917 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
922 m_cmdLast
= "type Makefile.in";
924 m_cmdLast
= "cat -n Makefile";
928 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
936 switch ( wxMessageBox(wxT("Execute it synchronously?"),
937 wxT("Exec question"),
938 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
954 wxLogStatus("\"%s\" is running please wait...", cmd
);
958 wxArrayString output
, errors
;
959 int code
= wxExecute(cmd
, output
, errors
);
961 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
962 cmd
, sw
.Time(), code
);
964 ShowOutput(cmd
, output
, wxT("Output"));
965 ShowOutput(cmd
, errors
, wxT("Errors"));
969 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
970 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
972 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
978 AddPipedProcess(process
);
985 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
988 m_cmdLast
= wxT("tr [a-z] [A-Z]");
990 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
997 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
1002 // always execute the filter asynchronously
1003 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
1004 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
1007 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
1009 AddPipedProcess(process
);
1013 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1021 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1023 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1029 wxProcess
*process
= wxProcess::Open(cmd
);
1032 wxLogError(wxT("Failed to launch the command."));
1036 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1038 wxOutputStream
*out
= process
->GetOutputStream();
1041 wxLogError(wxT("Failed to connect to child stdin"));
1045 wxInputStream
*in
= process
->GetInputStream();
1048 wxLogError(wxT("Failed to connect to child stdout"));
1052 new MyPipeFrame(this, cmd
, process
);
1055 static wxString gs_lastFile
;
1057 static bool AskUserForFileName()
1062 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1063 #else // !wxUSE_FILEDLG
1064 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1066 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1068 if ( filename
.empty() )
1071 gs_lastFile
= filename
;
1076 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1078 if ( !AskUserForFileName() )
1081 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1082 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1085 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1091 bool ok
= ft
->GetOpenCommand(&cmd
,
1092 wxFileType::MessageParameters(gs_lastFile
));
1096 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1104 void MyFrame::OnShowCommandForExt(wxCommandEvent
& WXUNUSED(event
))
1106 static wxString s_ext
;
1108 wxString ext
= wxGetTextFromUser
1110 "Enter the extension without leading dot",
1120 wxScopedPtr
<wxFileType
>
1121 ft(wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
));
1124 wxLogError("Information for extension \"%s\" not found", ext
);
1128 const wxString cmd
= ft
->GetOpenCommand("file." + ext
);
1131 wxLogWarning("Open command for extension \"%s\" not defined.", ext
);
1135 wxLogMessage("Open command for files of extension \"%s\" is\n%s",
1139 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1141 if ( !AskUserForFileName() )
1144 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1146 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1150 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1152 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1154 wxString filename
= wxGetTextFromUser
1156 wxT("Enter the URL"),
1162 if ( filename
.empty() )
1167 if ( !wxLaunchDefaultBrowser(s_url
) )
1169 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1173 // ----------------------------------------------------------------------------
1175 // ----------------------------------------------------------------------------
1179 bool MyFrame::GetDDEServer()
1181 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1182 DIALOG_TITLE
, m_server
);
1188 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1194 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1203 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1205 if ( !GetDDEServer() )
1209 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1212 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1217 if ( !conn
->Execute(m_cmdDde
) )
1219 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1224 wxLogStatus(wxT("Successfully executed DDE command"));
1229 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1231 if ( !GetDDEServer() )
1235 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1238 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1243 if ( !conn
->Request(m_cmdDde
) )
1245 wxLogError(wxT("Failed to send request '%s' via DDE."),
1250 wxLogStatus(wxT("Successfully sent DDE request."));
1255 #endif // __WINDOWS__
1257 // ----------------------------------------------------------------------------
1259 // ----------------------------------------------------------------------------
1262 void MyFrame::OnIdle(wxIdleEvent
& event
)
1264 size_t count
= m_running
.GetCount();
1265 for ( size_t n
= 0; n
< count
; n
++ )
1267 if ( m_running
[n
]->HasInput() )
1269 event
.RequestMore();
1274 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1279 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1281 static unsigned long s_ticks
= 0;
1282 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1285 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1287 RemovePipedProcess(process
);
1290 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1292 m_allAsync
.Remove(process
);
1297 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1299 if ( m_running
.IsEmpty() )
1301 // we want to start getting the timer events to ensure that a
1302 // steady stream of idle events comes in -- otherwise we
1303 // wouldn't be able to poll the child process input
1304 m_timerIdleWakeUp
.Start(100);
1306 //else: the timer is already running
1308 m_running
.Add(process
);
1309 m_allAsync
.Add(process
);
1312 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1314 m_running
.Remove(process
);
1316 if ( m_running
.IsEmpty() )
1318 // we don't need to get idle events all the time any more
1319 m_timerIdleWakeUp
.Stop();
1323 void MyFrame::ShowOutput(const wxString
& cmd
,
1324 const wxArrayString
& output
,
1325 const wxString
& title
)
1327 size_t count
= output
.GetCount();
1331 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1332 title
.c_str(), cmd
.c_str()));
1334 for ( size_t n
= 0; n
< count
; n
++ )
1336 m_lbox
->Append(output
[n
]);
1339 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1340 title
.Lower().c_str()));
1343 // ----------------------------------------------------------------------------
1345 // ----------------------------------------------------------------------------
1347 void MyProcess::OnTerminate(int pid
, int status
)
1349 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1350 pid
, m_cmd
.c_str(), status
);
1352 m_parent
->OnAsyncTermination(this);
1355 // ----------------------------------------------------------------------------
1357 // ----------------------------------------------------------------------------
1359 bool MyPipedProcess::HasInput()
1361 bool hasInput
= false;
1363 if ( IsInputAvailable() )
1365 wxTextInputStream
tis(*GetInputStream());
1367 // this assumes that the output is always line buffered
1369 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1371 m_parent
->GetLogListBox()->Append(msg
);
1376 if ( IsErrorAvailable() )
1378 wxTextInputStream
tis(*GetErrorStream());
1380 // this assumes that the output is always line buffered
1382 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1384 m_parent
->GetLogListBox()->Append(msg
);
1392 void MyPipedProcess::OnTerminate(int pid
, int status
)
1394 // show the rest of the output
1395 while ( HasInput() )
1398 m_parent
->OnProcessTerminated(this);
1400 MyProcess::OnTerminate(pid
, status
);
1403 // ----------------------------------------------------------------------------
1405 // ----------------------------------------------------------------------------
1407 bool MyPipedProcess2::HasInput()
1409 if ( !m_input
.empty() )
1411 wxTextOutputStream
os(*GetOutputStream());
1412 os
.WriteString(m_input
);
1417 // call us once again - may be we'll have output
1421 return MyPipedProcess::HasInput();
1424 // ============================================================================
1425 // MyPipeFrame implementation
1426 // ============================================================================
1428 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1429 const wxString
& cmd
,
1431 : wxFrame(parent
, wxID_ANY
, cmd
),
1433 // in a real program we'd check that the streams are !NULL here
1434 m_out(*process
->GetOutputStream()),
1435 m_in(*process
->GetInputStream()),
1436 m_err(*process
->GetErrorStream())
1438 m_process
->SetNextHandler(this);
1440 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1442 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1443 wxDefaultPosition
, wxDefaultSize
,
1444 wxTE_PROCESS_ENTER
);
1445 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1446 wxDefaultPosition
, wxDefaultSize
,
1447 wxTE_MULTILINE
| wxTE_RICH
);
1448 m_textIn
->SetEditable(false);
1449 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1450 wxDefaultPosition
, wxDefaultSize
,
1451 wxTE_MULTILINE
| wxTE_RICH
);
1452 m_textErr
->SetEditable(false);
1454 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1455 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1457 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1459 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1461 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1463 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1465 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1467 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1468 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1469 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1471 panel
->SetSizer(sizerTop
);
1472 sizerTop
->Fit(this);
1477 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1480 wxFileDialog
filedlg(this, wxT("Select file to send"));
1481 if ( filedlg
.ShowModal() != wxID_OK
)
1484 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1486 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1489 // can't write the entire string at once, this risk overflowing the pipe
1490 // and we would dead lock
1491 size_t len
= data
.length();
1492 const wxChar
*pc
= data
.c_str();
1495 const size_t CHUNK_SIZE
= 4096;
1496 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1498 // note that not all data could have been written as we don't block on
1499 // the write end of the pipe
1500 const size_t lenChunk
= m_out
.LastWrite();
1507 #endif // wxUSE_FILEDLG
1510 void MyPipeFrame::DoGet()
1512 // we don't have any way to be notified when any input appears on the
1513 // stream so we have to poll it :-(
1514 DoGetFromStream(m_textIn
, m_in
);
1515 DoGetFromStream(m_textErr
, m_err
);
1518 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1520 while ( in
.CanRead() )
1523 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = '\0';
1525 text
->AppendText(buffer
);
1529 void MyPipeFrame::DoClose()
1531 m_process
->CloseOutput();
1536 void MyPipeFrame::DisableInput()
1538 m_textOut
->SetEditable(false);
1539 FindWindow(Exec_Btn_Send
)->Disable();
1540 FindWindow(Exec_Btn_SendFile
)->Disable();
1541 FindWindow(Exec_Btn_Close
)->Disable();
1544 void MyPipeFrame::DisableOutput()
1546 FindWindow(Exec_Btn_Get
)->Disable();
1549 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1553 // we're not interested in getting the process termination notification
1554 // if we are closing it ourselves
1555 wxProcess
*process
= m_process
;
1557 process
->SetNextHandler(NULL
);
1559 process
->CloseOutput();
1565 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1569 wxDELETE(m_process
);
1571 wxLogWarning(wxT("The other process has terminated, closing"));