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
574 static const wxString signalNames
[] =
576 wxT("Just test (SIGNONE)"),
577 wxT("Hangup (SIGHUP)"),
578 wxT("Interrupt (SIGINT)"),
579 wxT("Quit (SIGQUIT)"),
580 wxT("Illegal instruction (SIGILL)"),
581 wxT("Trap (SIGTRAP)"),
582 wxT("Abort (SIGABRT)"),
583 wxT("Emulated trap (SIGEMT)"),
584 wxT("FP exception (SIGFPE)"),
585 wxT("Kill (SIGKILL)"),
587 wxT("Segment violation (SIGSEGV)"),
588 wxT("System (SIGSYS)"),
589 wxT("Broken pipe (SIGPIPE)"),
590 wxT("Alarm (SIGALRM)"),
591 wxT("Terminate (SIGTERM)"),
594 int sig
= wxGetSingleChoiceIndex(wxT("How to kill the process?"),
595 wxT("Exec question"),
596 WXSIZEOF(signalNames
), signalNames
,
601 wxFAIL_MSG( wxT("unexpected return value") );
629 if ( wxProcess::Exists(pid
) )
631 wxLogStatus(wxT("Process %ld is running."), pid
);
635 wxLogStatus(wxT("No process with pid = %ld."), pid
);
640 wxKillError rc
= wxProcess::Kill(pid
, (wxSignal
)sig
);
641 if ( rc
== wxKILL_OK
)
643 wxLogStatus(wxT("Process %ld killed with signal %d."), pid
, sig
);
647 static const wxChar
*errorText
[] =
650 wxT("signal not supported"),
651 wxT("permission denied"),
652 wxT("no such process"),
653 wxT("unspecified error"),
656 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
657 pid
, sig
, errorText
[rc
]);
662 // ----------------------------------------------------------------------------
663 // event handlers: exec menu
664 // ----------------------------------------------------------------------------
666 void MyFrame::DoAsyncExec(const wxString
& cmd
)
668 MyProcess
* const process
= new MyProcess(this, cmd
);
669 m_pidLast
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
672 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
678 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast
, cmd
.c_str());
682 // the parent frame keeps track of all async processes as it needs to
683 // free them if we exit before the child process terminates
684 AddAsyncProcess(process
);
688 void MyFrame::OnSyncExec(wxCommandEvent
& WXUNUSED(event
))
690 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
697 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
699 int code
= wxExecute(cmd
, wxEXEC_SYNC
);
701 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
707 void MyFrame::OnSyncNoEventsExec(wxCommandEvent
& WXUNUSED(event
))
709 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
716 wxLogStatus( wxT("'%s' is running please wait..."), cmd
.c_str() );
718 int code
= wxExecute(cmd
, wxEXEC_BLOCK
);
720 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
726 void MyFrame::OnAsyncExec(wxCommandEvent
& WXUNUSED(event
))
728 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
738 void MyFrame::OnShell(wxCommandEvent
& WXUNUSED(event
))
740 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
747 int code
= wxShell(cmd
);
748 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
753 void MyFrame::OnExecWithRedirect(wxCommandEvent
& WXUNUSED(event
))
758 m_cmdLast
= "type Makefile.in";
760 m_cmdLast
= "cat -n Makefile";
764 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
772 switch ( wxMessageBox(wxT("Execute it synchronously?"),
773 wxT("Exec question"),
774 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this) )
790 wxLogStatus("\"%s\" is running please wait...", cmd
);
794 wxArrayString output
, errors
;
795 int code
= wxExecute(cmd
, output
, errors
);
797 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
798 cmd
, sw
.Time(), code
);
802 ShowOutput(cmd
, output
, wxT("Output"));
803 ShowOutput(cmd
, errors
, wxT("Errors"));
808 MyPipedProcess
*process
= new MyPipedProcess(this, cmd
);
809 if ( !wxExecute(cmd
, wxEXEC_ASYNC
, process
) )
811 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
817 AddPipedProcess(process
);
824 void MyFrame::OnExecWithPipe(wxCommandEvent
& WXUNUSED(event
))
827 m_cmdLast
= wxT("tr [a-z] [A-Z]");
829 wxString cmd
= wxGetTextFromUser(wxT("Enter the command: "),
836 wxString input
= wxGetTextFromUser(wxT("Enter the string to send to it: "),
841 // always execute the filter asynchronously
842 MyPipedProcess2
*process
= new MyPipedProcess2(this, cmd
, input
);
843 long pid
= wxExecute(cmd
, wxEXEC_ASYNC
, process
);
846 wxLogStatus(wxT("Process %ld (%s) launched."), pid
, cmd
.c_str());
848 AddPipedProcess(process
);
852 wxLogError(wxT("Execution of '%s' failed."), cmd
.c_str());
860 void MyFrame::OnPOpen(wxCommandEvent
& WXUNUSED(event
))
862 wxString cmd
= wxGetTextFromUser(wxT("Enter the command to launch: "),
868 wxProcess
*process
= wxProcess::Open(cmd
);
871 wxLogError(wxT("Failed to launch the command."));
875 wxLogVerbose(wxT("PID of the new process: %ld"), process
->GetPid());
877 wxOutputStream
*out
= process
->GetOutputStream();
880 wxLogError(wxT("Failed to connect to child stdin"));
884 wxInputStream
*in
= process
->GetInputStream();
887 wxLogError(wxT("Failed to connect to child stdout"));
891 new MyPipeFrame(this, cmd
, process
);
894 static wxString gs_lastFile
;
896 static bool AskUserForFileName()
901 filename
= wxLoadFileSelector(wxT("any"), wxEmptyString
, gs_lastFile
);
902 #else // !wxUSE_FILEDLG
903 filename
= wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
905 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
907 if ( filename
.empty() )
910 gs_lastFile
= filename
;
915 void MyFrame::OnFileExec(wxCommandEvent
& WXUNUSED(event
))
917 if ( !AskUserForFileName() )
920 wxString ext
= gs_lastFile
.AfterLast(wxT('.'));
921 wxFileType
*ft
= wxTheMimeTypesManager
->GetFileTypeFromExtension(ext
);
924 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
930 bool ok
= ft
->GetOpenCommand(&cmd
,
931 wxFileType::MessageParameters(gs_lastFile
));
935 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
943 void MyFrame::OnFileLaunch(wxCommandEvent
& WXUNUSED(event
))
945 if ( !AskUserForFileName() )
948 if ( !wxLaunchDefaultApplication(gs_lastFile
) )
950 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile
);
954 void MyFrame::OnOpenURL(wxCommandEvent
& WXUNUSED(event
))
956 static wxString
s_url(wxT("http://www.wxwidgets.org/"));
958 wxString filename
= wxGetTextFromUser
960 wxT("Enter the URL"),
966 if ( filename
.empty() )
971 if ( !wxLaunchDefaultBrowser(s_url
) )
973 wxLogError(wxT("Failed to open URL \"%s\""), s_url
.c_str());
977 // ----------------------------------------------------------------------------
979 // ----------------------------------------------------------------------------
983 bool MyFrame::GetDDEServer()
985 wxString server
= wxGetTextFromUser(wxT("Server to connect to:"),
986 DIALOG_TITLE
, m_server
);
992 wxString topic
= wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE
, m_topic
);
998 wxString cmd
= wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE
, m_cmdDde
);
1007 void MyFrame::OnDDEExec(wxCommandEvent
& WXUNUSED(event
))
1009 if ( !GetDDEServer() )
1013 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1016 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1021 if ( !conn
->Execute(m_cmdDde
) )
1023 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1028 wxLogStatus(wxT("Successfully executed DDE command"));
1033 void MyFrame::OnDDERequest(wxCommandEvent
& WXUNUSED(event
))
1035 if ( !GetDDEServer() )
1039 wxConnectionBase
*conn
= client
.MakeConnection(wxEmptyString
, m_server
, m_topic
);
1042 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1047 if ( !conn
->Request(m_cmdDde
) )
1049 wxLogError(wxT("Failed to send request '%s' via DDE."),
1054 wxLogStatus(wxT("Successfully sent DDE request."));
1059 #endif // __WINDOWS__
1061 // ----------------------------------------------------------------------------
1063 // ----------------------------------------------------------------------------
1066 void MyFrame::OnIdle(wxIdleEvent
& event
)
1068 size_t count
= m_running
.GetCount();
1069 for ( size_t n
= 0; n
< count
; n
++ )
1071 if ( m_running
[n
]->HasInput() )
1073 event
.RequestMore();
1078 void MyFrame::OnIdleTimer(wxTimerEvent
& WXUNUSED(event
))
1083 void MyFrame::OnBgTimer(wxTimerEvent
& WXUNUSED(event
))
1085 static unsigned long s_ticks
= 0;
1086 SetStatusText(wxString::Format("%lu ticks", s_ticks
++), 1);
1089 void MyFrame::OnProcessTerminated(MyPipedProcess
*process
)
1091 RemovePipedProcess(process
);
1094 void MyFrame::OnAsyncTermination(MyProcess
*process
)
1096 m_allAsync
.Remove(process
);
1101 void MyFrame::AddPipedProcess(MyPipedProcess
*process
)
1103 if ( m_running
.IsEmpty() )
1105 // we want to start getting the timer events to ensure that a
1106 // steady stream of idle events comes in -- otherwise we
1107 // wouldn't be able to poll the child process input
1108 m_timerIdleWakeUp
.Start(100);
1110 //else: the timer is already running
1112 m_running
.Add(process
);
1113 m_allAsync
.Add(process
);
1116 void MyFrame::RemovePipedProcess(MyPipedProcess
*process
)
1118 m_running
.Remove(process
);
1120 if ( m_running
.IsEmpty() )
1122 // we don't need to get idle events all the time any more
1123 m_timerIdleWakeUp
.Stop();
1127 void MyFrame::ShowOutput(const wxString
& cmd
,
1128 const wxArrayString
& output
,
1129 const wxString
& title
)
1131 size_t count
= output
.GetCount();
1135 m_lbox
->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1136 title
.c_str(), cmd
.c_str()));
1138 for ( size_t n
= 0; n
< count
; n
++ )
1140 m_lbox
->Append(output
[n
]);
1143 m_lbox
->Append(wxString::Format(wxT("--- End of %s ---"),
1144 title
.Lower().c_str()));
1147 // ----------------------------------------------------------------------------
1149 // ----------------------------------------------------------------------------
1151 void MyProcess::OnTerminate(int pid
, int status
)
1153 wxLogStatus(m_parent
, wxT("Process %u ('%s') terminated with exit code %d."),
1154 pid
, m_cmd
.c_str(), status
);
1156 m_parent
->OnAsyncTermination(this);
1159 // ----------------------------------------------------------------------------
1161 // ----------------------------------------------------------------------------
1163 bool MyPipedProcess::HasInput()
1165 bool hasInput
= false;
1167 if ( IsInputAvailable() )
1169 wxTextInputStream
tis(*GetInputStream());
1171 // this assumes that the output is always line buffered
1173 msg
<< m_cmd
<< wxT(" (stdout): ") << tis
.ReadLine();
1175 m_parent
->GetLogListBox()->Append(msg
);
1180 if ( IsErrorAvailable() )
1182 wxTextInputStream
tis(*GetErrorStream());
1184 // this assumes that the output is always line buffered
1186 msg
<< m_cmd
<< wxT(" (stderr): ") << tis
.ReadLine();
1188 m_parent
->GetLogListBox()->Append(msg
);
1196 void MyPipedProcess::OnTerminate(int pid
, int status
)
1198 // show the rest of the output
1199 while ( HasInput() )
1202 m_parent
->OnProcessTerminated(this);
1204 MyProcess::OnTerminate(pid
, status
);
1207 // ----------------------------------------------------------------------------
1209 // ----------------------------------------------------------------------------
1211 bool MyPipedProcess2::HasInput()
1213 if ( !m_input
.empty() )
1215 wxTextOutputStream
os(*GetOutputStream());
1216 os
.WriteString(m_input
);
1221 // call us once again - may be we'll have output
1225 return MyPipedProcess::HasInput();
1228 // ============================================================================
1229 // MyPipeFrame implementation
1230 // ============================================================================
1232 MyPipeFrame::MyPipeFrame(wxFrame
*parent
,
1233 const wxString
& cmd
,
1235 : wxFrame(parent
, wxID_ANY
, cmd
),
1237 // in a real program we'd check that the streams are !NULL here
1238 m_out(*process
->GetOutputStream()),
1239 m_in(*process
->GetInputStream()),
1240 m_err(*process
->GetErrorStream())
1242 m_process
->SetNextHandler(this);
1244 wxPanel
*panel
= new wxPanel(this, wxID_ANY
);
1246 m_textOut
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1247 wxDefaultPosition
, wxDefaultSize
,
1248 wxTE_PROCESS_ENTER
);
1249 m_textIn
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1250 wxDefaultPosition
, wxDefaultSize
,
1251 wxTE_MULTILINE
| wxTE_RICH
);
1252 m_textIn
->SetEditable(false);
1253 m_textErr
= new wxTextCtrl(panel
, wxID_ANY
, wxEmptyString
,
1254 wxDefaultPosition
, wxDefaultSize
,
1255 wxTE_MULTILINE
| wxTE_RICH
);
1256 m_textErr
->SetEditable(false);
1258 wxSizer
*sizerTop
= new wxBoxSizer(wxVERTICAL
);
1259 sizerTop
->Add(m_textOut
, 0, wxGROW
| wxALL
, 5);
1261 wxSizer
*sizerBtns
= new wxBoxSizer(wxHORIZONTAL
);
1263 Add(new wxButton(panel
, Exec_Btn_Send
, wxT("&Send")), 0, wxALL
, 5);
1265 Add(new wxButton(panel
, Exec_Btn_SendFile
, wxT("&File...")), 0, wxALL
, 5);
1267 Add(new wxButton(panel
, Exec_Btn_Get
, wxT("&Get")), 0, wxALL
, 5);
1269 Add(new wxButton(panel
, Exec_Btn_Close
, wxT("&Close")), 0, wxALL
, 5);
1271 sizerTop
->Add(sizerBtns
, 0, wxCENTRE
| wxALL
, 5);
1272 sizerTop
->Add(m_textIn
, 1, wxGROW
| wxALL
, 5);
1273 sizerTop
->Add(m_textErr
, 1, wxGROW
| wxALL
, 5);
1275 panel
->SetSizer(sizerTop
);
1276 sizerTop
->Fit(this);
1281 void MyPipeFrame::OnBtnSendFile(wxCommandEvent
& WXUNUSED(event
))
1284 wxFileDialog
filedlg(this, wxT("Select file to send"));
1285 if ( filedlg
.ShowModal() != wxID_OK
)
1288 wxFFile
file(filedlg
.GetFilename(), wxT("r"));
1290 if ( !file
.IsOpened() || !file
.ReadAll(&data
) )
1293 // can't write the entire string at once, this risk overflowing the pipe
1294 // and we would dead lock
1295 size_t len
= data
.length();
1296 const wxChar
*pc
= data
.c_str();
1299 const size_t CHUNK_SIZE
= 4096;
1300 m_out
.Write(pc
, len
> CHUNK_SIZE
? CHUNK_SIZE
: len
);
1302 // note that not all data could have been written as we don't block on
1303 // the write end of the pipe
1304 const size_t lenChunk
= m_out
.LastWrite();
1311 #endif // wxUSE_FILEDLG
1314 void MyPipeFrame::DoGet()
1316 // we don't have any way to be notified when any input appears on the
1317 // stream so we have to poll it :-(
1318 DoGetFromStream(m_textIn
, m_in
);
1319 DoGetFromStream(m_textErr
, m_err
);
1322 void MyPipeFrame::DoGetFromStream(wxTextCtrl
*text
, wxInputStream
& in
)
1324 while ( in
.CanRead() )
1326 wxChar buffer
[4096];
1327 buffer
[in
.Read(buffer
, WXSIZEOF(buffer
) - 1).LastRead()] = wxT('\0');
1329 text
->AppendText(buffer
);
1333 void MyPipeFrame::DoClose()
1335 m_process
->CloseOutput();
1340 void MyPipeFrame::DisableInput()
1342 m_textOut
->SetEditable(false);
1343 FindWindow(Exec_Btn_Send
)->Disable();
1344 FindWindow(Exec_Btn_SendFile
)->Disable();
1345 FindWindow(Exec_Btn_Close
)->Disable();
1348 void MyPipeFrame::DisableOutput()
1350 FindWindow(Exec_Btn_Get
)->Disable();
1353 void MyPipeFrame::OnClose(wxCloseEvent
& event
)
1357 // we're not interested in getting the process termination notification
1358 // if we are closing it ourselves
1359 wxProcess
*process
= m_process
;
1361 process
->SetNextHandler(NULL
);
1363 process
->CloseOutput();
1369 void MyPipeFrame::OnProcessTerm(wxProcessEvent
& WXUNUSED(event
))
1376 wxLogWarning(wxT("The other process has terminated, closing"));