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"
56 #include "wx/stopwatch.h"
58 #include "wx/process.h"
60 #include "wx/mimetype.h"
66 // ----------------------------------------------------------------------------
67 // the usual application and main frame classes
68 // ----------------------------------------------------------------------------
70 // Define a new application type, each program should derive a class from wxApp
71 class MyApp
: public wxApp
74 // override base class virtuals
75 // ----------------------------
77 // this one is called on application startup and is a good place for the app
78 // initialization (doing it here and not in the ctor allows to have an error
79 // return: if OnInit() returns false, the application terminates)
80 virtual bool OnInit();
83 // Define an array of process pointers used by MyFrame
85 WX_DEFINE_ARRAY_PTR(MyPipedProcess
*, MyPipedProcessesArray
);
88 WX_DEFINE_ARRAY_PTR(MyProcess
*, MyProcessesArray
);
90 // Define a new frame type: this is going to be our main frame
91 class MyFrame
: public wxFrame
95 MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
);
98 // event handlers (these functions should _not_ be virtual)
99 void OnQuit(wxCommandEvent
& event
);
101 void OnKill(wxCommandEvent
& event
);
103 void OnClear(wxCommandEvent
& event
);
105 void OnBeginBusyCursor(wxCommandEvent
& event
);
106 void OnEndBusyCursor(wxCommandEvent
& event
);
108 void OnSyncExec(wxCommandEvent
& event
);
109 void OnSyncNoEventsExec(wxCommandEvent
& event
);
110 void OnAsyncExec(wxCommandEvent
& event
);
111 void OnShell(wxCommandEvent
& event
);
112 void OnExecWithRedirect(wxCommandEvent
& event
);
113 void OnExecWithPipe(wxCommandEvent
& event
);
115 void OnPOpen(wxCommandEvent
& event
);
117 void OnFileExec(wxCommandEvent
& event
);
118 void OnFileLaunch(wxCommandEvent
& event
);
119 void OnOpenURL(wxCommandEvent
& event
);
121 void OnAbout(wxCommandEvent
& event
);
123 // polling output of async processes
124 void OnIdleTimer(wxTimerEvent
& event
);
125 void OnIdle(wxIdleEvent
& event
);
127 // for MyPipedProcess
128 void OnProcessTerminated(MyPipedProcess
*process
);
129 wxListBox
*GetLogListBox() const { return m_lbox
; }
132 void OnAsyncTermination(MyProcess
*process
);
134 // timer updating a counter in the background
135 void OnBgTimer(wxTimerEvent
& event
);
138 void ShowOutput(const wxString
& cmd
,
139 const wxArrayString
& output
,
140 const wxString
& title
);
142 void DoAsyncExec(const wxString
& cmd
);
144 void AddAsyncProcess(MyProcess
*process
) { m_allAsync
.push_back(process
); }
146 void AddPipedProcess(MyPipedProcess
*process
);
147 void RemovePipedProcess(MyPipedProcess
*process
);
150 // the PID of the last process we launched asynchronously
153 // last command we executed
157 void OnDDEExec(wxCommandEvent
& event
);
158 void OnDDERequest(wxCommandEvent
& event
);
162 // last params of a DDE transaction
166 #endif // __WINDOWS__
170 // array of running processes with redirected IO
171 MyPipedProcessesArray m_running
;
173 // array of all asynchrously running processes
174 MyProcessesArray m_allAsync
;
176 // the idle event wake up timer
177 wxTimer m_timerIdleWakeUp
;
179 // a background timer allowing to easily check visually whether the
180 // messages are processed or not
183 // any class wishing to process wxWidgets events must use this macro
184 DECLARE_EVENT_TABLE()
187 // ----------------------------------------------------------------------------
188 // MyPipeFrame: allows the user to communicate with the child process
189 // ----------------------------------------------------------------------------
191 class MyPipeFrame
: public wxFrame
194 MyPipeFrame(wxFrame
*parent
,
199 void OnTextEnter(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
200 void OnBtnSend(wxCommandEvent
& WXUNUSED(event
)) { DoSend(); }
201 void OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
));
202 void OnBtnGet(wxCommandEvent
& WXUNUSED(event
)) { DoGet(); }
203 void OnBtnClose(wxCommandEvent
& WXUNUSED(event
)) { DoClose(); }
205 void OnClose(wxCloseEvent
& event
);
207 void OnProcessTerm(wxProcessEvent
& event
);
211 wxString
s(m_textOut
->GetValue());
213 m_out
.Write(s
.c_str(), s
.length());
223 void DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
);
225 void DisableOutput();
228 wxProcess
*m_process
;
230 wxOutputStream
&m_out
;
234 wxTextCtrl
*m_textOut
,
238 DECLARE_EVENT_TABLE()
241 // ----------------------------------------------------------------------------
242 // wxProcess-derived classes
243 // ----------------------------------------------------------------------------
245 // This is the handler for process termination events
246 class MyProcess
: public wxProcess
249 MyProcess(MyFrame
*parent
, const wxString
& cmd
)
250 : wxProcess(parent
), m_cmd(cmd
)
255 // instead of overriding this virtual function we might as well process the
256 // event from it in the frame class - this might be more convenient in some
258 virtual void OnTerminate(int pid
, int status
);
265 // A specialization of MyProcess for redirecting the output
266 class MyPipedProcess
: public MyProcess
269 MyPipedProcess(MyFrame
*parent
, const wxString
& cmd
)
270 : MyProcess(parent
, cmd
)
275 virtual void OnTerminate(int pid
, int status
);
277 virtual bool HasInput();
280 // A version of MyPipedProcess which also sends input to the stdin of the
282 class MyPipedProcess2
: public MyPipedProcess
285 MyPipedProcess2(MyFrame
*parent
, const wxString
& cmd
, const wxString
& input
)
286 : MyPipedProcess(parent
, cmd
), m_input(input
)
290 virtual bool HasInput();
296 // ----------------------------------------------------------------------------
298 // ----------------------------------------------------------------------------
300 // IDs for the controls and the menu commands
311 Exec_BeginBusyCursor
,
314 Exec_SyncNoEventsExec
,
325 Exec_About
= wxID_ABOUT
,
328 Exec_Btn_Send
= 1000,
334 static const wxChar
*DIALOG_TITLE
= wxT("Exec sample");
336 // ----------------------------------------------------------------------------
337 // event tables and other macros for wxWidgets
338 // ----------------------------------------------------------------------------
340 // the event tables connect the wxWidgets events with the functions (event
341 // handlers) which process them. It can be also done at run-time, but for the
342 // simple menu events like this the static method is much simpler.
343 BEGIN_EVENT_TABLE(MyFrame
, wxFrame
)
344 EVT_MENU(Exec_Quit
, MyFrame::OnQuit
)
345 EVT_MENU(Exec_Kill
, MyFrame::OnKill
)
346 EVT_MENU(Exec_ClearLog
, MyFrame::OnClear
)
347 EVT_MENU(Exec_BeginBusyCursor
, MyFrame::OnBeginBusyCursor
)
348 EVT_MENU(Exec_EndBusyCursor
, MyFrame::OnEndBusyCursor
)
350 EVT_MENU(Exec_SyncExec
, MyFrame::OnSyncExec
)
351 EVT_MENU(Exec_SyncNoEventsExec
, MyFrame::OnSyncNoEventsExec
)
352 EVT_MENU(Exec_AsyncExec
, MyFrame::OnAsyncExec
)
353 EVT_MENU(Exec_Shell
, MyFrame::OnShell
)
354 EVT_MENU(Exec_Redirect
, MyFrame::OnExecWithRedirect
)
355 EVT_MENU(Exec_Pipe
, MyFrame::OnExecWithPipe
)
357 EVT_MENU(Exec_POpen
, MyFrame::OnPOpen
)
359 EVT_MENU(Exec_OpenFile
, MyFrame::OnFileExec
)
360 EVT_MENU(Exec_LaunchFile
, MyFrame::OnFileLaunch
)
361 EVT_MENU(Exec_OpenURL
, MyFrame::OnOpenURL
)
364 EVT_MENU(Exec_DDEExec
, MyFrame::OnDDEExec
)
365 EVT_MENU(Exec_DDERequest
, MyFrame::OnDDERequest
)
366 #endif // __WINDOWS__
368 EVT_MENU(Exec_About
, MyFrame::OnAbout
)
370 EVT_IDLE(MyFrame::OnIdle
)
372 EVT_TIMER(Exec_TimerIdle
, MyFrame::OnIdleTimer
)
373 EVT_TIMER(Exec_TimerBg
, MyFrame::OnBgTimer
)
376 BEGIN_EVENT_TABLE(MyPipeFrame
, wxFrame
)
377 EVT_BUTTON(Exec_Btn_Send
, MyPipeFrame::OnBtnSend
)
378 EVT_BUTTON(Exec_Btn_SendFile
, MyPipeFrame::OnBtnSendFile
)
379 EVT_BUTTON(Exec_Btn_Get
, MyPipeFrame::OnBtnGet
)
380 EVT_BUTTON(Exec_Btn_Close
, MyPipeFrame::OnBtnClose
)
382 EVT_TEXT_ENTER(wxID_ANY
, MyPipeFrame::OnTextEnter
)
384 EVT_CLOSE(MyPipeFrame::OnClose
)
386 EVT_END_PROCESS(wxID_ANY
, MyPipeFrame::OnProcessTerm
)
389 // Create a new application object: this macro will allow wxWidgets to create
390 // the application object during program execution (it's better than using a
391 // static object for many reasons) and also declares the accessor function
392 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
396 // ============================================================================
398 // ============================================================================
400 // ----------------------------------------------------------------------------
401 // the application class
402 // ----------------------------------------------------------------------------
404 // `Main program' equivalent: the program execution "starts" here
407 if ( !wxApp::OnInit() )
410 // Create the main application window
411 MyFrame
*frame
= new MyFrame(wxT("Exec wxWidgets sample"),
412 wxDefaultPosition
, wxSize(500, 140));
414 // Show it and tell the application that it's our main window
418 // success: wxApp::OnRun() will be called which will enter the main message
419 // loop and the application will run. If we returned false here, the
420 // application would exit immediately.
424 // ----------------------------------------------------------------------------
426 // ----------------------------------------------------------------------------
429 #pragma warning(disable: 4355) // this used in base member initializer list
433 MyFrame::MyFrame(const wxString
& title
, const wxPoint
& pos
, const wxSize
& size
)
434 : wxFrame((wxFrame
*)NULL
, wxID_ANY
, title
, pos
, size
),
435 m_timerIdleWakeUp(this, Exec_TimerIdle
),
436 m_timerBg(this, Exec_TimerBg
)
441 // we need this in order to allow the about menu relocation, since ABOUT is
442 // not the default id of the about menu
443 wxApp::s_macAboutMenuItemId
= Exec_About
;
447 wxMenu
*menuFile
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
448 menuFile
->Append(Exec_Kill
, wxT("&Kill process...\tCtrl-K"),
449 wxT("Kill a process by PID"));
450 menuFile
->AppendSeparator();
451 menuFile
->Append(Exec_ClearLog
, wxT("&Clear log\tCtrl-L"),
452 wxT("Clear the log window"));
453 menuFile
->AppendSeparator();
454 menuFile
->Append(Exec_BeginBusyCursor
, wxT("Show &busy cursor\tCtrl-C"));
455 menuFile
->Append(Exec_EndBusyCursor
, wxT("Show &normal cursor\tShift-Ctrl-C"));
456 menuFile
->AppendSeparator();
457 menuFile
->Append(Exec_Quit
, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
459 wxMenu
*execMenu
= new wxMenu
;
460 execMenu
->Append(Exec_SyncExec
, wxT("Sync &execution...\tCtrl-E"),
461 wxT("Launch a program and return when it terminates"));
462 execMenu
->Append(Exec_SyncNoEventsExec
, wxT("Sync execution and &block...\tCtrl-B"),
463 wxT("Launch a program and block until it terminates"));
464 execMenu
->Append(Exec_AsyncExec
, wxT("&Async execution...\tCtrl-A"),
465 wxT("Launch a program and return immediately"));
466 execMenu
->Append(Exec_Shell
, wxT("Execute &shell command...\tCtrl-S"),
467 wxT("Launch a shell and execute a command in it"));
468 execMenu
->AppendSeparator();
469 execMenu
->Append(Exec_Redirect
, wxT("Capture command &output...\tCtrl-O"),
470 wxT("Launch a program and capture its output"));
471 execMenu
->Append(Exec_Pipe
, wxT("&Pipe through command..."),
472 wxT("Pipe a string through a filter"));
473 execMenu
->Append(Exec_POpen
, wxT("&Open a pipe to a command...\tCtrl-P"),
474 wxT("Open a pipe to and from another program"));
476 execMenu
->AppendSeparator();
477 execMenu
->Append(Exec_OpenFile
, wxT("Open &file...\tCtrl-F"),
478 wxT("Launch the command to open this kind of files"));
479 execMenu
->Append(Exec_LaunchFile
, wxT("La&unch file...\tShift-Ctrl-F"),
480 wxT("Launch the default application associated with the file"));
481 execMenu
->Append(Exec_OpenURL
, wxT("Open &URL...\tCtrl-U"),
482 wxT("Launch the default browser with the given URL"));
484 execMenu
->AppendSeparator();
485 execMenu
->Append(Exec_DDEExec
, wxT("Execute command via &DDE...\tCtrl-D"));
486 execMenu
->Append(Exec_DDERequest
, wxT("Send DDE &request...\tCtrl-R"));
489 wxMenu
*helpMenu
= new wxMenu(wxEmptyString
, wxMENU_TEAROFF
);
490 helpMenu
->Append(Exec_About
, wxT("&About...\tF1"), wxT("Show about dialog"));
492 // now append the freshly created menu to the menu bar...
493 wxMenuBar
*menuBar
= new wxMenuBar();
494 menuBar
->Append(menuFile
, wxT("&File"));
495 menuBar
->Append(execMenu
, wxT("&Exec"));
496 menuBar
->Append(helpMenu
, wxT("&Help"));
498 // ... and attach this menu bar to the frame
501 // create the listbox in which we will show misc messages as they come
502 m_lbox
= new wxListBox(this, wxID_ANY
);
503 wxFont
font(12, wxFONTFAMILY_TELETYPE
, wxFONTSTYLE_NORMAL
,
504 wxFONTWEIGHT_NORMAL
);
506 m_lbox
->SetFont(font
);
509 // create a status bar just for fun (by default with 1 pane only)
511 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
512 #endif // wxUSE_STATUSBAR
514 m_timerBg
.Start(1000);
519 // any processes left until now must be deleted manually: normally this is
520 // done when the associated process terminates but it must be still running
521 // if this didn't happen until now
522 for ( size_t n
= 0; n
< m_allAsync
.size(); n
++ )
524 delete m_allAsync
[n
];
528 // ----------------------------------------------------------------------------
529 // event handlers: file and help menu
530 // ----------------------------------------------------------------------------
532 void MyFrame::OnQuit(wxCommandEvent
& WXUNUSED(event
))
534 // true is to force the frame to close
538 void MyFrame::OnClear(wxCommandEvent
& WXUNUSED(event
))
543 void MyFrame::OnBeginBusyCursor(wxCommandEvent
& WXUNUSED(event
))
548 void MyFrame::OnEndBusyCursor(wxCommandEvent
& WXUNUSED(event
))
553 void MyFrame::OnAbout(wxCommandEvent
& WXUNUSED(event
))
555 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
556 wxT("About Exec"), wxOK
| wxICON_INFORMATION
, this);
559 void MyFrame::OnKill(wxCommandEvent
& WXUNUSED(event
))
561 long pid
= wxGetNumberFromUser(wxT("Please specify the process to kill"),
563 wxT("Exec question"),
565 // we need the full unsigned int range
576 static const wxString signalNames
[] =
578 wxT("Just test (SIGNONE)"),
579 wxT("Hangup (SIGHUP)"),
580 wxT("Interrupt (SIGINT)"),
581 wxT("Quit (SIGQUIT)"),
582 wxT("Illegal instruction (SIGILL)"),
583 wxT("Trap (SIGTRAP)"),
584 wxT("Abort (SIGABRT)"),
585 wxT("Emulated trap (SIGEMT)"),
586 wxT("FP exception (SIGFPE)"),
587 wxT("Kill (SIGKILL)"),
589 wxT("Segment violation (SIGSEGV)"),
590 wxT("System (SIGSYS)"),
591 wxT("Broken pipe (SIGPIPE)"),
592 wxT("Alarm (SIGALRM)"),
593 wxT("Terminate (SIGTERM)"),
596 static int s_sigLast
= wxSIGNONE
;
597 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
598 wxT("Exec question"),
599 WXSIZEOF(signalNames
), signalNames
,
605 wxFAIL_MSG( wxT("unexpected return value") );
633 if ( sig
== wxSIGNONE
)
635 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
636 if ( wxProcess::Exists(pid
) )
638 wxLogStatus(wxT("Process %ld is running."), pid
);
642 wxLogStatus(wxT("No process with pid = %ld."), pid
);
647 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
648 if ( rc
== wxKILL_OK
)
650 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
654 static const wxChar
*errorText
[] =
657 wxT("signal not supported"),
658 wxT("permission denied"),
659 wxT("no such process"),
660 wxT("unspecified error"),
663 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
664 pid
, sig
, errorText
[rc
]);
669 // ----------------------------------------------------------------------------
670 // event handlers: exec menu
671 // ----------------------------------------------------------------------------
673 void MyFrame::DoAsyncExec(const wxString
& cmd
)
675 MyProcess
* const process
= new MyProcess(this, cmd
);
676 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
679 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
685 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
689 // the parent frame keeps track of all async processes as it needs to
690 // free them if we exit before the child process terminates
691 AddAsyncProcess(process
);
695 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
697 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
704 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
706 int code
= wxExecute(cmd
, wxEXEC_SYNC
);
708 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
714 void MyFrame::OnSyncNoEventsExec(wxCommandEvent
& WXUNUSED(event
))
716 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
723 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
725 int code
= wxExecute(cmd
, wxEXEC_BLOCK
);
727 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
733 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
735 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
745 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
747 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
754 int code
= wxShell(cmd
);
755 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
760 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
765 m_cmdLast
= "type Makefile.in";
767 m_cmdLast
= "cat -n Makefile";
771 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
779 switch ( wxMessageBox(wxT("Execute it synchronously?"),
780 wxT("Exec question"),
781 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
797 wxLogStatus("\"%s\" is running please wait...", cmd
);
801 wxArrayString output
, errors
;
802 int code
= wxExecute(cmd
, output
, errors
);
804 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
805 cmd
, sw
.Time(), code
);
809 ShowOutput(cmd
, output
, wxT("Output"));
810 ShowOutput(cmd
, errors
, wxT("Errors"));
815 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
816 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
818 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
824 AddPipedProcess(process
);
831 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
834 m_cmdLast
= wxT("tr [a-z] [A-Z]");
836 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
843 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
848 // always execute the filter asynchronously
849 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
850 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
853 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
855 AddPipedProcess(process
);
859 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
867 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
869 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
875 wxProcess
*process
= wxProcess::Open(cmd
);
878 wxLogError(wxT("Failed to launch the command."));
882 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
884 wxOutputStream
*out
= process
->GetOutputStream();
887 wxLogError(wxT("Failed to connect to child stdin"));
891 wxInputStream
*in
= process
->GetInputStream();
894 wxLogError(wxT("Failed to connect to child stdout"));
898 new MyPipeFrame(this, cmd
, process
);
901 static wxString gs_lastFile
;
903 static bool AskUserForFileName()
908 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
909 #else // !wxUSE_FILEDLG
910 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
912 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
914 if ( filename
.empty() )
917 gs_lastFile
= filename
;
922 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
924 if ( !AskUserForFileName() )
927 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
928 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
931 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
937 bool ok
= ft
->GetOpenCommand(&cmd
,
938 wxFileType::MessageParameters(gs_lastFile
));
942 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
950 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
952 if ( !AskUserForFileName() )
955 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
957 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
961 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
963 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
965 wxString filename
= wxGetTextFromUser
967 wxT("Enter the URL"),
973 if ( filename
.empty() )
978 if ( !wxLaunchDefaultBrowser(s_url
) )
980 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
984 // ----------------------------------------------------------------------------
986 // ----------------------------------------------------------------------------
990 bool MyFrame::GetDDEServer()
992 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
993 DIALOG_TITLE
, m_server
);
999 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
1005 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1014 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1016 if ( !GetDDEServer() )
1020 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1023 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1028 if ( !conn
->Execute(m_cmdDde
) )
1030 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1035 wxLogStatus(wxT("Successfully executed DDE command"));
1040 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1042 if ( !GetDDEServer() )
1046 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1049 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1054 if ( !conn
->Request(m_cmdDde
) )
1056 wxLogError(wxT("Failed to send request '%s' via DDE."),
1061 wxLogStatus(wxT("Successfully sent DDE request."));
1066 #endif // __WINDOWS__
1068 // ----------------------------------------------------------------------------
1070 // ----------------------------------------------------------------------------
1073 void MyFrame::OnIdle(wxIdleEvent
& event
)
1075 size_t count
= m_running
.GetCount();
1076 for ( size_t n
= 0; n
< count
; n
++ )
1078 if ( m_running
[n
]->HasInput() )
1080 event
.RequestMore();
1085 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1090 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1092 static unsigned long s_ticks
= 0;
1093 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1096 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1098 RemovePipedProcess(process
);
1101 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1103 m_allAsync
.Remove(process
);
1108 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1110 if ( m_running
.IsEmpty() )
1112 // we want to start getting the timer events to ensure that a
1113 // steady stream of idle events comes in -- otherwise we
1114 // wouldn't be able to poll the child process input
1115 m_timerIdleWakeUp
.Start(100);
1117 //else: the timer is already running
1119 m_running
.Add(process
);
1120 m_allAsync
.Add(process
);
1123 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1125 m_running
.Remove(process
);
1127 if ( m_running
.IsEmpty() )
1129 // we don't need to get idle events all the time any more
1130 m_timerIdleWakeUp
.Stop();
1134 void MyFrame::ShowOutput(const wxString
& cmd
,
1135 const wxArrayString
& output
,
1136 const wxString
& title
)
1138 size_t count
= output
.GetCount();
1142 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1143 title
.c_str(), cmd
.c_str()));
1145 for ( size_t n
= 0; n
< count
; n
++ )
1147 m_lbox
->Append(output
[n
]);
1150 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1151 title
.Lower().c_str()));
1154 // ----------------------------------------------------------------------------
1156 // ----------------------------------------------------------------------------
1158 void MyProcess::OnTerminate(int pid
, int status
)
1160 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1161 pid
, m_cmd
.c_str(), status
);
1163 m_parent
->OnAsyncTermination(this);
1166 // ----------------------------------------------------------------------------
1168 // ----------------------------------------------------------------------------
1170 bool MyPipedProcess::HasInput()
1172 bool hasInput
= false;
1174 if ( IsInputAvailable() )
1176 wxTextInputStream
tis(*GetInputStream());
1178 // this assumes that the output is always line buffered
1180 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1182 m_parent
->GetLogListBox()->Append(msg
);
1187 if ( IsErrorAvailable() )
1189 wxTextInputStream
tis(*GetErrorStream());
1191 // this assumes that the output is always line buffered
1193 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1195 m_parent
->GetLogListBox()->Append(msg
);
1203 void MyPipedProcess::OnTerminate(int pid
, int status
)
1205 // show the rest of the output
1206 while ( HasInput() )
1209 m_parent
->OnProcessTerminated(this);
1211 MyProcess::OnTerminate(pid
, status
);
1214 // ----------------------------------------------------------------------------
1216 // ----------------------------------------------------------------------------
1218 bool MyPipedProcess2::HasInput()
1220 if ( !m_input
.empty() )
1222 wxTextOutputStream
os(*GetOutputStream());
1223 os
.WriteString(m_input
);
1228 // call us once again - may be we'll have output
1232 return MyPipedProcess::HasInput();
1235 // ============================================================================
1236 // MyPipeFrame implementation
1237 // ============================================================================
1239 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1240 const wxString
& cmd
,
1242 : wxFrame(parent
, wxID_ANY
, cmd
),
1244 // in a real program we'd check that the streams are !NULL here
1245 m_out(*process
->GetOutputStream()),
1246 m_in(*process
->GetInputStream()),
1247 m_err(*process
->GetErrorStream())
1249 m_process
->SetNextHandler(this);
1251 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1253 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1254 wxDefaultPosition
, wxDefaultSize
,
1255 wxTE_PROCESS_ENTER
);
1256 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1257 wxDefaultPosition
, wxDefaultSize
,
1258 wxTE_MULTILINE
| wxTE_RICH
);
1259 m_textIn
->SetEditable(false);
1260 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1261 wxDefaultPosition
, wxDefaultSize
,
1262 wxTE_MULTILINE
| wxTE_RICH
);
1263 m_textErr
->SetEditable(false);
1265 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1266 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1268 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1270 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1272 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1274 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1276 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1278 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1279 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1280 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1282 panel
->SetSizer(sizerTop
);
1283 sizerTop
->Fit(this);
1288 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1291 wxFileDialog
filedlg(this, wxT("Select file to send"));
1292 if ( filedlg
.ShowModal() != wxID_OK
)
1295 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1297 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1300 // can't write the entire string at once, this risk overflowing the pipe
1301 // and we would dead lock
1302 size_t len
= data
.length();
1303 const wxChar
*pc
= data
.c_str();
1306 const size_t CHUNK_SIZE
= 4096;
1307 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1309 // note that not all data could have been written as we don't block on
1310 // the write end of the pipe
1311 const size_t lenChunk
= m_out
.LastWrite();
1318 #endif // wxUSE_FILEDLG
1321 void MyPipeFrame::DoGet()
1323 // we don't have any way to be notified when any input appears on the
1324 // stream so we have to poll it :-(
1325 DoGetFromStream(m_textIn
, m_in
);
1326 DoGetFromStream(m_textErr
, m_err
);
1329 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1331 while ( in
.CanRead() )
1333 wxChar buffer
[4096];
1334 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = wxT('\0');
1336 text
->AppendText(buffer
);
1340 void MyPipeFrame::DoClose()
1342 m_process
->CloseOutput();
1347 void MyPipeFrame::DisableInput()
1349 m_textOut
->SetEditable(false);
1350 FindWindow(Exec_Btn_Send
)->Disable();
1351 FindWindow(Exec_Btn_SendFile
)->Disable();
1352 FindWindow(Exec_Btn_Close
)->Disable();
1355 void MyPipeFrame::DisableOutput()
1357 FindWindow(Exec_Btn_Get
)->Disable();
1360 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1364 // we're not interested in getting the process termination notification
1365 // if we are closing it ourselves
1366 wxProcess
*process
= m_process
;
1368 process
->SetNextHandler(NULL
);
1370 process
->CloseOutput();
1376 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1380 wxDELETE(m_process
);
1382 wxLogWarning(wxT("The other process has terminated, closing"));