From 6eefca4fb7793a8d6bc02e69694735cb3e5fc230 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 26 Nov 2010 13:30:37 +0000 Subject: [PATCH] Allow wxAutomationObject::GetInstance() create new instance if needed. 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 | 6 ++ include/wx/msw/ole/automtn.h | 15 +++- interface/wx/msw/ole/automtn.h | 40 ++++++++- samples/oleauto/oleauto.cpp | 26 +++--- src/msw/ole/automtn.cpp | 147 ++++++++++++++++++--------------- 5 files changed, 152 insertions(+), 82 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 05ca1fdecb..67205aed91 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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. +- 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 ----------------------------------------------------------- @@ -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). +- wxAutomationObject::GetInstance() creates objects on demand (Kolya Kosenko). diff --git a/include/wx/msw/ole/automtn.h b/include/wx/msw/ole/automtn.h index 7ea70f8e4c..63b9414d29 100644 --- a/include/wx/msw/ole/automtn.h +++ b/include/wx/msw/ole/automtn.h @@ -26,6 +26,16 @@ typedef unsigned short* WXBSTR; #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. @@ -44,9 +54,10 @@ public: // 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, diff --git a/interface/wx/msw/ole/automtn.h b/interface/wx/msw/ole/automtn.h index 397703cbe8..c351d20e88 100644 --- a/interface/wx/msw/ole/automtn.h +++ b/interface/wx/msw/ole/automtn.h @@ -6,6 +6,35 @@ // 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 @@ -103,14 +132,23 @@ public: 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. + 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 diff --git a/samples/oleauto/oleauto.cpp b/samples/oleauto/oleauto.cpp index 18a83c8359..a9dd3cb48b 100644 --- a/samples/oleauto/oleauto.cpp +++ b/samples/oleauto/oleauto.cpp @@ -196,28 +196,26 @@ void MyFrame::OnAbout(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)) { - 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()) { - wxMessageBox(wxT("Could not get workbooks count")); + wxLogError(wxT("Could not get workbooks count")); 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()) { - 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!"))) { - 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)) ) { - wxMessageBox(wxT("Could not put Bold property to active cell.")); + wxLogError(wxT("Could not put Bold property to active cell.")); return; } } diff --git a/src/msw/ole/automtn.cpp b/src/msw/ole/automtn.cpp index f047bda3d4..364608fd6b 100644 --- a/src/msw/ole/automtn.cpp +++ b/src/msw/ole/automtn.cpp @@ -56,12 +56,12 @@ #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, - EXCEPINFO *pexcep, - unsigned int uiArgErr); + EXCEPINFO *pexcep = NULL, + unsigned int uiArgErr = 0); // wxAutomationObject @@ -147,7 +147,7 @@ bool wxAutomationObject::Invoke(const wxString& member, int action, 1 + namedArgCount, LOCALE_SYSTEM_DEFAULT, dispIds); if (FAILED(hr)) { - ShowException(member, hr, NULL, 0); + ShowException(member, hr); delete[] argNames; delete[] dispIds; return false; @@ -489,40 +489,78 @@ bool wxAutomationObject::GetObject(wxAutomationObject& obj, const wxString& prop 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 -bool wxAutomationObject::GetInstance(const wxString& progId) const +bool wxAutomationObject::GetInstance(const wxString& progId, int flags) const { if (m_dispatchPtr) return false; - HRESULT hr; CLSID clsId; - IUnknown * pUnk = NULL; - - wxBasicString unicodeName(progId); - - hr = CLSIDFromProgID((BSTR) unicodeName, &clsId); + HRESULT hr = wxCLSIDFromProgID(progId, clsId); if (FAILED(hr)) - { - ShowException(progId, hr, NULL, 0); - wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); return false; - } + IUnknown *pUnk = NULL; 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(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)) { - ShowException(progId, hr, NULL, 0); - wxLogWarning(wxT("Cannot find IDispatch interface")); + wxLogSysError(hr, + _("Failed to get OLE automation interface for \"%s\""), + progId); return false; } @@ -536,34 +574,15 @@ bool wxAutomationObject::CreateInstance(const wxString& progId) const if (m_dispatchPtr) return false; - HRESULT hr; CLSID clsId; - - wxBasicString unicodeName(progId); - - hr = CLSIDFromProgID((BSTR) unicodeName, &clsId); + HRESULT hr = wxCLSIDFromProgID(progId, clsId); if (FAILED(hr)) - { - ShowException(progId, hr, NULL, 0); - wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); 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(this)-> + m_dispatchPtr = DoCreateInstance(progId, clsId); - return true; + return m_dispatchPtr != NULL; } static void @@ -576,70 +595,68 @@ ShowException(const wxString& member, 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: - message = wxT("Incorrect number of arguments."); + message = _("Incorrect number of arguments."); 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("<>"); + message = _("Unknown exception"); } break; case DISP_E_MEMBERNOTFOUND: - message = wxT("Method or property not found."); + message = _("Method or property not found."); break; case DISP_E_OVERFLOW: - message = wxT("Overflow while coercing argument values."); + message = _("Overflow while coercing argument values."); 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: - message = wxT("The locale ID is unknown."); + message = _("The locale ID is unknown."); break; case DISP_E_PARAMNOTOPTIONAL: - message = wxT("Missing a required parameter."); + message = _("Missing a required parameter."); 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: - message = wxT("Type mismatch, argument."); - message << uiArgErr; + message.Printf(_("Type mismatch in argument %u."), uiArgErr); 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: - message = wxT("Class not registered."); + message = _("Class not registered."); break; default: - message = wxT("Unknown error occurred. Return value is "); - message << hr; + message.Printf(_("Unknown error %08x"), hr); break; } - wxLogDebug("OLE Automation error in %s: %s", member, message); + wxLogError(_("OLE Automation error in %s: %s"), member, message); } #endif // wxUSE_OLE_AUTOMATION -- 2.45.2