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:
Vadim Zeitlin
2001-05-09 01:20:04 +00:00
parent 66e23ad208
commit ca289436cd
3 changed files with 166 additions and 67 deletions

View File

@@ -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),

View File

@@ -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)
{ {

View File

@@ -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;