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}.
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),

View File

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

View File

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