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:
@@ -1466,7 +1466,12 @@ should ensure that this can cause no recursion, in the simplest case by
|
|||||||
calling \helpref{wxEnableTopLevelWindows(FALSE)}{wxenabletoplevelwindows}.
|
calling \helpref{wxEnableTopLevelWindows(FALSE)}{wxenabletoplevelwindows}.
|
||||||
|
|
||||||
For asynchronous execution, however, the return value is the process id and
|
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
|
If callback isn't NULL and if execution is asynchronous (note that callback
|
||||||
parameter can not be non-NULL for synchronous execution),
|
parameter can not be non-NULL for synchronous execution),
|
||||||
|
@@ -93,8 +93,6 @@ public:
|
|||||||
|
|
||||||
void OnFileExec(wxCommandEvent& event);
|
void OnFileExec(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnDDEExec(wxCommandEvent& event);
|
|
||||||
|
|
||||||
void OnAbout(wxCommandEvent& event);
|
void OnAbout(wxCommandEvent& event);
|
||||||
|
|
||||||
// polling output of async processes
|
// polling output of async processes
|
||||||
@@ -111,8 +109,21 @@ private:
|
|||||||
|
|
||||||
void DoAsyncExec(const wxString& cmd);
|
void DoAsyncExec(const wxString& cmd);
|
||||||
|
|
||||||
|
// last command we executed
|
||||||
wxString m_cmdLast;
|
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;
|
wxListBox *m_lbox;
|
||||||
|
|
||||||
MyProcessesArray m_running;
|
MyProcessesArray m_running;
|
||||||
@@ -187,6 +198,7 @@ enum
|
|||||||
Exec_Shell,
|
Exec_Shell,
|
||||||
Exec_OpenFile,
|
Exec_OpenFile,
|
||||||
Exec_DDEExec,
|
Exec_DDEExec,
|
||||||
|
Exec_DDERequest,
|
||||||
Exec_Redirect,
|
Exec_Redirect,
|
||||||
Exec_Pipe,
|
Exec_Pipe,
|
||||||
Exec_About = 300
|
Exec_About = 300
|
||||||
@@ -214,6 +226,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
|||||||
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
|
EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
|
||||||
|
|
||||||
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
|
EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
|
||||||
|
EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
|
||||||
|
|
||||||
EVT_MENU(Exec_About, MyFrame::OnAbout)
|
EVT_MENU(Exec_About, MyFrame::OnAbout)
|
||||||
|
|
||||||
@@ -292,6 +305,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
|
|||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
execMenu->AppendSeparator();
|
execMenu->AppendSeparator();
|
||||||
execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D"));
|
execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D"));
|
||||||
|
execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF);
|
wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF);
|
||||||
@@ -529,48 +543,86 @@ void MyFrame::OnFileExec(wxCommandEvent& event)
|
|||||||
DoAsyncExec(cmd);
|
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))
|
void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
#ifdef __WINDOWS__
|
if ( !GetDDEServer() )
|
||||||
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 )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxDDEClient client;
|
wxDDEClient client;
|
||||||
wxConnectionBase *conn = client.MakeConnection("", server, topic);
|
wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic);
|
||||||
if ( !conn )
|
if ( !conn )
|
||||||
{
|
{
|
||||||
wxLogError(_T("Failed to connect to the DDE server '%s'."),
|
wxLogError(_T("Failed to connect to the DDE server '%s'."),
|
||||||
server.c_str());
|
m_server.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( !conn->Execute(cmd) )
|
if ( !conn->Execute(m_cmdDde) )
|
||||||
{
|
{
|
||||||
wxLogError(_T("Failed to execute command '%s' via DDE."),
|
wxLogError(_T("Failed to execute command '%s' via DDE."),
|
||||||
cmd.c_str());
|
m_cmdDde.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wxLogStatus(_T("Successfully executed DDE command"));
|
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
|
// input polling
|
||||||
void MyFrame::OnIdle(wxIdleEvent& event)
|
void MyFrame::OnIdle(wxIdleEvent& event)
|
||||||
{
|
{
|
||||||
|
@@ -315,6 +315,49 @@ LRESULT APIENTRY _EXPORT wxExecuteWindowCbk(HWND hWnd, UINT message,
|
|||||||
}
|
}
|
||||||
#endif // Win32
|
#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)
|
long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
||||||
{
|
{
|
||||||
wxCHECK_MSG( !!cmd, 0, wxT("empty command in wxExecute") );
|
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:")
|
static const size_t lenDdePrefix = 7; // strlen("WX_DDE:")
|
||||||
if ( cmd.Left(lenDdePrefix) == _T("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;
|
const wxChar *p = cmd.c_str() + 7;
|
||||||
while ( *p && *p != _T('#') )
|
while ( *p && *p != _T('#') )
|
||||||
{
|
{
|
||||||
@@ -385,27 +433,21 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
|||||||
ddeCommand += *p++;
|
ddeCommand += *p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe we don't have to launch the DDE server at all - if it is
|
// if we want to just launch the program and not wait for its
|
||||||
// already running, for example
|
// termination, try to execute DDE command right now, it can succeed if
|
||||||
wxDDEClient client;
|
// the process is already running - but as it fails if it's not
|
||||||
wxLogNull nolog;
|
// running, suppress any errors it might generate
|
||||||
wxConnectionBase *conn = client.MakeConnection(_T(""),
|
if ( !sync )
|
||||||
ddeServer,
|
|
||||||
ddeTopic);
|
|
||||||
if ( conn )
|
|
||||||
{
|
{
|
||||||
// FIXME we don't check the return code as for some strange reason
|
wxLogNull noErrors;
|
||||||
// it will sometimes be FALSE - it is probably a bug in our
|
if ( wxExecuteDDE(ddeServer, ddeTopic, ddeCommand) )
|
||||||
// DDE code but I don't see anything wrong there
|
{
|
||||||
(void)conn->Execute(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
|
||||||
// ok, the command executed - return value indicating success,
|
// which would be quite bad
|
||||||
// making it up for async case as we really don't have any way to
|
return -1;
|
||||||
// get the real PID of the DDE server here
|
}
|
||||||
return sync ? 0 : -1;
|
|
||||||
}
|
}
|
||||||
//else: couldn't establish DDE conversation, now try launching the app
|
|
||||||
// and sending the DDE request again
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // wxUSE_IPC
|
#endif // wxUSE_IPC
|
||||||
@@ -637,36 +679,36 @@ long wxExecute(const wxString& cmd, bool sync, wxProcess *handler)
|
|||||||
#if wxUSE_IPC
|
#if wxUSE_IPC
|
||||||
// second part of DDE hack: now establish the DDE conversation with the
|
// second part of DDE hack: now establish the DDE conversation with the
|
||||||
// just launched process
|
// just launched process
|
||||||
if ( !!ddeServer )
|
if ( !ddeServer.empty() )
|
||||||
{
|
{
|
||||||
wxDDEClient client;
|
bool ok;
|
||||||
wxConnectionBase *conn;
|
|
||||||
|
|
||||||
|
// 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
|
default:
|
||||||
wxLogNull nolog;
|
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 )
|
if ( !ok )
|
||||||
{
|
|
||||||
// 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 )
|
|
||||||
{
|
{
|
||||||
// FIXME just as above we don't check Execute() return code
|
// FIXME just as above we don't check Execute() return code
|
||||||
wxLogNull nolog;
|
wxLogNull nolog;
|
||||||
|
Reference in New Issue
Block a user