Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-81545

QAxBase::querySubObject cannot find methods or properties of MS Word when working documents with VBA macros

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Reported
    • Priority: P4: Low
    • Resolution: Unresolved
    • Affects Version/s: 5.12.6
    • Fix Version/s: None
    • Component/s: ActiveX Support
    • Labels:
      None
    • Environment:
      Windows 7/10, Office 2010
    • Platform/s:
      Windows

      Description

      Code:

      CoInitialize(NULL);
      QAxObject *app = new QAxObject("Word.Application");
      app->setProperty("Visible", QVariant(true));
      QAxObject *documents = app->querySubObject("Documents");
      QAxObject *document = documents->querySubObject("Open(const QString&)", QVariant("z:/a.doc"));
      QAxObject *window = document->querySubObject("ActiveWindow");
      

      On some system, the last line will return a nullptr and throw out an error

      QAxBase::querySubObject: ActiveWindow: Method or property is not of interface type in ({00020906-0000-0000-C000-000000000046})

      I've tried on 20+ PCs, only a few have this problem.

      On some of the affected systems, adding qApp->processEvents() or waiting a few moments before last line will solve this bug. The others still say ActiveWindow is not found.

      If the line in a.doc was deleted, the code works again on all previously affected systems. But I can't delete it in the real life document.

      I found out that on the affected systems, the disp->GetIDsOfNames in QAxMetaObject::dispIDofName returned VBA_E_INVALIDSTATE (0x800A8029) or VBA_E_CANTEXITDESIGNMODE (0x800ADF21), and the dispid was set to DISPID_VALUE

       

      // qaxbase.cpp 
      inline DISPID QAxMetaObject::dispIDofName(const QByteArray &name, IDispatch *disp)
      {
          DISPID dispid = dispIDs.value(name, DISPID_UNKNOWN);
          if (dispid == DISPID_UNKNOWN) {
              // get the Dispatch ID from the object
              QString unicodeName = QLatin1String(name);
              OLECHAR *names = reinterpret_cast<wchar_t *>(const_cast<ushort *>(unicodeName.utf16()));
              disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid);
              if (dispid != DISPID_UNKNOWN)
                  dispIDs.insert(name, dispid);
          }
          return dispid;
      }
      

       

      Checking the returned value is not enough, because the document will be still in an invalid state. Googling 0x800ADF21 and 0x800A8029 will show that some other people suffered the same problem.

      Although the IDispatch::GetIDsOfNames returns VBA_E_INVALIDSTATE or VBA_E_CANTEXITDESIGNMODE, I found a way to get id from the ITypeInfo interface. Here is my ugly patch

      --- qaxbase.cpp	2019-11-05 15:47:56.878410600 +0800
      +++ qaxbase.cpp	2020-01-21 13:51:15.094494100 +0800
      @@ -219,8 +219,28 @@
               // get the Dispatch ID from the object
               QString unicodeName = QLatin1String(name);
               OLECHAR *names = reinterpret_cast<wchar_t *>(const_cast<ushort *>(unicodeName.utf16()));
      -        disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid);
      -        if (dispid != DISPID_UNKNOWN)
      +        HRESULT hres = disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid);
      +        // VBA_E_INVALIDSTATE or VBA_E_CANTEXITDESIGNMODE
      +        if (hres == static_cast<HRESULT>(0x800A8029) || hres == static_cast<HRESULT>(0x800ADF21)) {
      +            ITypeInfo *typeInfo;
      +            if (disp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeInfo) == S_OK) {
      +                hres = typeInfo->GetIDsOfNames(&names, 1, &dispid);
      +                if (hres == E_NOTIMPL) {
      +                    BSTR members[32];
      +                    UINT c;
      +                    for (DISPID id = 0; id <= 65536; id++) {
      +                        if (typeInfo->GetNames(id, members, 32, &c) == S_OK) {
      +                            if (wcscmp(names, members[0]) == 0) {
      +                                dispid = id;
      +                                hres = S_OK;
      +                                break;
      +                            }
      +                        }
      +                    }
      +                }
      +            }
      +        }
      +        if (hres == S_OK && dispid != DISPID_UNKNOWN)
                   dispIDs.insert(name, dispid);
           }
           return dispid;
      

       

        Attachments

        1. a.doc
          32 kB
        2. qtbug81545.zip
          19 kB

          Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            Activity

              People

              Assignee:
              kleint Friedemann Kleint
              Reporter:
              xuzhen xuzhen
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Dates

                Created:
                Updated:

                  Gerrit Reviews

                  There are no open Gerrit changes