Invoking Other Applications
Sometimes you may want to run other applications from your own application, whether it's an external browser or another of your own applications. wxExecute is a versatile function that can be used to run a program with or without command-line arguments, synchronously or asychronously, optionally collecting output from the launched process, or even redirecting input to and output from the process to enable the current application to interact with it.
Running an Application
Here are some simple examples of using wxExecute:
Note that you can optionally enclose parameters and the executable in quotation marks, which is useful if there are spaces in the names.
// Executes asychronously by default (returns immediately)
wxExecute(wxT("c:\\windows\\notepad.exe"));
// Does not return until the user has quit Notepad
wxExecute(wxT("c:\\windows\\notepad.exe c:\\temp\\temp.txt"),
wxEXEC_SYNC);
Launching Documents
If you want to run an application to open an associated document, you can use the wxMimeTypesManager class on Windows and Linux. You can find out the file type associated with the extension and then use it to get the required command to pass to wxExecute. For example, to view a249 file:
Unfortunately, this doesn't work under Mac OS X because OS X uses a completely different way of associating document types with applications. For arbitrary documents, it's better to ask the Finder to open a document, and fo252 files, it's better to use the special Mac OS X function, ICLaunchURL. wxExecute is not always the best solution under Windows either, where ShellExecute may be a more effective function to use fo252 files. Even on Unix, there may be specific fallback scripts you want to use if an associated application is not found, such as
wxString url = wxT("c:\\home\\234");
bool ok = false;
wxFileType *ft = wxTheMimeTypesManager->
GetFileTypeFromExtension(wxT("html"));
if ( ft )
{
wxString cmd;
ok = ft->GetOpenCommand(&cmd,
wxFileType::MessageParameters(url, wxEmptyString));
delete ft;
if (ok)
{
ok = (wxExecute(cmd, wxEXEC_ASYNC) != 0);
}
}
For Linux, wxVi240File, wxViewPDFFile, and wxPlaySoundFile include fallbacks for when an associated application is not found. You may want to adjust the fallbacks to your own requirements. wxPlaySoundFile is intended for playing large audio files in a separate application; for small sound effects in your application, use wxSound instead.
wxExecute(wxT("osascript -e \"tell application \\\"AcmeApp\\\"\" -e
\"activate\" -e \"end tell\"));
Redirecting Process Input and Output
There may be times when you want to "capture" another process and allow your application (and/or its user) to control it. This can be preferable to rewriting an entire complex program just to have the functionality integrated in your application. wxExecute can help you integrate another console-based process by letting you redirect its input and output.To do this, you pass an instance of a class derived from wxProcess to wxExecute. The instance's OnTerminate function will be called when the process terminates, and the process object can be used to extract output from or send input to the process.You can see various examples of wxExecute usage in samples/exec in your wxWidgets distribution. We also provide an example of embedding the GDB debugger in examples/chap20/pipedprocess. We don't provide the toolbar bitmaps or compilable application, but otherwise it's complete and will work on Windows, Linux, and Mac OS X if GDB is available.debugger.h and debugger.cpp implement a piped process and a window containing a toolbar and a text control, used for displaying debugger output and getting input from the user to send to the debugger.textctrlex.h and textctrlex.cpp implement a control derived from wxStyledTextCtrl but with some wxTextCtrl compatibility functions and standard event handlers for copy, cut, paste, undo, and redo.processapp.h and processapp.cpp implement an application class that can handle input from several processes in idle time.The debugger is started with
It can be killed with
DebuggerProcess *process = new DebuggerProcess (this);
m_pid = wxExecute(cmd, wxEXEC_ASYNC, process);
To send a command to the debugger, an internal variable is set to let the input to the process to be picked up in idle time:
wxKill(m_pid, wxSIGKILL, NULL, wxKILL_CHILDREN);
HasInput is called periodically by the application object from its idle handler and is responsible for sending input to the process and reading output from the standard error and standard output streams:
// Send a command to the debugger
bool DebuggerWindow::SendDebugCommand(const wxString& cmd,
bool needEcho)
{
if (m_process && m_process->GetOutputStream())
{
wxString c = cmd;
c += wxT("\n");
if (needEcho)
AddLine(cmd);
// This simple sets m_input to be processed
// by HasInput in OnIdle time
m_process->SendInput(c);
return true;
}
return false;
}
Note a crucial difference from the code in the wxWidgets exec sample, which assumes that it can read a line at a time. This will cause a hang if a carriage return is not output by the process. The previous code uses a buffer to keep reading as much input as possible, which is safer.The ProcessApp class can be used as a base class for your own application class, or you can copy the functions into your class. It maintains a list of processes, registered with the application instance with RegisterProcess and UnregisterProcess, and handles process input in idle time, as follows:
bool DebuggerProcess::HasInput()
{
bool hasInput = false;
static wxChar buffer[4096];
if ( !m_input.IsEmpty() )
{
wxTextOutputStream os(*GetOutputStream());
os.WriteString(m_input);
m_input.Empty();
hasInput = true;
}
if ( IsErrorAvailable() )
{
buffer[GetErrorStream()->Read(buffer, WXSIZEOF(buffer) -
1).LastRead()] = _T('\0');
wxString msg(buffer);
m_debugWindow->ReadDebuggerOutput(msg, true);
hasInput = true;
}
if ( IsInputAvailable() )
{
buffer[GetInputStream()->Read(buffer, WXSIZEOF(buffer) -
1).LastRead()] = _T('\0');
wxString msg(buffer);
m_debugWindow->ReadDebuggerOutput(buffer, false);
hasInput = true;
}
return hasInput;
}
// Handle any pending input, in idle time
bool ProcessApp::HandleProcessInput()
{
if (!HasProcesses())
return false;
bool hasInput = false;
wxNode* node = m_processes.GetFirst();
while (node)
{
PipedProcess* process = wxDynamicCast(node->GetData(), PipedProcess);
if (process && process->HasInput())
hasInput = true;
node = node->GetNext();
}
return hasInput;
}
void ProcessApp::OnIdle(wxIdleEvent& event)
{
if (HandleProcessInput())
event.RequestMore();
event.Skip();
}