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/textctrl.h"
47 #include "wx/listbox.h"
52 #include "wx/txtstrm.h"
53 #include "wx/numdlg.h"
54 #include "wx/textdlg.h"
57 #include "wx/process.h"
59 #include "wx/mimetype.h"
65 // ----------------------------------------------------------------------------
66 // the usual application and main frame classes
67 // ----------------------------------------------------------------------------
69 // Define a new application type, each program should derive a class from wxApp
70 class MyApp
: public wxApp
73 // override base class virtuals
74 // ----------------------------
76 // this one is called on application startup and is a good place for the app
77 // initialization (doing it here and not in the ctor allows to have an error
78 // return: if OnInit() returns false, the application terminates)
79 virtual bool OnInit();
82 // Define an array of process pointers used by MyFrame
84 WX_DEFINE_ARRAY_PTR(MyPipedProcess
*, MyPipedProcessesArray
);
87 WX_DEFINE_ARRAY_PTR(MyProcess
*, MyProcessesArray
);
89 // Define a new frame type: this is going to be our main frame
90 class MyFrame
: public wxFrame
94 MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
);
97 // event handlers (these functions should _not_ be virtual)
98 void OnQuit(wxCommandEvent
& event
);
100 void OnKill(wxCommandEvent
& event
);
102 void OnClear(wxCommandEvent
& event
);
104 void OnBeginBusyCursor(wxCommandEvent
& event
);
105 void OnEndBusyCursor(wxCommandEvent
& event
);
107 void OnSyncExec(wxCommandEvent
& event
);
108 void OnSyncNoEventsExec(wxCommandEvent
& event
);
109 void OnAsyncExec(wxCommandEvent
& event
);
110 void OnShell(wxCommandEvent
& event
);
111 void OnExecWithRedirect(wxCommandEvent
& event
);
112 void OnExecWithPipe(wxCommandEvent
& event
);
114 void OnPOpen(wxCommandEvent
& event
);
116 void OnFileExec(wxCommandEvent
& event
);
117 void OnOpenURL(wxCommandEvent
& event
);
119 void OnAbout(wxCommandEvent
& event
);
121 // polling output of async processes
122 void OnTimer(wxTimerEvent
& event
);
123 void OnIdle(wxIdleEvent
& event
);
125 // for MyPipedProcess
126 void OnProcessTerminated(MyPipedProcess
*process
);
127 wxListBox
*GetLogListBox() const { return m_lbox
; }
130 void OnAsyncTermination(MyProcess
*process
);
133 void ShowOutput(const wxString
& cmd
,
134 const wxArrayString
& output
,
135 const wxString
& title
);
137 void DoAsyncExec(const wxString
& cmd
);
139 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
141 void AddPipedProcess(MyPipedProcess
*process
);
142 void RemovePipedProcess(MyPipedProcess
*process
);
145 // the PID of the last process we launched asynchronously
148 // last command we executed
152 void OnDDEExec(wxCommandEvent
& event
);
153 void OnDDERequest(wxCommandEvent
& event
);
157 // last params of a DDE transaction
161 #endif // __WINDOWS__
165 // array of running processes with redirected IO
166 MyPipedProcessesArray m_running
;
168 // array of all asynchrously running processes
169 MyProcessesArray m_allAsync
;
171 // the idle event wake up timer
172 wxTimer m_timerIdleWakeUp
;
174 // any class wishing to process wxWidgets events must use this macro
175 DECLARE_EVENT_TABLE()
178 // ----------------------------------------------------------------------------
179 // MyPipeFrame: allows the user to communicate with the child process
180 // ----------------------------------------------------------------------------
182 class MyPipeFrame
: public wxFrame
185 MyPipeFrame(wxFrame
*parent
,
190 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
191 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
192 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
193 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
194 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
196 void OnClose(wxCloseEvent
& event
);
198 void OnProcessTerm(wxProcessEvent
& event
);
202 wxString
s(m_textOut
->GetValue());
204 m_out
.Write(s
.c_str(), s
.length());
214 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
216 void DisableOutput();
219 wxProcess
*m_process
;
221 wxOutputStream
&m_out
;
225 wxTextCtrl
*m_textOut
,
229 DECLARE_EVENT_TABLE()
232 // ----------------------------------------------------------------------------
233 // wxProcess-derived classes
234 // ----------------------------------------------------------------------------
236 // This is the handler for process termination events
237 class MyProcess
: public wxProcess
240 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
241 : wxProcess(parent
), m_cmd(cmd
)
246 // instead of overriding this virtual function we might as well process the
247 // event from it in the frame class - this might be more convenient in some
249 virtual void OnTerminate(int pid
, int status
);
256 // A specialization of MyProcess for redirecting the output
257 class MyPipedProcess
: public MyProcess
260 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
261 : MyProcess(parent
, cmd
)
266 virtual void OnTerminate(int pid
, int status
);
268 virtual bool HasInput();
271 // A version of MyPipedProcess which also sends input to the stdin of the
273 class MyPipedProcess2
: public MyPipedProcess
276 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
277 : MyPipedProcess(parent
, cmd
), m_input(input
)
281 virtual bool HasInput();
287 // ----------------------------------------------------------------------------
289 // ----------------------------------------------------------------------------
291 // IDs for the controls and the menu commands
298 Exec_BeginBusyCursor
,
301 Exec_SyncNoEventsExec
,
314 Exec_Btn_Send
= 1000,
320 static const wxChar
*DIALOG_TITLE
= _T("Exec sample");
322 // ----------------------------------------------------------------------------
323 // event tables and other macros for wxWidgets
324 // ----------------------------------------------------------------------------
326 // the event tables connect the wxWidgets events with the functions (event
327 // handlers) which process them. It can be also done at run-time, but for the
328 // simple menu events like this the static method is much simpler.
329 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
330 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
331 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
332 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
333 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
334 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
336 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
337 EVT_MENU(Exec_SyncNoEventsExec
, MyFrame::OnSyncNoEventsExec
)
338 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
339 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
340 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
341 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
343 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
345 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
346 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
349 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
350 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
351 #endif // __WINDOWS__
353 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
355 EVT_IDLE(MyFrame::OnIdle
)
357 EVT_TIMER(wxID_ANY
, MyFrame::OnTimer
)
360 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
361 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
362 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
363 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
364 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
366 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
368 EVT_CLOSE(MyPipeFrame::OnClose
)
370 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
373 // Create a new application object: this macro will allow wxWidgets to create
374 // the application object during program execution (it's better than using a
375 // static object for many reasons) and also declares the accessor function
376 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
380 // ============================================================================
382 // ============================================================================
384 // ----------------------------------------------------------------------------
385 // the application class
386 // ----------------------------------------------------------------------------
388 // `Main program' equivalent: the program execution "starts" here
391 if ( !wxApp::OnInit() )
394 // Create the main application window
395 MyFrame
*frame
= new MyFrame(_T("Exec wxWidgets sample"),
396 wxDefaultPosition
, wxSize(500, 140));
398 // Show it and tell the application that it's our main window
402 // success: wxApp::OnRun() will be called which will enter the main message
403 // loop and the application will run. If we returned false here, the
404 // application would exit immediately.
408 // ----------------------------------------------------------------------------
410 // ----------------------------------------------------------------------------
413 #pragma warning(disable: 4355) // this used in base member initializer list
417 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
418 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
419 m_timerIdleWakeUp(this)
424 // we need this in order to allow the about menu relocation, since ABOUT is
425 // not the default id of the about menu
426 wxApp::s_macAboutMenuItemId
= Exec_About
;
430 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
431 menuFile
->Append(Exec_Kill
, _T("&Kill process...\tCtrl-K"),
432 _T("Kill a process by PID"));
433 menuFile
->AppendSeparator();
434 menuFile
->Append(Exec_ClearLog
, _T("&Clear log\tCtrl-L"),
435 _T("Clear the log window"));
436 menuFile
->AppendSeparator();
437 menuFile
->Append(Exec_BeginBusyCursor
, _T("Show &busy cursor\tCtrl-C"));
438 menuFile
->Append(Exec_EndBusyCursor
, _T("Show &normal cursor\tShift-Ctrl-C"));
439 menuFile
->AppendSeparator();
440 menuFile
->Append(Exec_Quit
, _T("E&xit\tAlt-X"), _T("Quit this program"));
442 wxMenu
*execMenu
= new wxMenu
;
443 execMenu
->Append(Exec_SyncExec
, _T("Sync &execution...\tCtrl-E"),
444 _T("Launch a program and return when it terminates"));
445 execMenu
->Append(Exec_SyncNoEventsExec
, _T("Sync execution and &block...\tCtrl-B"),
446 _T("Launch a program and block until it terminates"));
447 execMenu
->Append(Exec_AsyncExec
, _T("&Async execution...\tCtrl-A"),
448 _T("Launch a program and return immediately"));
449 execMenu
->Append(Exec_Shell
, _T("Execute &shell command...\tCtrl-S"),
450 _T("Launch a shell and execute a command in it"));
451 execMenu
->AppendSeparator();
452 execMenu
->Append(Exec_Redirect
, _T("Capture command &output...\tCtrl-O"),
453 _T("Launch a program and capture its output"));
454 execMenu
->Append(Exec_Pipe
, _T("&Pipe through command..."),
455 _T("Pipe a string through a filter"));
456 execMenu
->Append(Exec_POpen
, _T("&Open a pipe to a command...\tCtrl-P"),
457 _T("Open a pipe to and from another program"));
459 execMenu
->AppendSeparator();
460 execMenu
->Append(Exec_OpenFile
, _T("Open &file...\tCtrl-F"),
461 _T("Launch the command to open this kind of files"));
462 execMenu
->Append(Exec_OpenURL
, _T("Open &URL...\tCtrl-U"),
463 _T("Launch the default browser with the given URL"));
465 execMenu
->AppendSeparator();
466 execMenu
->Append(Exec_DDEExec
, _T("Execute command via &DDE...\tCtrl-D"));
467 execMenu
->Append(Exec_DDERequest
, _T("Send DDE &request...\tCtrl-R"));
470 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
471 helpMenu
->Append(Exec_About
, _T("&About...\tF1"), _T("Show about dialog"));
473 // now append the freshly created menu to the menu bar...
474 wxMenuBar
*menuBar
= new wxMenuBar();
475 menuBar
->Append(menuFile
, _T("&File"));
476 menuBar
->Append(execMenu
, _T("&Exec"));
477 menuBar
->Append(helpMenu
, _T("&Help"));
479 // ... and attach this menu bar to the frame
482 // create the listbox in which we will show misc messages as they come
483 m_lbox
= new wxListBox(this, wxID_ANY
);
484 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
485 wxFONTWEIGHT_NORMAL
);
487 m_lbox
->SetFont(font
);
490 // create a status bar just for fun (by default with 1 pane only)
492 SetStatusText(_T("Welcome to wxWidgets exec sample!"));
493 #endif // wxUSE_STATUSBAR
498 // any processes left until now must be deleted manually: normally this is
499 // done when the associated process terminates but it must be still running
500 // if this didn't happen until now
501 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
503 delete m_allAsync
[n
];
507 // ----------------------------------------------------------------------------
508 // event handlers: file and help menu
509 // ----------------------------------------------------------------------------
511 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
513 // true is to force the frame to close
517 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
522 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
527 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
532 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
534 wxMessageBox(_T("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
535 _T("About Exec"), wxOK
| wxICON_INFORMATION
, this);
538 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
540 long pid
= wxGetNumberFromUser(_T("Please specify the process to kill"),
544 // we need the full unsigned int range
553 static const wxString signalNames
[] =
555 _T("Just test (SIGNONE)"),
556 _T("Hangup (SIGHUP)"),
557 _T("Interrupt (SIGINT)"),
558 _T("Quit (SIGQUIT)"),
559 _T("Illegal instruction (SIGILL)"),
560 _T("Trap (SIGTRAP)"),
561 _T("Abort (SIGABRT)"),
562 _T("Emulated trap (SIGEMT)"),
563 _T("FP exception (SIGFPE)"),
564 _T("Kill (SIGKILL)"),
566 _T("Segment violation (SIGSEGV)"),
567 _T("System (SIGSYS)"),
568 _T("Broken pipe (SIGPIPE)"),
569 _T("Alarm (SIGALRM)"),
570 _T("Terminate (SIGTERM)"),
573 int sig
= wxGetSingleChoiceIndex(_T("How to kill the process?"),
575 WXSIZEOF(signalNames
), signalNames
,
580 wxFAIL_MSG( _T("unexpected return value") );
608 if ( wxProcess::Exists(pid
) )
609 wxLogStatus(_T("Process %ld is running."), pid
);
611 wxLogStatus(_T("No process with pid = %ld."), pid
);
615 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
616 if ( rc
== wxKILL_OK
)
618 wxLogStatus(_T("Process %ld killed with signal %d."), pid
, sig
);
622 static const wxChar
*errorText
[] =
625 _T("signal not supported"),
626 _T("permission denied"),
627 _T("no such process"),
628 _T("unspecified error"),
631 wxLogStatus(_T("Failed to kill process %ld with signal %d: %s"),
632 pid
, sig
, errorText
[rc
]);
637 // ----------------------------------------------------------------------------
638 // event handlers: exec menu
639 // ----------------------------------------------------------------------------
641 void MyFrame::DoAsyncExec(const wxString
& cmd
)
643 MyProcess
* const process
= new MyProcess(this, cmd
);
644 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
647 wxLogError(_T("Execution of '%s' failed."), cmd
.c_str());
653 wxLogStatus(_T("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
657 // the parent frame keeps track of all async processes as it needs to
658 // free them if we exit before the child process terminates
659 AddAsyncProcess(process
);
663 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
665 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
672 wxLogStatus( _T("'%s' is running please wait..."), cmd
.c_str() );
674 int code
= wxExecute(cmd
, wxEXEC_SYNC
);
676 wxLogStatus(_T("Process '%s' terminated with exit code %d."),
682 void MyFrame::OnSyncNoEventsExec(wxCommandEvent
& WXUNUSED(event
))
684 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
691 wxLogStatus( _T("'%s' is running please wait..."), cmd
.c_str() );
693 int code
= wxExecute(cmd
, wxEXEC_BLOCK
);
695 wxLogStatus(_T("Process '%s' terminated with exit code %d."),
701 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
703 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
713 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
715 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
722 int code
= wxShell(cmd
);
723 wxLogStatus(_T("Shell command '%s' terminated with exit code %d."),
728 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
733 m_cmdLast
= "type Makefile.in";
735 m_cmdLast
= "cat -n Makefile";
739 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
747 switch ( wxMessageBox(_T("Execute it synchronously?"),
749 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
765 wxLogStatus( _T("'%s' is running please wait..."), cmd
.c_str() );
767 wxArrayString output
, errors
;
768 int code
= wxExecute(cmd
, output
, errors
);
770 wxLogStatus(_T("Command '%s' terminated with exit code %d."),
775 ShowOutput(cmd
, output
, _T("Output"));
776 ShowOutput(cmd
, errors
, _T("Errors"));
781 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
782 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
784 wxLogError(_T("Execution of '%s' failed."), cmd
.c_str());
790 AddPipedProcess(process
);
797 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
800 m_cmdLast
= _T("tr [a-z] [A-Z]");
802 wxString cmd
= wxGetTextFromUser(_T("Enter the command: "),
809 wxString input
= wxGetTextFromUser(_T("Enter the string to send to it: "),
814 // always execute the filter asynchronously
815 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
816 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
819 wxLogStatus(_T("Process %ld (%s) launched."), pid
, cmd
.c_str());
821 AddPipedProcess(process
);
825 wxLogError(_T("Execution of '%s' failed."), cmd
.c_str());
833 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
835 wxString cmd
= wxGetTextFromUser(_T("Enter the command to launch: "),
841 wxProcess
*process
= wxProcess::Open(cmd
);
844 wxLogError(_T("Failed to launch the command."));
848 wxLogVerbose(_T("PID of the new process: %ld"), process
->GetPid());
850 wxOutputStream
*out
= process
->GetOutputStream();
853 wxLogError(_T("Failed to connect to child stdin"));
857 wxInputStream
*in
= process
->GetInputStream();
860 wxLogError(_T("Failed to connect to child stdout"));
864 new MyPipeFrame(this, cmd
, process
);
867 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
869 static wxString s_filename
;
874 filename
= wxLoadFileSelector(_T("any file"), wxEmptyString
, s_filename
, this);
875 #else // !wxUSE_FILEDLG
876 filename
= wxGetTextFromUser(_T("Enter the file name"), _T("exec sample"),
878 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
880 if ( filename
.empty() )
883 s_filename
= filename
;
885 wxString ext
= filename
.AfterFirst(_T('.'));
886 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
889 wxLogError(_T("Impossible to determine the file type for extension '%s'"),
895 bool ok
= ft
->GetOpenCommand(&cmd
,
896 wxFileType::MessageParameters(filename
));
900 wxLogError(_T("Impossible to find out how to open files of extension '%s'"),
908 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
910 static wxString
s_filename(_T("http://www.wxwidgets.org/"));
912 wxString filename
= wxGetTextFromUser
920 if ( filename
.empty() )
923 s_filename
= filename
;
925 if ( !wxLaunchDefaultBrowser(s_filename
) )
926 wxLogError(_T("Failed to open URL \"%s\""), s_filename
.c_str());
929 // ----------------------------------------------------------------------------
931 // ----------------------------------------------------------------------------
935 bool MyFrame::GetDDEServer()
937 wxString server
= wxGetTextFromUser(_T("Server to connect to:"),
938 DIALOG_TITLE
, m_server
);
944 wxString topic
= wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE
, m_topic
);
950 wxString cmd
= wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
959 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
961 if ( !GetDDEServer() )
965 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
968 wxLogError(_T("Failed to connect to the DDE server '%s'."),
973 if ( !conn
->Execute(m_cmdDde
) )
975 wxLogError(_T("Failed to execute command '%s' via DDE."),
980 wxLogStatus(_T("Successfully executed DDE command"));
985 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
987 if ( !GetDDEServer() )
991 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
994 wxLogError(_T("Failed to connect to the DDE server '%s'."),
999 if ( !conn
->Request(m_cmdDde
) )
1001 wxLogError(_T("Failed to send request '%s' via DDE."),
1006 wxLogStatus(_T("Successfully sent DDE request."));
1011 #endif // __WINDOWS__
1013 // ----------------------------------------------------------------------------
1015 // ----------------------------------------------------------------------------
1018 void MyFrame::OnIdle(wxIdleEvent
& event
)
1020 size_t count
= m_running
.GetCount();
1021 for ( size_t n
= 0; n
< count
; n
++ )
1023 if ( m_running
[n
]->HasInput() )
1025 event
.RequestMore();
1030 void MyFrame::OnTimer(wxTimerEvent
& WXUNUSED(event
))
1035 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1037 RemovePipedProcess(process
);
1040 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1042 m_allAsync
.Remove(process
);
1047 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1049 if ( m_running
.IsEmpty() )
1051 // we want to start getting the timer events to ensure that a
1052 // steady stream of idle events comes in -- otherwise we
1053 // wouldn't be able to poll the child process input
1054 m_timerIdleWakeUp
.Start(100);
1056 //else: the timer is already running
1058 m_running
.Add(process
);
1059 m_allAsync
.Add(process
);
1062 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1064 m_running
.Remove(process
);
1066 if ( m_running
.IsEmpty() )
1068 // we don't need to get idle events all the time any more
1069 m_timerIdleWakeUp
.Stop();
1073 void MyFrame::ShowOutput(const wxString
& cmd
,
1074 const wxArrayString
& output
,
1075 const wxString
& title
)
1077 size_t count
= output
.GetCount();
1081 m_lbox
->Append(wxString::Format(_T("--- %s of '%s' ---"),
1082 title
.c_str(), cmd
.c_str()));
1084 for ( size_t n
= 0; n
< count
; n
++ )
1086 m_lbox
->Append(output
[n
]);
1089 m_lbox
->Append(wxString::Format(_T("--- End of %s ---"),
1090 title
.Lower().c_str()));
1093 // ----------------------------------------------------------------------------
1095 // ----------------------------------------------------------------------------
1097 void MyProcess::OnTerminate(int pid
, int status
)
1099 wxLogStatus(m_parent
, _T("Process %u ('%s') terminated with exit code %d."),
1100 pid
, m_cmd
.c_str(), status
);
1102 m_parent
->OnAsyncTermination(this);
1105 // ----------------------------------------------------------------------------
1107 // ----------------------------------------------------------------------------
1109 bool MyPipedProcess::HasInput()
1111 bool hasInput
= false;
1113 if ( IsInputAvailable() )
1115 wxTextInputStream
tis(*GetInputStream());
1117 // this assumes that the output is always line buffered
1119 msg
<< m_cmd
<< _T(" (stdout): ") << tis
.ReadLine();
1121 m_parent
->GetLogListBox()->Append(msg
);
1126 if ( IsErrorAvailable() )
1128 wxTextInputStream
tis(*GetErrorStream());
1130 // this assumes that the output is always line buffered
1132 msg
<< m_cmd
<< _T(" (stderr): ") << tis
.ReadLine();
1134 m_parent
->GetLogListBox()->Append(msg
);
1142 void MyPipedProcess::OnTerminate(int pid
, int status
)
1144 // show the rest of the output
1145 while ( HasInput() )
1148 m_parent
->OnProcessTerminated(this);
1150 MyProcess::OnTerminate(pid
, status
);
1153 // ----------------------------------------------------------------------------
1155 // ----------------------------------------------------------------------------
1157 bool MyPipedProcess2::HasInput()
1159 if ( !m_input
.empty() )
1161 wxTextOutputStream
os(*GetOutputStream());
1162 os
.WriteString(m_input
);
1167 // call us once again - may be we'll have output
1171 return MyPipedProcess::HasInput();
1174 // ============================================================================
1175 // MyPipeFrame implementation
1176 // ============================================================================
1178 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1179 const wxString
& cmd
,
1181 : wxFrame(parent
, wxID_ANY
, cmd
),
1183 // in a real program we'd check that the streams are !NULL here
1184 m_out(*process
->GetOutputStream()),
1185 m_in(*process
->GetInputStream()),
1186 m_err(*process
->GetErrorStream())
1188 m_process
->SetNextHandler(this);
1190 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1192 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1193 wxDefaultPosition
, wxDefaultSize
,
1194 wxTE_PROCESS_ENTER
);
1195 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1196 wxDefaultPosition
, wxDefaultSize
,
1197 wxTE_MULTILINE
| wxTE_RICH
);
1198 m_textIn
->SetEditable(false);
1199 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1200 wxDefaultPosition
, wxDefaultSize
,
1201 wxTE_MULTILINE
| wxTE_RICH
);
1202 m_textErr
->SetEditable(false);
1204 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1205 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1207 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1209 Add(new wxButton(panel
, Exec_Btn_Send
, _T("&Send")), 0, wxALL
, 5);
1211 Add(new wxButton(panel
, Exec_Btn_SendFile
, _T("&File...")), 0, wxALL
, 5);
1213 Add(new wxButton(panel
, Exec_Btn_Get
, _T("&Get")), 0, wxALL
, 5);
1215 Add(new wxButton(panel
, Exec_Btn_Close
, _T("&Close")), 0, wxALL
, 5);
1217 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1218 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1219 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1221 panel
->SetSizer(sizerTop
);
1222 sizerTop
->Fit(this);
1227 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1230 wxFileDialog
filedlg(this, _T("Select file to send"));
1231 if ( filedlg
.ShowModal() != wxID_OK
)
1234 wxFFile
file(filedlg
.GetFilename(), _T("r"));
1236 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1239 // can't write the entire string at once, this risk overflowing the pipe
1240 // and we would dead lock
1241 size_t len
= data
.length();
1242 const wxChar
*pc
= data
.c_str();
1245 const size_t CHUNK_SIZE
= 4096;
1246 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1248 // note that not all data could have been written as we don't block on
1249 // the write end of the pipe
1250 const size_t lenChunk
= m_out
.LastWrite();
1257 #endif // wxUSE_FILEDLG
1260 void MyPipeFrame::DoGet()
1262 // we don't have any way to be notified when any input appears on the
1263 // stream so we have to poll it :-(
1264 DoGetFromStream(m_textIn
, m_in
);
1265 DoGetFromStream(m_textErr
, m_err
);
1268 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1270 while ( in
.CanRead() )
1272 wxChar buffer
[4096];
1273 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = _T('\0');
1275 text
->AppendText(buffer
);
1279 void MyPipeFrame::DoClose()
1281 m_process
->CloseOutput();
1286 void MyPipeFrame::DisableInput()
1288 m_textOut
->SetEditable(false);
1289 FindWindow(Exec_Btn_Send
)->Disable();
1290 FindWindow(Exec_Btn_SendFile
)->Disable();
1291 FindWindow(Exec_Btn_Close
)->Disable();
1294 void MyPipeFrame::DisableOutput()
1296 FindWindow(Exec_Btn_Get
)->Disable();
1299 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1303 // we're not interested in getting the process termination notification
1304 // if we are closing it ourselves
1305 wxProcess
*process
= m_process
;
1307 process
->SetNextHandler(NULL
);
1309 process
->CloseOutput();
1315 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1322 wxLogWarning(_T("The other process has terminated, closing"));