]> git.saurik.com Git - wxWidgets.git/commitdiff
Allow wxAutomationObject::GetInstance() create new instance if needed.
authorVadim Zeitlin <vadim@wxwidgets.org>
Fri, 26 Nov 2010 13:30:37 +0000 (13:30 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Fri, 26 Nov 2010 13:30:37 +0000 (13:30 +0000)
When getting an instance of an OLE automation object, it is often useful to
connect to the existing instance if any or start a new one otherwise. Make
GetInstance() behave like this by default while still allowing to use the
wxAutomationInstance_UseExistingOnly flag to reestablish the old behaviour.

Also improve the error reporting in wxAutomationObject.

See #12489.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66262 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
include/wx/msw/ole/automtn.h
interface/wx/msw/ole/automtn.h
samples/oleauto/oleauto.cpp
src/msw/ole/automtn.cpp

index 05ca1fdecb2b0285daae3c82e2035658833af096..67205aed91d19448b02ad62e271c8e43858177f0 100644 (file)
@@ -175,6 +175,11 @@ Changes in behaviour not resulting in compilation errors, please read this!
   from the previous versions. Using wxNotebookEvent::GetSelection() instead of
   querying the notebook selection avoids the problem and is recommended.
 
   from the previous versions. Using wxNotebookEvent::GetSelection() instead of
   querying the notebook selection avoids the problem and is recommended.
 
+- wxMSW-specific wxAutomationObject::GetInstance() method now creates a new
+  instance if needed instead of failing if the application providing the
+  requested ProgID is not running. Pass wxAutomationInstance_UseExistingOnly
+  flag to it to revert to the old behaviour.
+
 
 Changes in behaviour which may result in compilation errors
 -----------------------------------------------------------
 
 Changes in behaviour which may result in compilation errors
 -----------------------------------------------------------
@@ -452,6 +457,7 @@ MSW:
 - Allow using wxDC::DrawText() with multiline texts.
 - Fix wxBitmapButton best size determination broken in 2.9.1.
 - Center task dialog-based wxProgressDialog on the parent (John Roberts).
 - Allow using wxDC::DrawText() with multiline texts.
 - Fix wxBitmapButton best size determination broken in 2.9.1.
 - Center task dialog-based wxProgressDialog on the parent (John Roberts).
+- wxAutomationObject::GetInstance() creates objects on demand (Kolya Kosenko).
 
 
 
 
 
 
index 7ea70f8e4c77e6260f75ab653ce77e9b3d71c5b2..63b9414d29ab344933f90208a8ec659ce2788da6 100644 (file)
@@ -26,6 +26,16 @@ typedef unsigned short* WXBSTR;
 #undef GetObject
 #endif
 
 #undef GetObject
 #endif
 
+// Flags used with wxAutomationObject::GetInstance()
+enum wxAutomationInstanceFlags
+{
+    // Only use the existing instance, never create a new one.
+    wxAutomationInstance_UseExistingOnly = 0,
+
+    // Create a new instance if there are no existing ones.
+    wxAutomationInstance_CreateIfNeeded = 1
+};
+
 /*
  * wxAutomationObject
  * Wraps up an IDispatch pointer and invocation; does variant conversion.
 /*
  * wxAutomationObject
  * Wraps up an IDispatch pointer and invocation; does variant conversion.
@@ -44,9 +54,10 @@ public:
 
     // Get a dispatch pointer from the current object associated
     // with a ProgID, such as "Excel.Application"
 
     // Get a dispatch pointer from the current object associated
     // with a ProgID, such as "Excel.Application"
-    bool GetInstance(const wxString& progId) const;
+    bool GetInstance(const wxString& progId,
+                     int flags = wxAutomationInstance_CreateIfNeeded) const;
 
 
-    // Get a dispatch pointer from a new instance of the the class
+    // Get a dispatch pointer from a new instance of the class
     bool CreateInstance(const wxString& progId) const;
 
     // Low-level invocation function. Pass either an array of variants,
     bool CreateInstance(const wxString& progId) const;
 
     // Low-level invocation function. Pass either an array of variants,
index 397703cbe8cb18e5c75e04aadf38e9eff064736f..c351d20e88723d7ad5d1c102b91bc08778a76c6e 100644 (file)
@@ -6,6 +6,35 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
+/**
+    Automation object creation flags.
+
+    These flags can be used with wxAutomationObject::GetInstance().
+
+    @since 2.9.2
+*/
+enum wxAutomationInstanceFlags
+{
+    /**
+        Only use the existing instance, never create a new one.
+
+        This flag can be used to forbid the creation of a new instance if none
+        is currently running.
+     */
+    wxAutomationInstance_UseExistingOnly = 0,
+
+    /**
+        Create a new instance if there are no existing ones.
+
+        This flag corresponds to the default behaviour of
+        wxAutomationObject::GetInstance() and means that if getting an existing
+        instance failed, we should call wxAutomationObject::CreateInstance() to
+        create a new one.
+     */
+    wxAutomationInstance_CreateIfNeeded = 1
+};
+
+
 /**
     @class wxAutomationObject
 
 /**
     @class wxAutomationObject
 
@@ -103,14 +132,23 @@ public:
         Retrieves the current object associated with the specified ProgID, and
         attaches the IDispatch pointer to this object.
 
         Retrieves the current object associated with the specified ProgID, and
         attaches the IDispatch pointer to this object.
 
+        If attaching to an existing object failed and @a flags includes
+        wxAutomationInstance_CreateIfNeeded flag, a new object will be created.
+
         Returns @true if a pointer was successfully retrieved, @false
         otherwise.
         Returns @true if a pointer was successfully retrieved, @false
         otherwise.
+
         Note that this cannot cope with two instances of a given OLE object being
         active simultaneously,
         such as two copies of Excel running. Which object is referenced cannot
         currently be specified.
         Note that this cannot cope with two instances of a given OLE object being
         active simultaneously,
         such as two copies of Excel running. Which object is referenced cannot
         currently be specified.
+
+        @param progId COM ProgID, e.g. "Excel.Application"
+        @param flags The creation flags (this parameters was added in wxWidgets
+            2.9.2)
     */
     */
-    bool GetInstance(const wxString& progId) const;
+    bool GetInstance(const wxString& progId,
+                     int flags = wxAutomationInstance_CreateIfNeeded) const;
 
     /**
         Retrieves a property from this object, assumed to be a dispatch pointer, and
 
     /**
         Retrieves a property from this object, assumed to be a dispatch pointer, and
index 18a83c83598b08a6f1420bf0bb7705a360dbf00c..a9dd3cb48b157fcd8872ace9af5e4b117b7190de 100644 (file)
@@ -196,28 +196,26 @@ void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
  */
 void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event))
 {
  */
 void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event))
 {
-    wxMessageBox(wxT("Please ensure Excel is running, then press OK.\nThe active cell should then say 'wxWidgets automation test!' in bold."));
+    wxMessageBox(wxT("Excel will be started if it is not running after you have pressed OK button.")
+        wxT("\nThe active cell should then say 'wxWidgets automation test!' in bold."),
+        wxT("Excel start"));
 
 
-    wxAutomationObject excelObject, rangeObject;
-    if (!excelObject.GetInstance(wxT("Excel.Application")))
+    wxAutomationObject excelObject;
+    if ( !excelObject.GetInstance(wxT("Excel.Application")) )
     {
     {
-        // Start Excel if it is not running
-        if (!excelObject.CreateInstance(wxT("Excel.Application")))
-        {
-            wxMessageBox(wxT("Could not create Excel object."));
-            return;
-        }
+        wxLogError(wxT("Could not create Excel object."));
+        return;
     }
 
     // Ensure that Excel is visible
     if (!excelObject.PutProperty(wxT("Visible"), true))
     {
     }
 
     // Ensure that Excel is visible
     if (!excelObject.PutProperty(wxT("Visible"), true))
     {
-        wxMessageBox(wxT("Could not make Excel object visible"));
+        wxLogError(wxT("Could not make Excel object visible"));
     }
     const wxVariant workbooksCountVariant = excelObject.GetProperty(wxT("Workbooks.Count"));
     if (workbooksCountVariant.IsNull())
     {
     }
     const wxVariant workbooksCountVariant = excelObject.GetProperty(wxT("Workbooks.Count"));
     if (workbooksCountVariant.IsNull())
     {
-        wxMessageBox(wxT("Could not get workbooks count"));
+        wxLogError(wxT("Could not get workbooks count"));
         return;
     }
     const long workbooksCount = workbooksCountVariant;
         return;
     }
     const long workbooksCount = workbooksCountVariant;
@@ -226,19 +224,19 @@ void MyFrame::OnTest(wxCommandEvent& WXUNUSED(event))
         const wxVariant workbook = excelObject.CallMethod(wxT("Workbooks.Add"));
         if (workbook.IsNull())
         {
         const wxVariant workbook = excelObject.CallMethod(wxT("Workbooks.Add"));
         if (workbook.IsNull())
         {
-            wxMessageBox(wxT("Could not create new Workbook"));
+            wxLogError(wxT("Could not create new Workbook"));
             return;
         }
     }
 
     if (!excelObject.PutProperty(wxT("ActiveCell.Value"), wxT("wxWidgets automation test!")))
     {
             return;
         }
     }
 
     if (!excelObject.PutProperty(wxT("ActiveCell.Value"), wxT("wxWidgets automation test!")))
     {
-        wxMessageBox(wxT("Could not set active cell value."));
+        wxLogError(wxT("Could not set active cell value."));
         return;
     }
     if (!excelObject.PutProperty(wxT("ActiveCell.Font.Bold"), wxVariant(true)) )
     {
         return;
     }
     if (!excelObject.PutProperty(wxT("ActiveCell.Font.Bold"), wxVariant(true)) )
     {
-        wxMessageBox(wxT("Could not put Bold property to active cell."));
+        wxLogError(wxT("Could not put Bold property to active cell."));
         return;
     }
 }
         return;
     }
 }
