wxAutomationObject Helpers Topic is solved

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4183
Joined: Sun Jan 03, 2010 5:45 pm

wxAutomationObject Helpers

Post by PB »

Three utility functions (adapted from wxAutoExcel) aiming to help with the unpleasant task of debugging OLE Automation with wxAutomationObject.

wxautomationobject_helpers.h

Code: Select all

///////////////////////////////////////////////////////////////////////////////
// Name:        wxautomationobject_helpers.h
// Purpose:     Helper functions for wxAutomationObject
// Author:      PB
// Created:     2020-07-12
// Copyright:   (c) 2020 PB
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef WXAUTOMATIONOBJECT_HELPERS
#define WXAUTOMATIONOBJECT_HELPERS

#include <wx/wx.h>

#if defined(__WXMSW__)

#include <wx/msw/ole/automtn.h>

// LCID for US English
extern const WXLCID wxLCIDEnglishUS;

// Returns name of the actual object based on object's IDispatch->GetTypeInfo
// or empty string when the name could not be retrieved
wxString wxGetAutomationObjectName(const wxAutomationObject& object,
                                   WXLCID lcid = wxLCIDEnglishUS,
                                   bool stripUnderscores = false);

// Returns list of properties and methods for given wxAutomationObject
bool wxGetAutomationObjectPropertyAndMethodNames(const wxAutomationObject& object,
                                                 wxArrayString& propertyNames,
                                                 wxArrayString& methodNames,
                                                 bool includeHidden = false);

// Uses wxLogDebug to dump information about wxVariant
void wxLogDebugAutomationVariant(const wxVariant& v, size_t maxItemsInList = 30);

#endif // #if  defined(__WXMSW__)

#endif // WXAUTOMATIONOBJECT_HELPERS
wxautomationobject_helpers.cpp

Code: Select all

///////////////////////////////////////////////////////////////////////////////
// Name:        wxautomationobject_helpers.cpp
// Purpose:     Helper functions for wxAutomationObject
// Author:      PB
// Created:     2020-07-12
// Copyright:   (c) 2020 PB
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////


#include <wx/wx.h>

#if defined(__WXMSW__)

#include "wxautomationobject_helpers.h"

#include <wx/msw/private/comptr.h>

// LCID for US English
const WXLCID wxLCIDEnglishUS = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);


// Returns name of the actual object based on object's IDispatch->GetTypeInfo
// or empty string when the name could not be retrieved
wxString wxGetAutomationObjectName(const wxAutomationObject& object,
                                   WXLCID lcid, bool stripUnderscores)
{
    wxCHECK_MSG(object.GetDispatchPtr(), wxEmptyString, "object does not contain a valid IDispatch pointer");

    wxCOMPtr<ITypeInfo> typeInfo;
    IDispatch* dispatch = (IDispatch*)object.GetDispatchPtr();
    HRESULT hr = dispatch->GetTypeInfo(0, lcid, &typeInfo);

    if ( FAILED(hr) )
    {
        wxLogApiError("IDispatch::GetTypeInfo()", hr);
        return wxEmptyString;
    }

    BSTR bName = nullptr;
    wxString name;
    hr = typeInfo->GetDocumentation(MEMBERID_NIL, &bName, nullptr, nullptr, nullptr);

    if ( FAILED(hr) )
    {
        wxLogApiError("ITypeInfo::GetDocumentation()", hr);
        return wxEmptyString;
    }

    name = bName;
    ::SysFreeString(bName);

    if ( stripUnderscores )
        name.Replace("_", wxEmptyString);

    return name;
}

