1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
27 // for all others, include the necessary headers (this file is usually all you
28 // need because it includes almost all "standard" wxWidgets headers
40 #include "wx/msgdlg.h"
41 #include "wx/textdlg.h"
42 #include "wx/filedlg.h"
43 #include "wx/choicdlg.h"
45 #include "wx/button.h"
46 #include "wx/checkbox.h"
47 #include "wx/stattext.h"
48 #include "wx/textctrl.h"
49 #include "wx/listbox.h"
54 #include "wx/txtstrm.h"
55 #include "wx/numdlg.h"
56 #include "wx/textdlg.h"
58 #include "wx/stopwatch.h"
60 #include "wx/process.h"
62 #include "wx/mimetype.h"
68 #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
);
126 void OnAbout(wxCommandEvent
& event
);
128 // polling output of async processes
129 void OnIdleTimer(wxTimerEvent
& event
);
130 void OnIdle(wxIdleEvent
& event
);
132 // for MyPipedProcess
133 void OnProcessTerminated(MyPipedProcess
*process
);
134 wxListBox
*GetLogListBox() const { return m_lbox
; }
137 void OnAsyncTermination(MyProcess
*process
);
139 // timer updating a counter in the background
140 void OnBgTimer(wxTimerEvent
& event
);
143 void ShowOutput(const wxString
& cmd
,
144 const wxArrayString
& output
,
145 const wxString
& title
);
147 int GetExecFlags() const;
149 void DoAsyncExec(const wxString
& cmd
);
151 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
153 void AddPipedProcess(MyPipedProcess
*process
);
154 void RemovePipedProcess(MyPipedProcess
*process
);
157 // the PID of the last process we launched asynchronously
160 // last command we executed
164 void OnDDEExec(wxCommandEvent
& event
);
165 void OnDDERequest(wxCommandEvent
& event
);
169 // last params of a DDE transaction
173 #endif // __WINDOWS__
177 // array of running processes with redirected IO
178 MyPipedProcessesArray m_running
;
180 // array of all asynchrously running processes
181 MyProcessesArray m_allAsync
;
183 // the idle event wake up timer
184 wxTimer m_timerIdleWakeUp
;
186 // a background timer allowing to easily check visually whether the
187 // messages are processed or not
190 // any class wishing to process wxWidgets events must use this macro
191 DECLARE_EVENT_TABLE()
194 // ----------------------------------------------------------------------------
195 // MyPipeFrame: allows the user to communicate with the child process
196 // ----------------------------------------------------------------------------
198 class MyPipeFrame
: public wxFrame
201 MyPipeFrame(wxFrame
*parent
,
206 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
207 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
208 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
209 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
210 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
212 void OnClose(wxCloseEvent
& event
);
214 void OnProcessTerm(wxProcessEvent
& event
);
218 wxString
s(m_textOut
->GetValue());
220 m_out
.Write(s
.c_str(), s
.length());
230 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
232 void DisableOutput();
235 wxProcess
*m_process
;
237 wxOutputStream
&m_out
;
241 wxTextCtrl
*m_textOut
,
245 DECLARE_EVENT_TABLE()
248 // ----------------------------------------------------------------------------
249 // wxProcess-derived classes
250 // ----------------------------------------------------------------------------
252 // This is the handler for process termination events
253 class MyProcess
: public wxProcess
256 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
257 : wxProcess(parent
), m_cmd(cmd
)
262 // instead of overriding this virtual function we might as well process the
263 // event from it in the frame class - this might be more convenient in some
265 virtual void OnTerminate(int pid
, int status
);
272 // A specialization of MyProcess for redirecting the output
273 class MyPipedProcess
: public MyProcess
276 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
277 : MyProcess(parent
, cmd
)
282 virtual void OnTerminate(int pid
, int status
);
284 virtual bool HasInput();
287 // A version of MyPipedProcess which also sends input to the stdin of the
289 class MyPipedProcess2
: public MyPipedProcess
292 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
293 : MyPipedProcess(parent
, cmd
), m_input(input
)
297 virtual bool HasInput();
303 // ----------------------------------------------------------------------------
305 // ----------------------------------------------------------------------------
307 // IDs for the controls and the menu commands
317 Exec_BeginBusyCursor
,
330 Exec_Flags_HideConsole
,
331 Exec_Flags_ShowConsole
,
333 Exec_About
= wxID_ABOUT
,
334 Exec_Quit
= wxID_EXIT
,
337 Exec_Btn_Send
= 1000,
343 static const wxChar
*DIALOG_TITLE
= wxT("Exec sample");
345 // ----------------------------------------------------------------------------
346 // event tables and other macros for wxWidgets
347 // ----------------------------------------------------------------------------
349 // the event tables connect the wxWidgets events with the functions (event
350 // handlers) which process them. It can be also done at run-time, but for the
351 // simple menu events like this the static method is much simpler.
352 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
353 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
354 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
355 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
356 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
357 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
359 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
360 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
361 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
362 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
363 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
365 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
367 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
368 EVT_MENU(Exec_LaunchFile
, MyFrame::OnFileLaunch
)
369 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
372 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
373 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
374 #endif // __WINDOWS__
376 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
378 EVT_IDLE(MyFrame::OnIdle
)
380 EVT_TIMER(Exec_TimerIdle
, MyFrame::OnIdleTimer
)
381 EVT_TIMER(Exec_TimerBg
, MyFrame::OnBgTimer
)
384 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
385 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
386 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
387 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
388 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
390 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
392 EVT_CLOSE(MyPipeFrame::OnClose
)
394 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
397 // Create a new application object: this macro will allow wxWidgets to create
398 // the application object during program execution (it's better than using a
399 // static object for many reasons) and also declares the accessor function
400 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
404 // ============================================================================
406 // ============================================================================
408 // ----------------------------------------------------------------------------
409 // the application class
410 // ----------------------------------------------------------------------------
412 // `Main program' equivalent: the program execution "starts" here
415 if ( !wxApp::OnInit() )
418 // Create the main application window
419 MyFrame
*frame
= new MyFrame(wxT("Exec wxWidgets sample"),
420 wxDefaultPosition
, wxSize(500, 140));
425 // success: wxApp::OnRun() will be called which will enter the main message
426 // loop and the application will run. If we returned false here, the
427 // application would exit immediately.
431 // ----------------------------------------------------------------------------
433 // ----------------------------------------------------------------------------
436 #pragma warning(disable: 4355) // this used in base member initializer list
440 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
441 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
442 m_timerIdleWakeUp(this, Exec_TimerIdle
),
443 m_timerBg(this, Exec_TimerBg
)
445 SetIcon(wxICON(sample
));
450 // we need this in order to allow the about menu relocation, since ABOUT is
451 // not the default id of the about menu
452 wxApp::s_macAboutMenuItemId
= Exec_About
;
456 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
457 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
458 wxT("Kill a process by PID"));
459 menuFile
->AppendSeparator();
460 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
461 wxT("Clear the log window"));
462 menuFile
->AppendSeparator();
463 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
464 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
465 menuFile
->AppendSeparator();
466 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
468 wxMenu
*flagsMenu
= new wxMenu
;
469 flagsMenu
->AppendCheckItem(Exec_Flags_HideConsole
, "Always &hide console");
470 flagsMenu
->AppendCheckItem(Exec_Flags_ShowConsole
, "Always &show console");
471 flagsMenu
->AppendCheckItem(Exec_Flags_NoEvents
, "Disable &events",
472 "This flag is valid for sync execution only");
474 wxMenu
*execMenu
= new wxMenu
;
475 execMenu
->AppendSubMenu(flagsMenu
, "Execution flags");
476 execMenu
->AppendSeparator();
477 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
478 wxT("Launch a program and return when it terminates"));
479 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
480 wxT("Launch a program and return immediately"));
481 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
482 wxT("Launch a shell and execute a command in it"));
483 execMenu
->AppendSeparator();
484 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
485 wxT("Launch a program and capture its output"));
486 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
487 wxT("Pipe a string through a filter"));
488 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
489 wxT("Open a pipe to and from another program"));
491 execMenu
->AppendSeparator();
492 execMenu
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
493 wxT("Launch the command to open this kind of files"));
494 execMenu
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
495 wxT("Launch the default application associated with the file"));
496 execMenu
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
497 wxT("Launch the default browser with the given URL"));
499 execMenu
->AppendSeparator();
500 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
501 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
504 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
505 helpMenu
->Append(Exec_About
, wxT("&About\tF1"), wxT("Show about dialog"));
507 // now append the freshly created menu to the menu bar...
508 wxMenuBar
*menuBar
= new wxMenuBar();
509 menuBar
->Append(menuFile
, wxT("&File"));
510 menuBar
->Append(execMenu
, wxT("&Exec"));
511 menuBar
->Append(helpMenu
, wxT("&Help"));
513 // ... and attach this menu bar to the frame
516 // create the listbox in which we will show misc messages as they come
517 m_lbox
= new wxListBox(this, wxID_ANY
);
518 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
519 wxFONTWEIGHT_NORMAL
);
521 m_lbox
->SetFont(font
);
524 // create a status bar just for fun (by default with 1 pane only)
526 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
527 #endif // wxUSE_STATUSBAR
529 m_timerBg
.Start(1000);
534 // any processes left until now must be deleted manually: normally this is
535 // done when the associated process terminates but it must be still running
536 // if this didn't happen until now
537 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
539 delete m_allAsync
[n
];
543 // ----------------------------------------------------------------------------
544 // event handlers: file and help menu
545 // ----------------------------------------------------------------------------
547 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
549 // true is to force the frame to close
553 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
558 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
563 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
568 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
570 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
571 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
574 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
576 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
578 wxT("Exec question"),
580 // we need the full unsigned int range
591 static const wxString signalNames
[] =
593 wxT("Just test (SIGNONE)"),
594 wxT("Hangup (SIGHUP)"),
595 wxT("Interrupt (SIGINT)"),
596 wxT("Quit (SIGQUIT)"),
597 wxT("Illegal instruction (SIGILL)"),
598 wxT("Trap (SIGTRAP)"),
599 wxT("Abort (SIGABRT)"),
600 wxT("Emulated trap (SIGEMT)"),
601 wxT("FP exception (SIGFPE)"),
602 wxT("Kill (SIGKILL)"),
604 wxT("Segment violation (SIGSEGV)"),
605 wxT("System (SIGSYS)"),
606 wxT("Broken pipe (SIGPIPE)"),
607 wxT("Alarm (SIGALRM)"),
608 wxT("Terminate (SIGTERM)"),
611 static int s_sigLast
= wxSIGNONE
;
612 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
613 wxT("Exec question"),
614 WXSIZEOF(signalNames
), signalNames
,
620 wxFAIL_MSG( wxT("unexpected return value") );
648 if ( sig
== wxSIGNONE
)
650 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
651 if ( wxProcess::Exists(pid
) )
653 wxLogStatus(wxT("Process %ld is running."), pid
);
657 wxLogStatus(wxT("No process with pid = %ld."), pid
);
662 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
663 if ( rc
== wxKILL_OK
)
665 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
669 static const wxChar
*errorText
[] =
672 wxT("signal not supported"),
673 wxT("permission denied"),
674 wxT("no such process"),
675 wxT("unspecified error"),
678 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
679 pid
, sig
, errorText
[rc
]);
684 // ----------------------------------------------------------------------------
685 // execution options dialog
686 // ----------------------------------------------------------------------------
688 enum ExecQueryDialogID
695 class ExecQueryDialog
: public wxDialog
698 ExecQueryDialog(const wxString
& cmd
);
700 wxString
GetExecutable() const
702 return m_executable
->GetValue();
705 wxString
GetWorkDir() const
707 return m_useCWD
->GetValue() ? m_cwdtext
->GetValue() : wxString();
710 void GetEnvironment(wxEnvVariableHashMap
& env
);
713 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent
& event
)
715 event
.Enable(m_useCWD
->GetValue());
718 void OnUpdateEnvironmentUI(wxUpdateUIEvent
& event
)
720 event
.Enable(m_useEnv
->GetValue());
723 wxTextCtrl
* m_executable
;
724 wxTextCtrl
* m_cwdtext
;
725 wxTextCtrl
* m_envtext
;
726 wxCheckBox
* m_useCWD
;
727 wxCheckBox
* m_useEnv
;
729 DECLARE_EVENT_TABLE()
732 BEGIN_EVENT_TABLE(ExecQueryDialog
, wxDialog
)
733 EVT_UPDATE_UI(TEXT_CWD
, ExecQueryDialog::OnUpdateWorkingDirectoryUI
)
734 EVT_UPDATE_UI(TEXT_ENVIRONMENT
, ExecQueryDialog::OnUpdateEnvironmentUI
)
737 ExecQueryDialog::ExecQueryDialog(const wxString
& cmd
)
738 : wxDialog(NULL
, wxID_ANY
, DIALOG_TITLE
,
739 wxDefaultPosition
, wxDefaultSize
,
740 wxDEFAULT_DIALOG_STYLE
| wxRESIZE_BORDER
)
742 wxSizer
* globalSizer
= new wxBoxSizer(wxVERTICAL
);
744 m_executable
= new wxTextCtrl(this, TEXT_EXECUTABLE
, wxString());
745 m_cwdtext
= new wxTextCtrl(this, TEXT_CWD
, wxString());
746 m_envtext
= new wxTextCtrl(this, TEXT_ENVIRONMENT
, wxString(),
747 wxDefaultPosition
, wxSize(300, 200),
748 wxTE_MULTILINE
|wxHSCROLL
);
750 const wxSizerFlags flagsExpand
= wxSizerFlags().Expand().Border();
751 globalSizer
->Add(new wxStaticText(this, wxID_ANY
, "Enter the command: "),
753 globalSizer
->Add(m_executable
, flagsExpand
);
755 m_useCWD
= new wxCheckBox(this, wxID_ANY
, "Working directory: ");
756 globalSizer
->Add(m_useCWD
, flagsExpand
);
757 globalSizer
->Add(m_cwdtext
, flagsExpand
);
759 m_useEnv
= new wxCheckBox(this, wxID_ANY
, "Environment: ");
760 globalSizer
->Add(m_useEnv
, flagsExpand
);
761 globalSizer
->Add(m_envtext
, wxSizerFlags(flagsExpand
).Proportion(1));
763 globalSizer
->Add(CreateStdDialogButtonSizer(wxOK
|wxCANCEL
), flagsExpand
);
764 SetSizerAndFit(globalSizer
);
767 m_executable
->SetValue(cmd
);
768 m_cwdtext
->SetValue(wxGetCwd());
769 wxEnvVariableHashMap env
;
770 if ( wxGetEnvMap(&env
) )
772 for ( wxEnvVariableHashMap::iterator it
= env
.begin();
776 m_envtext
->AppendText(it
->first
+ '=' + it
->second
+ '\n');
779 m_useCWD
->SetValue(false);
780 m_useEnv
->SetValue(false);
783 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap
& env
)
786 if ( m_useEnv
->GetValue() )
791 const int nb
= m_envtext
->GetNumberOfLines();
792 for ( int l
= 0; l
< nb
; l
++ )
794 const wxString line
= m_envtext
->GetLineText(l
).Trim();
798 name
= line
.BeforeFirst('=', &value
);
801 wxLogWarning("Skipping invalid environment line \"%s\".", line
);
811 static bool QueryExec(wxString
& cmd
, wxExecuteEnv
& env
)
813 ExecQueryDialog
dialog(cmd
);
815 if ( dialog
.ShowModal() != wxID_OK
)
818 cmd
= dialog
.GetExecutable();
819 env
.cwd
= dialog
.GetWorkDir();
820 dialog
.GetEnvironment(env
.env
);
825 // ----------------------------------------------------------------------------
826 // event handlers: exec menu
827 // ----------------------------------------------------------------------------
829 int MyFrame::GetExecFlags() const
831 wxMenuBar
* const mbar
= GetMenuBar();
835 if ( mbar
->IsChecked(Exec_Flags_HideConsole
) )
836 flags
|= wxEXEC_HIDE_CONSOLE
;
837 if ( mbar
->IsChecked(Exec_Flags_ShowConsole
) )
838 flags
|= wxEXEC_SHOW_CONSOLE
;
839 if ( mbar
->IsChecked(Exec_Flags_NoEvents
) )
840 flags
|= wxEXEC_NOEVENTS
;
845 void MyFrame::DoAsyncExec(const wxString
& cmd
)
847 MyProcess
* const process
= new MyProcess(this, cmd
);
848 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
| GetExecFlags(), process
);
851 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
857 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
861 // the parent frame keeps track of all async processes as it needs to
862 // free them if we exit before the child process terminates
863 AddAsyncProcess(process
);
867 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
871 if ( !QueryExec(cmd
, env
) )
874 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
876 int code
= wxExecute(cmd
, wxEXEC_SYNC
| GetExecFlags(), NULL
, &env
);
878 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
884 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
886 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
896 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
898 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
905 int code
= wxShell(cmd
);
906 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
911 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
916 m_cmdLast
= "type Makefile.in";
918 m_cmdLast
= "cat -n Makefile";
922 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
930 switch ( wxMessageBox(wxT("Execute it synchronously?"),
931 wxT("Exec question"),
932 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
948 wxLogStatus("\"%s\" is running please wait...", cmd
);
952 wxArrayString output
, errors
;
953 int code
= wxExecute(cmd
, output
, errors
);
955 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
956 cmd
, sw
.Time(), code
);
960 ShowOutput(cmd
, output
, wxT("Output"));
961 ShowOutput(cmd
, errors
, wxT("Errors"));
966 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
967 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
969 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
975 AddPipedProcess(process
);
982 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
985 m_cmdLast
= wxT("tr [a-z] [A-Z]");
987 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
994 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
999 // always execute the filter asynchronously
1000 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
1001 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
1004 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
1006 AddPipedProcess(process
);
1010 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
1018 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
1020 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
1026 wxProcess
*process
= wxProcess::Open(cmd
);
1029 wxLogError(wxT("Failed to launch the command."));
1033 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
1035 wxOutputStream
*out
= process
->GetOutputStream();
1038 wxLogError(wxT("Failed to connect to child stdin"));
1042 wxInputStream
*in
= process
->GetInputStream();
1045 wxLogError(wxT("Failed to connect to child stdout"));
1049 new MyPipeFrame(this, cmd
, process
);
1052 static wxString gs_lastFile
;
1054 static bool AskUserForFileName()
1059 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
1060 #else // !wxUSE_FILEDLG
1061 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1063 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1065 if ( filename
.empty() )
1068 gs_lastFile
= filename
;
1073 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
1075 if ( !AskUserForFileName() )
1078 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
1079 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
1082 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1088 bool ok
= ft
->GetOpenCommand(&cmd
,
1089 wxFileType::MessageParameters(gs_lastFile
));
1093 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1101 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
1103 if ( !AskUserForFileName() )
1106 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
1108 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
1112 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
1114 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
1116 wxString filename
= wxGetTextFromUser
1118 wxT("Enter the URL"),
1124 if ( filename
.empty() )
1129 if ( !wxLaunchDefaultBrowser(s_url
) )
1131 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
1135 // ----------------------------------------------------------------------------
1137 // ----------------------------------------------------------------------------
1141 bool MyFrame::GetDDEServer()
1143 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
1144 DIALOG_TITLE
, m_server
);
1150 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1156 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1165 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1167 if ( !GetDDEServer() )
1171 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1174 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1179 if ( !conn
->Execute(m_cmdDde
) )
1181 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1186 wxLogStatus(wxT("Successfully executed DDE command"));
1191 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1193 if ( !GetDDEServer() )
1197 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1200 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1205 if ( !conn
->Request(m_cmdDde
) )
1207 wxLogError(wxT("Failed to send request '%s' via DDE."),
1212 wxLogStatus(wxT("Successfully sent DDE request."));
1217 #endif // __WINDOWS__
1219 // ----------------------------------------------------------------------------
1221 // ----------------------------------------------------------------------------
1224 void MyFrame::OnIdle(wxIdleEvent
& event
)
1226 size_t count
= m_running
.GetCount();
1227 for ( size_t n
= 0; n
< count
; n
++ )
1229 if ( m_running
[n
]->HasInput() )
1231 event
.RequestMore();
1236 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1241 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1243 static unsigned long s_ticks
= 0;
1244 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1247 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1249 RemovePipedProcess(process
);
1252 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1254 m_allAsync
.Remove(process
);
1259 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1261 if ( m_running
.IsEmpty() )
1263 // we want to start getting the timer events to ensure that a
1264 // steady stream of idle events comes in -- otherwise we
1265 // wouldn't be able to poll the child process input
1266 m_timerIdleWakeUp
.Start(100);
1268 //else: the timer is already running
1270 m_running
.Add(process
);
1271 m_allAsync
.Add(process
);
1274 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1276 m_running
.Remove(process
);
1278 if ( m_running
.IsEmpty() )
1280 // we don't need to get idle events all the time any more
1281 m_timerIdleWakeUp
.Stop();
1285 void MyFrame::ShowOutput(const wxString
& cmd
,
1286 const wxArrayString
& output
,
1287 const wxString
& title
)
1289 size_t count
= output
.GetCount();
1293 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1294 title
.c_str(), cmd
.c_str()));
1296 for ( size_t n
= 0; n
< count
; n
++ )
1298 m_lbox
->Append(output
[n
]);
1301 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1302 title
.Lower().c_str()));
1305 // ----------------------------------------------------------------------------
1307 // ----------------------------------------------------------------------------
1309 void MyProcess::OnTerminate(int pid
, int status
)
1311 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1312 pid
, m_cmd
.c_str(), status
);
1314 m_parent
->OnAsyncTermination(this);
1317 // ----------------------------------------------------------------------------
1319 // ----------------------------------------------------------------------------
1321 bool MyPipedProcess::HasInput()
1323 bool hasInput
= false;
1325 if ( IsInputAvailable() )
1327 wxTextInputStream
tis(*GetInputStream());
1329 // this assumes that the output is always line buffered
1331 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1333 m_parent
->GetLogListBox()->Append(msg
);
1338 if ( IsErrorAvailable() )
1340 wxTextInputStream
tis(*GetErrorStream());
1342 // this assumes that the output is always line buffered
1344 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1346 m_parent
->GetLogListBox()->Append(msg
);
1354 void MyPipedProcess::OnTerminate(int pid
, int status
)
1356 // show the rest of the output
1357 while ( HasInput() )
1360 m_parent
->OnProcessTerminated(this);
1362 MyProcess::OnTerminate(pid
, status
);
1365 // ----------------------------------------------------------------------------
1367 // ----------------------------------------------------------------------------
1369 bool MyPipedProcess2::HasInput()
1371 if ( !m_input
.empty() )
1373 wxTextOutputStream
os(*GetOutputStream());
1374 os
.WriteString(m_input
);
1379 // call us once again - may be we'll have output
1383 return MyPipedProcess::HasInput();
1386 // ============================================================================
1387 // MyPipeFrame implementation
1388 // ============================================================================
1390 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1391 const wxString
& cmd
,
1393 : wxFrame(parent
, wxID_ANY
, cmd
),
1395 // in a real program we'd check that the streams are !NULL here
1396 m_out(*process
->GetOutputStream()),
1397 m_in(*process
->GetInputStream()),
1398 m_err(*process
->GetErrorStream())
1400 m_process
->SetNextHandler(this);
1402 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1404 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1405 wxDefaultPosition
, wxDefaultSize
,
1406 wxTE_PROCESS_ENTER
);
1407 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1408 wxDefaultPosition
, wxDefaultSize
,
1409 wxTE_MULTILINE
| wxTE_RICH
);
1410 m_textIn
->SetEditable(false);
1411 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1412 wxDefaultPosition
, wxDefaultSize
,
1413 wxTE_MULTILINE
| wxTE_RICH
);
1414 m_textErr
->SetEditable(false);
1416 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1417 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1419 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1421 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1423 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1425 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1427 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1429 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1430 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1431 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1433 panel
->SetSizer(sizerTop
);
1434 sizerTop
->Fit(this);
1439 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1442 wxFileDialog
filedlg(this, wxT("Select file to send"));
1443 if ( filedlg
.ShowModal() != wxID_OK
)
1446 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1448 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1451 // can't write the entire string at once, this risk overflowing the pipe
1452 // and we would dead lock
1453 size_t len
= data
.length();
1454 const wxChar
*pc
= data
.c_str();
1457 const size_t CHUNK_SIZE
= 4096;
1458 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1460 // note that not all data could have been written as we don't block on
1461 // the write end of the pipe
1462 const size_t lenChunk
= m_out
.LastWrite();
1469 #endif // wxUSE_FILEDLG
1472 void MyPipeFrame::DoGet()
1474 // we don't have any way to be notified when any input appears on the
1475 // stream so we have to poll it :-(
1476 DoGetFromStream(m_textIn
, m_in
);
1477 DoGetFromStream(m_textErr
, m_err
);
1480 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1482 while ( in
.CanRead() )
1485 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = '\0';
1487 text
->AppendText(buffer
);
1491 void MyPipeFrame::DoClose()
1493 m_process
->CloseOutput();
1498 void MyPipeFrame::DisableInput()
1500 m_textOut
->SetEditable(false);
1501 FindWindow(Exec_Btn_Send
)->Disable();
1502 FindWindow(Exec_Btn_SendFile
)->Disable();
1503 FindWindow(Exec_Btn_Close
)->Disable();
1506 void MyPipeFrame::DisableOutput()
1508 FindWindow(Exec_Btn_Get
)->Disable();
1511 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1515 // we're not interested in getting the process termination notification
1516 // if we are closing it ourselves
1517 wxProcess
*process
= m_process
;
1519 process
->SetNextHandler(NULL
);
1521 process
->CloseOutput();
1527 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1531 wxDELETE(m_process
);
1533 wxLogWarning(wxT("The other process has terminated, closing"));