index f047bda3d48887ba3f986304a6f53f7a25fde5c8..364608fd6b1dbf07b5dee22e4eb1e46ea0521692 100644 (file)
 
 #if wxUSE_OLE_AUTOMATION
 
 
 #if wxUSE_OLE_AUTOMATION
 
-// Report an OLE error to the user via wxLog.
+// Report an OLE error when calling the specified method to the user via wxLog.
 static void
 ShowException(const wxString& member,
               HRESULT hr,
 static void
 ShowException(const wxString& member,
               HRESULT hr,
-              EXCEPINFO *pexcep,
-              unsigned int uiArgErr);
+              EXCEPINFO *pexcep = NULL,
+              unsigned int uiArgErr = 0);
 
 // wxAutomationObject
 
 
 // wxAutomationObject
 
@@ -147,7 +147,7 @@ bool wxAutomationObject::Invoke(const wxString& member, int action,
                                 1 + namedArgCount, LOCALE_SYSTEM_DEFAULT, dispIds);
     if (FAILED(hr))
     {
                                 1 + namedArgCount, LOCALE_SYSTEM_DEFAULT, dispIds);
     if (FAILED(hr))
     {
-        ShowException(member, hr, NULL, 0);
+        ShowException(member, hr);
         delete[] argNames;
         delete[] dispIds;
         return false;
         delete[] argNames;
         delete[] dispIds;
         return false;
@@ -489,40 +489,78 @@ bool wxAutomationObject::GetObject(wxAutomationObject& obj, const wxString& prop
         return false;
 }
 
         return false;
 }
 
