fixed wxExecute + DDE bug (merged from 2.2)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@10084 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
66e23ad208
commit
ca289436cd
@ -1466,7 +1466,12 @@ should ensure that this can cause no recursion, in the simplest case by
|
||||
calling \helpref{wxEnableTopLevelWindows(FALSE)}{wxenabletoplevelwindows}.
|
||||
|
||||
For asynchronous execution, however, the return value is the process id and
|
||||
zero value indicates that the command could not be executed.
|
||||
zero value indicates that the command could not be executed. As an added
|
||||
complication, the return value of $-1$ in this case indicattes that we didn't
|
||||
launch a new process, but connected to the running one (this can only happen in
|
||||
case of using DDE under Windows for command execution). In particular, in this,
|
||||
and only this, case the calling code will not get the notification about
|
||||
process termination.
|
||||
|
||||
If callback isn't NULL and if execution is asynchronous (note that callback
|
||||
parameter can not be non-NULL for synchronous execution),
|
||||
|
@ -93,8 +93,6 @@ public:
|
||||
|
||||
void OnFileExec(wxCommandEvent& event);
|
||||
|
||||
void OnDDEExec(wxCommandEvent& event);
|
||||
|
||||
void OnAbout(wxCommandEvent& event);
|
||||
|
||||
// polling output of async processes
|
||||
@ -111,8 +109,21 @@ private:
|
||||
|
||||
void DoAsyncExec(const wxString& cmd);
|
||||
|
||||
// last command we executed
|
||||
wxString m_cmdLast;
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
void OnDDEExec(wxCommandEvent& event);
|
||||
void OnDDERequest(wxCommandEvent& event);
|
||||
|
||||
bool GetDDEServer();
|
||||
|
||||
// last params of a DDE transaction
|
||||
wxString m_server,
|
||||
m_topic,
|
||||
m_cmdDde;
|
||||
#endif // __WINDOWS__
|
||||
|
||||
wxListBox *m_lbox;
|
||||
|
||||
MyProcessesArray m_running;
|
||||
@ -187,6 +198,7 @@ enum
|
||||
Exec_Shell,
|
||||
Exec_OpenFile,
|
||||
Exec_DDEExec,
|
||||
Exec_DDERequest,
|
||||
Exec_Redirect,
|
||||
Exec_Pipe,
|
||||
Exec_About = 300
|
||||
@ -214,6 +226,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
|
||||
|
||||
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
|
||||
EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
|
||||
|
||||
EVT_MENU(Exec_About, MyFrame::OnAbout)
|
||||
|
||||
@ -292,6 +305,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
|
||||
#ifdef __WINDOWS__
|
||||
execMenu->AppendSeparator();
|
||||
execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D"));
|
||||
execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R"));
|
||||
#endif
|
||||
|
||||
wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF);
|
||||
@ -529,48 +543,86 @@ void MyFrame::OnFileExec(wxCommandEvent& event)
|
||||
DoAsyncExec(cmd);
|
||||
}
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
bool MyFrame::GetDDEServer()
|
||||
{
|
||||
wxString server = wxGetTextFromUser(_T("Server to connect to:"),
|
||||
DIALOG_TITLE, m_server);
|
||||
if ( !server )
|
||||
return FALSE;
|
||||
|
||||
m_server = server;
|
||||
|
||||
wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic);
|
||||
if ( !topic )
|
||||
return FALSE;
|
||||
|
||||
m_topic = topic;
|
||||
|
||||
wxString cmd = wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE, m_cmdDde);
|
||||
if ( !cmd )
|
||||
return FALSE;
|
||||
|
||||
m_cmdDde = cmd;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
wxString server = wxGetTextFromUser(_T("Server to connect to:"),
|
||||
DIALOG_TITLE, _T("IExplore"));
|
||||
if ( !server )
|
||||
return;
|
||||
|
||||
wxString topic = wxGetTextFromUser(_T("DDE topic:"),
|
||||
DIALOG_TITLE, _T("WWW_OpenURL"));
|
||||
if ( !topic )
|
||||
return;
|
||||
|
||||
wxString cmd = wxGetTextFromUser(_T("DDE command:"),
|
||||
DIALOG_TITLE,
|
||||
_T("\"file:F:\\wxWindows\\samples\\"
|
||||
"image\\horse.gif\",,-1,,,,,"));
|
||||
if ( !cmd )
|
||||
if ( !GetDDEServer() )
|
||||
return;
|
||||
|
||||
wxDDEClient client;
|
||||
wxConnectionBase *conn = client.MakeConnection("", server, topic);
|
||||
wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic);
|
||||
if ( !conn )
|
||||
{
|
||||
wxLogError(_T("Failed to connect to the DDE server '%s'."),
|
||||
server.c_str());
|
||||
m_server.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !conn->Execute(cmd) )
|
||||
if ( !conn->Execute(m_cmdDde) )
|
||||
{
|
||||
wxLogError(_T("Failed to execute command '%s' via DDE."),
|
||||
cmd.c_str());
|
||||
m_cmdDde.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogStatus(_T("Successfully executed DDE command"));
|
||||
}
|
||||
}
|
||||
#endif // __WINDOWS__
|
||||
}
|
||||
|
||||
void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
if ( !GetDDEServer() )
|
||||
return;
|
||||
|
||||
wxDDEClient client;
|
||||
wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic);
|
||||
if ( !conn )
|
||||
{
|
||||
wxLogError(_T("Failed to connect to the DDE server '%s'."),
|
||||
m_server.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !conn->Request(m_cmdDde) )
|
||||
{
|
||||
wxLogError(_T("Failed to send request '%s' via DDE."),
|
||||
m_cmdDde.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogStatus(_T("Successfully sent DDE request."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__
|
||||
|
||||
// input polling
|
||||
void MyFrame::OnIdle(wxIdleEvent& event)
|
||||
{
|
||||
|
@ -315,6 +315,49 @@ LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
|
||||
}
|
||||
#endif // Win32
|
||||
|
||||
#if wxUSE_IPC
|
||||
|
||||
// connect to the given server via DDE and ask it to execute the command
|
||||
static bool wxExecuteDDE(const wxString& ddeServer,
|
||||
const wxString& ddeTopic,
|
||||
const wxString& ddeCommand)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
wxDDEClient client;
|
||||
wxConnectionBase *conn = client.MakeConnection(_T(""),
|
||||
ddeServer,
|
||||
ddeTopic);
|
||||
if ( !conn )
|
||||
{
|
||||
ok = FALSE;
|
||||
}
|
||||
else // connected to DDE server
|
||||
{
|
||||
// the added complication here is that although most
|
||||
// programs use XTYP_EXECUTE for their DDE API, some
|
||||
// important ones - like IE and other MS stuff - use
|
||||
// XTYP_REQUEST!
|
||||
//
|
||||
// so we try it first and then the other one if it
|
||||
// failed
|
||||
{
|
||||
wxLogNull noErrors;
|
||||
ok = conn->Request(ddeCommand) != NULL;
|
||||
}
|
||||
|
||||
if ( !ok )
|
||||
{
|
||||
// now try execute - but show the errors
|
||||
ok = conn->Execute(ddeCommand);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#endif // wxUSE_IPC
|
||||
|
||||
long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
||||
{
|
||||
wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
|
||||
@ -334,6 +377,11 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
||||
static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
|
||||
if ( cmd.Left(lenDdePrefix) == _T("WX_DDE#") )
|
||||
{
|
||||
// speed up the concatenations below
|
||||
ddeServer.reserve(256);
|
||||
ddeTopic.reserve(256);
|
||||
ddeCommand.reserve(256);
|
||||
|
||||
const wxChar *p = cmd.c_str() + 7;
|
||||
while ( *p && *p != _T('#') )
|
||||
{
|
||||
@ -385,27 +433,21 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
||||
ddeCommand += *p++;
|
||||
}
|
||||
|
||||
// maybe we don't have to launch the DDE server at all - if it is
|
||||
// already running, for example
|
||||
wxDDEClient client;
|
||||
wxLogNull nolog;
|
||||
wxConnectionBase *conn = client.MakeConnection(_T(""),
|
||||
ddeServer,
|
||||
ddeTopic);
|
||||
if ( conn )
|
||||
// if we want to just launch the program and not wait for its
|
||||
// termination, try to execute DDE command right now, it can succeed if
|
||||
// the process is already running - but as it fails if it's not
|
||||
// running, suppress any errors it might generate
|
||||
if ( !sync )
|
||||
{
|
||||
// FIXME we don't check the return code as for some strange reason
|
||||
// it will sometimes be FALSE - it is probably a bug in our
|
||||
// DDE code but I don't see anything wrong there
|
||||
(void)conn->Execute(ddeCommand);
|
||||
|
||||
// ok, the command executed - return value indicating success,
|
||||
// making it up for async case as we really don't have any way to
|
||||
// get the real PID of the DDE server here
|
||||
return sync ? 0 : -1;
|
||||
wxLogNull noErrors;
|
||||
if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
|
||||
{
|
||||
// a dummy PID - this is a hack, of course, but it's well worth
|
||||
// it as we don't open a new server each time we're called
|
||||
// which would be quite bad
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//else: couldn't establish DDE conversation, now try launching the app
|
||||
// and sending the DDE request again
|
||||
}
|
||||
else
|
||||
#endif // wxUSE_IPC
|
||||
@ -637,36 +679,36 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
||||
#if wxUSE_IPC
|
||||
// second part of DDE hack: now establish the DDE conversation with the
|
||||
// just launched process
|
||||
if ( !!ddeServer )
|
||||
if ( !ddeServer.empty() )
|
||||
{
|
||||
wxDDEClient client;
|
||||
wxConnectionBase *conn;
|
||||
bool ok;
|
||||
|
||||
// give the process the time to init itself
|
||||
//
|
||||
// we use a very big timeout hoping that WaitForInputIdle() will return
|
||||
// much sooner, but not INFINITE just in case the process hangs
|
||||
// completely - like this we will regain control sooner or later
|
||||
switch ( ::WaitForInputIdle(pi.hProcess, 10000 /* 10 seconds */) )
|
||||
{
|
||||
// try doing it the first time without error messages
|
||||
wxLogNull nolog;
|
||||
default:
|
||||
wxFAIL_MSG( _T("unexpected WaitForInputIdle() return code") );
|
||||
// fall through
|
||||
|
||||
conn = client.MakeConnection(_T(""), ddeServer, ddeTopic);
|
||||
case -1:
|
||||
wxLogLastError(_T("WaitForInputIdle() in wxExecute"));
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
wxLogDebug(_T("Timeout too small in WaitForInputIdle"));
|
||||
|
||||
ok = FALSE;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
// ok, process ready to accept DDE requests
|
||||
ok = wxExecuteDDE(ddeServer, ddeTopic, ddeCommand);
|
||||
}
|
||||
|
||||
if ( !conn )
|
||||
{
|
||||
// give the app some time to initialize itself: in fact, a common
|
||||
// reason for failure is that we tried to open DDE conversation too
|
||||
// soon (before the app had time to setup its DDE server), so wait
|
||||
// a bit and try again
|
||||
::Sleep(2000);
|
||||
|
||||
wxConnectionBase *conn = client.MakeConnection(_T(""),
|
||||
ddeServer,
|
||||
ddeTopic);
|
||||
if ( !conn )
|
||||
{
|
||||
wxLogError(_("Couldn't launch DDE server '%s'."), command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if ( conn )
|
||||
if ( !ok )
|
||||
{
|
||||
// FIXME just as above we don't check Execute() return code
|
||||
wxLogNull nolog;
|
||||
|
Loading…
Reference in New Issue
Block a user