Search code examples
c++comoffice-addinsexcel-2013office-2013

C++ Office Excel Plugin Range QueryInterface "E_NOINTERFACE" Problem


i created a plugin for ms office,it's works fine on office 2019 Excel & wps office 2019 ET,but it has problems on ms office 2013.

here is the describe:

        rst = disp->QueryInterface(__uuidof(Excel::_Worksheet), (void**)&sheet);
        disp->Release();
        Excel::Range* rawRange = nullptr;
        Excel::IRange* rows = nullptr;
        rst = sheet->get_Rows(&rawRange);
        rst = rawRange ->QueryInterface( __uuidof(Excel::IRange), (void**)&rows);

the get_Rows works fine and returns S_OK,but Range::QueryInterface returns E_NOINTERFACE,that's strange! I can not understand, is there anything i missed?

there are many positions called the QueryInterface method, but only the Range type query the IRange type will fail.

Office 2019 & WPS 2010 works fine,already reinstalled office 2013, but still no work. Maybe a Microsoft bug?


Solution

  • as @Joseph Willcoxson says, the Range type did not implement the IRange interface but it implemented the features, we can use IDispatch interface Invoke method to do the same thing. and I'd like to share my utility code.

    template<class T, size_t N>
    constexpr size_t ArrayLen(T(&ref)[N]) {
        return N;
    }
    
    enum class RangeDispID : DISPID {
        // Props
        NumberFormat    = 0x000000c1,
        Column          = 0x000000f0,
        Row             = 0x00000101,
        Rows            = 0x00000102,
        Columns         = 0x000000f1,
        Text            = 0x0000008a,
        Item            = 0x000000aa,
        Cells           = 0x000000ee,
        Count           = 0x00000076,
        Value2          = 0x0000056c,
        // Methods
        Select          = 0x000000eb,
        Find            = 0x0000018e,
        AutoFilter      = 0x00000319,
        Activate        = 0x00000130,
    };
    
    enum class WindowDispID : DISPID {
        FreezePanes     = 0x0000028a
    };
    
    enum class WorksheetDispID : DISPID {
        Rows            = 0x00000102,
        Range           = 0x000000c5,
        UsedRange       = 0x0000019c,
    };
    
    template<class T>
    struct DispIDBridge {};
    
    template<>
    struct DispIDBridge<RangeDispID> {
        using DispIDType = RangeDispID;
        using UUIDType = Excel::IRange;
        DispIDBridge(DispIDType id) : id_(id){}
        operator DispIDType() const { return id_; }
        operator DISPID() const { return (DISPID)id_; }
    private:
        DispIDType id_;
    };
    
    template<>
    struct DispIDBridge<WindowDispID> {
        using DispIDType = WindowDispID;
        using UUIDType = Excel::IWindow;
        DispIDBridge(DispIDType id) : id_(id) {}
        operator DispIDType() const { return id_; }
        operator DISPID() const { return (DISPID)id_; }
    private:
        DispIDType id_;
    };
    
    template<>
    struct DispIDBridge<WorksheetDispID> {
        using DispIDType = WorksheetDispID;
        using UUIDType = Excel::_Worksheet;
        DispIDBridge(DispIDType id) : id_(id) {}
        operator DispIDType() const { return id_; }
        operator DISPID() const { return (DISPID)id_; }
    private:
        DispIDType id_;
    };
    
    template<class T>
    HRESULT DispSetProperty(IDispatch* obj, T id, CComVariant& val) {
        DISPID dispid = DISPID_PROPERTYPUT;
        DISPPARAMS params; {
            params.cNamedArgs = 1;
            params.rgdispidNamedArgs = &dispid;
            params.cArgs = 1;
            params.rgvarg = &val;
        }
        UINT errIn;
        auto rst = obj->Invoke(
            (DISPID)id,
            __uuidof(DispIDBridge<T>::UUIDType),
            chs_lcid,
            DISPATCH_PROPERTYPUT,
            &params,
            nullptr,
            nullptr,
            &errIn
        );
        return rst;
    }
    
    template<class T>
    CComVariant DispGetProperty(IDispatch* obj, T id, CComVariant* args = nullptr, int argc = 0) {
        if (argc > 1) {
            std::reverse(args, args + argc);
        }
        CComVariant ret;
        DISPPARAMS params; {
            params.cNamedArgs = 0;
            params.rgdispidNamedArgs = nullptr;
            params.cArgs = argc;
            params.rgvarg = args;
        }
        UINT errIn;
        auto rst = obj->Invoke(
            (DISPID)id,
            __uuidof(DispIDBridge<T>::UUIDType),
            chs_lcid,
            DISPATCH_PROPERTYGET,
            &params,
            &ret,
            nullptr,
            &errIn
        );
        if (rst == S_OK) {
            return ret;
        }
        else {
            return CComVariant();
        }
    }
    
    template<class T>
    HRESULT DispCallMethod(IDispatch* obj, T id, CComVariant* args, int argc) {
        if (argc > 1) {
            std::reverse(args, args + argc);
        }
        DISPPARAMS dispParams{};
        dispParams.cArgs = argc;
        dispParams.rgvarg = args;
        UINT errIn;
        auto rst = obj->Invoke(
            (DISPID)id,
            __uuidof(DispIDBridge<T>::UUIDType),
            chs_lcid,
            DISPATCH_METHOD,
            &dispParams,
            nullptr,
            nullptr,
            &errIn
        );
        return rst;
    }
    
    template<class T>
    CComVariant DispCallMethodWithRet(IDispatch* obj, T id, CComVariant* args, int argc) {
        if (argc > 1) {
            std::reverse(args, args + argc);
        }
        DISPPARAMS dispParams{};
        dispParams.cArgs = argc;
        dispParams.rgvarg = args;
        UINT errIn;
        CComVariant ret;
        auto rst = obj->Invoke(
            (DISPID)id,
            __uuidof(DispIDBridge<T>::UUIDType),
            chs_lcid,
            DISPATCH_METHOD,
            &dispParams,
            &ret,
            nullptr,
            &errIn
        );
        assert(rst == S_OK);
        return ret;
    }
    

    we can use it in this way

            CComVariant top5Row; {
                CComVariant args[] = {cellLoc};
                top5Row = DispGetProperty(sheet, WorksheetDispID::Range, args, ArrayLen(args));
            }