From ca289436cd4da42ed1be1480aebd01750eee41ec Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Wed, 9 May 2001 01:20:04 +0000
Subject: [PATCH] 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
---
 docs/latex/wx/function.tex |   7 +-
 samples/exec/exec.cpp      |  88 +++++++++++++++++++------
 src/msw/utilsexc.cpp       | 128 ++++++++++++++++++++++++-------------
 3 files changed, 161 insertions(+), 62 deletions(-)

diff --git a/docs/latex/wx/function.tex b/docs/latex/wx/function.tex
index b85ec925e5..952dcc160c 100644
--- a/docs/latex/wx/function.tex
+++ b/docs/latex/wx/function.tex
@@ -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),
diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp
index b540460720..80f4f4335d 100644
--- a/samples/exec/exec.cpp
+++ b/samples/exec/exec.cpp
@@ -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);
 }
 
-void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
-{
 #ifdef __WINDOWS__
+
+bool MyFrame::GetDDEServer()
+{
     wxString server = wxGetTextFromUser(_T("Server to connect to:"),
-                                        DIALOG_TITLE, _T("IExplore"));
+                                        DIALOG_TITLE, m_server);
     if ( !server )
-        return;
+        return FALSE;
+
+    m_server = server;
 
-    wxString topic = wxGetTextFromUser(_T("DDE topic:"),
-                                       DIALOG_TITLE, _T("WWW_OpenURL"));
+    wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic);
     if ( !topic )
-        return;
+        return FALSE;
 
-    wxString cmd = wxGetTextFromUser(_T("DDE command:"),
-                                     DIALOG_TITLE,
-                                     _T("\"file:F:\\wxWindows\\samples\\"
-                                        "image\\horse.gif\",,-1,,,,,"));
+    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))
+{
+    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)
 {
diff --git a/src/msw/utilsexc.cpp b/src/msw/utilsexc.cpp
index 7bfdc8234a..6dfe980f2a 100644
--- a/src/msw/utilsexc.cpp
+++ b/src/msw/utilsexc.cpp
@@ -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"));
 
-        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());
-            }
+            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 )
         {
             // FIXME just as above we don't check Execute() return code
             wxLogNull nolog;
-- 
2.50.0