+namespace
+{
+
+HRESULT wxCLSIDFromProgID(const wxString& progId, CLSID& clsId)
+{
+    HRESULT hr = CLSIDFromProgID(wxBasicString(progId), &clsId);
+    if ( FAILED(hr) )
+    {
+        wxLogSysError(hr, _("Failed to find CLSID of \"%s\""), progId);
+    }
+    return hr;
+}
+
+void *DoCreateInstance(const wxString& progId, const CLSID& clsId)
+{
+    // get the server IDispatch interface
+    //
+    // NB: using CLSCTX_INPROC_HANDLER results in failure when getting
+    //     Automation interface for Microsoft Office applications so don't use
+    //     CLSCTX_ALL which includes it
+    void *pDispatch = NULL;
+    HRESULT hr = CoCreateInstance(clsId, NULL, CLSCTX_SERVER,
+                                  IID_IDispatch, &pDispatch);
+    if (FAILED(hr))
+    {
+        wxLogSysError(hr, _("Failed to create an instance of \"%s\""), progId);
+        return NULL;
+    }
+
+    return pDispatch;
+}
+
+} // anonymous namespace
+
 // Get a dispatch pointer from the current object associated
 // with a ProgID
 // Get a dispatch pointer from the current object associated
 // with a ProgID
-bool wxAutomationObject::GetInstance(const wxString& progId) const
+bool wxAutomationObject::GetInstance(const wxString& progId, int flags) const
 {
     if (m_dispatchPtr)
         return false;
 
 {
     if (m_dispatchPtr)
         return false;
 
-    HRESULT hr;
     CLSID clsId;
     CLSID clsId;
-    IUnknown * pUnk = NULL;
-
-    wxBasicString unicodeName(progId);
-
-    hr = CLSIDFromProgID((BSTR) unicodeName, &clsId);
+    HRESULT hr = wxCLSIDFromProgID(progId, clsId);
     if (FAILED(hr))
     if (FAILED(hr))
-    {
-        ShowException(progId, hr, NULL, 0);
-        wxLogWarning(wxT("Cannot obtain CLSID from ProgID"));
         return false;
         return false;
-    }
 
 
+    IUnknown *pUnk = NULL;
     hr = GetActiveObject(clsId, NULL, &pUnk);
     if (FAILED(hr))
     {
     hr = GetActiveObject(clsId, NULL, &pUnk);
     if (FAILED(hr))
     {
-        ShowException(progId, hr, NULL, 0);
-        wxLogWarning(wxT("Cannot find an active object"));
+        if ( flags & wxAutomationInstance_CreateIfNeeded )
+        {
+            const_cast<wxAutomationObject *>(this)->
+                m_dispatchPtr = DoCreateInstance(progId, clsId);
+            if ( m_dispatchPtr )
+                return true;
+        }
+        else
+        {
+            wxLogSysError(hr,
+                          _("Cannot get an active instance of \"%s\""), progId);
+        }
+
         return false;
     }
 
     hr = pUnk->QueryInterface(IID_IDispatch, (LPVOID*) &m_dispatchPtr);
     if (FAILED(hr))
     {
         return false;
     }
 
     hr = pUnk->QueryInterface(IID_IDispatch, (LPVOID*) &m_dispatchPtr);
     if (FAILED(hr))
     {
-        ShowException(progId, hr, NULL, 0);
-        wxLogWarning(wxT("Cannot find IDispatch interface"));
+        wxLogSysError(hr,
+                      _("Failed to get OLE automation interface for \"%s\""),
+                      progId);
         return false;
     }
 
         return false;
     }
 
@@ -536,34 +574,15 @@ bool wxAutomationObject::CreateInstance(const wxString& progId) const
     if (m_dispatchPtr)
         return false;
 
     if (m_dispatchPtr)
         return false;
 
-    HRESULT hr;
     CLSID clsId;
     CLSID clsId;
-
-    wxBasicString unicodeName(progId);
-
-    hr = CLSIDFromProgID((BSTR) unicodeName, &clsId);
+    HRESULT hr = wxCLSIDFromProgID(progId, clsId);
     if (FAILED(hr))
     if (FAILED(hr))
-    {
-        ShowException(progId, hr, NULL, 0);
-        wxLogWarning(wxT("Cannot obtain CLSID from ProgID"));
         return false;
         return false;
-    }
 
 
-    // get the server IDispatch interface
-    //
-    // NB: using CLSCTX_INPROC_HANDLER results in failure when getting
-    //     Automation interface for Microsoft Office applications so don't use
-    //     CLSCTX_ALL which includes it
-    hr = CoCreateInstance(clsId, NULL, CLSCTX_SERVER, IID_IDispatch,
-                                (void**)&m_dispatchPtr);
-    if (FAILED(hr))
-    {
-        ShowException(progId, hr, NULL, 0);
-        wxLogWarning(wxT("Could not start an instance of this class."));
-        return false;
-    }
+    const_cast<wxAutomationObject *>(this)->
+        m_dispatchPtr = DoCreateInstance(progId, clsId);
 
 
-    return true;
+    return m_dispatchPtr != NULL;
 }
 
 static void
 }
 
 static void