// Returns list of properties and methods for the given wxAutomationObject
bool wxGetAutomationObjectPropertyAndMethodNames(const wxAutomationObject& object,
                                                 wxArrayString& propertyNames,
                                                 wxArrayString& methodNames,
                                                 bool includeHidden)
{
    wxCHECK_MSG(object.GetDispatchPtr(), false, "object does not contain a valid IDispatch pointer");

    HRESULT hr = S_OK;
    wxCOMPtr<ITypeInfo> typeInfo;
    IDispatch* dispatch = (IDispatch*)object.GetDispatchPtr();
    TYPEATTR* typeAttr = NULL;

    hr = dispatch->GetTypeInfo(0, 0, &typeInfo);
    if ( FAILED(hr) )
    {
        wxLogApiError("IDispatch::GetTypeInfo()", hr);
        return false;
    }

    hr = typeInfo->GetTypeAttr(&typeAttr);
    if ( FAILED(hr) )
    {
        wxLogApiError("ITypeInfo::GetTypeAttr()", hr);
        return false;
    }

    for ( WORD i = 0; i < typeAttr->cFuncs; i++ )
    {
        FUNCDESC* funcDesc = NULL;
        BSTR bName = NULL;

        hr = typeInfo->GetFuncDesc(i, &funcDesc);
        if ( FAILED(hr) )
        {
            wxLogApiError("ITypeInfo::GetFuncDesc()", hr);
            typeInfo->ReleaseTypeAttr(typeAttr);
            return false;
        }

        hr = typeInfo->GetDocumentation(funcDesc->memid, &bName, NULL, NULL, NULL);
        if ( FAILED(hr) )
        {
            wxLogApiError("ITypeInfo::GetDocumentation()", hr);
            typeInfo->ReleaseFuncDesc(funcDesc);
            typeInfo->ReleaseTypeAttr(typeAttr);
            return false;
        }

        if ( ((funcDesc->wFuncFlags & FUNCFLAG_FHIDDEN) != FUNCFLAG_FHIDDEN)
             || includeHidden )
        {
            wxString name(bName);

            switch  ( funcDesc->invkind )
            {
                case INVOKE_FUNC:
                    methodNames.push_back(name);
                    break;
                case INVOKE_PROPERTYGET:
                case INVOKE_PROPERTYPUT:
                case INVOKE_PROPERTYPUTREF:
                    // avoid adding the same property name for different INVOKEKINDs
                    if ( !propertyNames.empty() && propertyNames.Last().IsSameAs(name, false) )
                        break;

                    propertyNames.push_back(name);
                    break;
                default:
                    wxFAIL_MSG("Unknown INVOKEKIND value");
            }
        }

        ::SysFreeString(bName);
        typeInfo->ReleaseFuncDesc(funcDesc);
    }

    typeInfo->ReleaseTypeAttr(typeAttr);
    return true;
}

// Uses wxLogDebug to dump information about wxVariant
void wxLogDebugAutomationVariant(const wxVariant& v, size_t maxItemsInList)
{
    const wxString type = v.GetType();

    wxString info;
    const wxString& name = v.GetName();

    if ( type == wxS("arrstring") )
    {
        wxArrayString as = v.GetArrayString();
        info.Printf(wxS("variant type: \"%s\", element count: %zu, name: \"%s\"."),
            type, as.size(), name);
        wxLogDebug(wxS("%s"), info);
        for (size_t i = 0; i < as.size(); i++)
        {
            info.Printf(wxS("   string #%zu value: \"%s\""), i, as[i]);
            if ( i == maxItemsInList )
            {
                wxLogDebug(wxS("And %zu more strings"), as.size() - i);
                break;
            }
            else
                wxLogDebug(wxS("%s"), info);
        }
        return;
    }
    if ( type == wxS("list") )
    {
        info.Printf(wxS("Variant type: \"%s\", element count: %zu, name: \"%s\"."),
            type, v.GetCount(), name);
        wxLogDebug(wxS("%s"), info);

        for ( size_t i = 0; i < v.GetCount(); i++ )
        {
            if ( i == maxItemsInList )
            {
                wxLogDebug(wxS("And %zu more variants"), v.GetCount() - i);
                break;
            }
            else
            {
                const wxVariant& vTmp = v[i];
                info.Printf(wxS("   variant #%zu type: \"%s\", value: \"%s\", name: \"%s\"."),
                    i, vTmp.GetType(), vTmp.MakeString(), vTmp.GetName());
                wxLogDebug(wxS("%s"), info);
            }
        }
        return;
    }

    if ( type == wxS("void*") && v.GetVoidPtr() != NULL )
    {
        wxString automationName;
        wxAutomationObject object;
        IDispatch* dispatch = (IDispatch*)v.GetVoidPtr();

        dispatch->AddRef();
        object.SetDispatchPtr(dispatch);
        info.Printf(wxS("variant type: \"IDispatch - %s\", value: \"%s\", name: \"%s\"."),
            wxGetAutomationObjectName(false), v.MakeString(), name);
    }
    else
    {
        info.Printf(wxS("variant type: \"%s\", value: \"%s\", name: \"%s\"."),
            type, v.MakeString(), name);
    }

    wxLogDebug(wxS("%s"), info);
}

#endif // #if  defined(__WXMSW__)
EDIT (2020-07-22): Updated wxGetAutomationObjectName() which now on error returns an empty string and uses wxLogApiError() to print what went wrong.
Attachments
wxautomationobject_helpersV2.zip
(2.62 KiB) Downloaded 448 times
Post Reply