@@ -576,70 +595,68 @@ ShowException(const wxString& member,
     switch (GetScode(hr))
     {
         case DISP_E_UNKNOWNNAME:
     switch (GetScode(hr))
     {
         case DISP_E_UNKNOWNNAME:
-            message = wxT("Unknown name or named argument.");
+            message = _("Unknown name or named argument.");
             break;
 
         case DISP_E_BADPARAMCOUNT:
             break;
 
         case DISP_E_BADPARAMCOUNT:
-            message = wxT("Incorrect number of arguments.");
+            message = _("Incorrect number of arguments.");
             break;
 
         case DISP_E_EXCEPTION:
             break;
 
         case DISP_E_EXCEPTION:
+            if ( pexcep )
+            {
+                if ( pexcep->bstrDescription )
+                    message << pexcep->bstrDescription << wxS(" ");
+                message += wxString::Format(wxS("error code %u"), pexcep->wCode);
+            }
+            else
             {
             {
-                message = wxT("Error Code (");
-                message << pexcep->wCode ;// unsigned short
-                message += wxT(")");
-                if (pexcep->bstrDescription != NULL)
-                    message += pexcep->bstrDescription;
-                else
-                    message += wxT("<<No Description>>");
+                message = _("Unknown exception");
             }
             break;
 
         case DISP_E_MEMBERNOTFOUND:
             }
             break;
 
         case DISP_E_MEMBERNOTFOUND:
-            message = wxT("Method or property not found.");
+            message = _("Method or property not found.");
             break;
 
         case DISP_E_OVERFLOW:
             break;
 
         case DISP_E_OVERFLOW:
-            message = wxT("Overflow while coercing argument values.");
+            message = _("Overflow while coercing argument values.");
             break;
 
         case DISP_E_NONAMEDARGS:
             break;
 
         case DISP_E_NONAMEDARGS:
-            message = wxT("Object implementation does not support named arguments.");
+            message = _("Object implementation does not support named arguments.");
             break;
 
         case DISP_E_UNKNOWNLCID:
             break;
 
         case DISP_E_UNKNOWNLCID:
-            message = wxT("The locale ID is unknown.");
+            message = _("The locale ID is unknown.");
             break;
 
         case DISP_E_PARAMNOTOPTIONAL:
             break;
 
         case DISP_E_PARAMNOTOPTIONAL:
-            message = wxT("Missing a required parameter.");
+            message = _("Missing a required parameter.");
             break;
 
         case DISP_E_PARAMNOTFOUND:
             break;
 
         case DISP_E_PARAMNOTFOUND:
-            message = wxT("Argument not found, argument.");
-            message << uiArgErr;
+            message.Printf(_("Argument %u not found."), uiArgErr);
             break;
 
         case DISP_E_TYPEMISMATCH:
             break;
 
         case DISP_E_TYPEMISMATCH:
-            message = wxT("Type mismatch, argument.");
-            message << uiArgErr;
+            message.Printf(_("Type mismatch in argument %u."), uiArgErr);
             break;
 
         case ERROR_FILE_NOT_FOUND:
             break;
 
         case ERROR_FILE_NOT_FOUND:
-            message = wxT("The system cannot find the file specified.");
+            message = _("The system cannot find the file specified.");
             break;
 
         case REGDB_E_CLASSNOTREG:
             break;
 
         case REGDB_E_CLASSNOTREG:
-            message = wxT("Class not registered.");
+            message = _("Class not registered.");
             break;
 
         default:
             break;
 
         default:
-            message = wxT("Unknown error occurred. Return value is ");
-            message << hr;
+            message.Printf(_("Unknown error %08x"), hr);
             break;
     }
 
             break;
     }
 
-    wxLogDebug("OLE Automation error in %s: %s", member, message);
+    wxLogError(_("OLE Automation error in %s: %s"), member, message);
 }
 
 #endif // wxUSE_OLE_AUTOMATION
 }
 
 #endif // wxUSE_OLE_AUTOMATION