【问题标题】:Using IManagedAddin in VC++ to load VSTO VBA add-ins在 VC++ 中使用 IManagedAddin 加载 VSTO VBA 加载项
【发布时间】:2021-11-24 17:48:55
【问题描述】:

我收到了一个用 VBA 编写的 VSTO Outlook 加载项,我的任务是减少启动时间。我对整个外接程序和 COM 对象还很陌生,所以我需要一些帮助。

加载项需要 0.2 秒到 2.0 秒才能启动,如果平均启动时间大于 1000 毫秒,Outlook 会禁用该插件。不幸的是,使用注册表破解来强制启用加载项不是一种选择。我还用一个空插件对其进行了测试,它也可能需要 1.8 秒才能启动。我已经搜索了 SO 和其他此类站点以寻找解决方案,并且在一个涉及使用非托管语言(例如 Delphi 或 C++)编写“存根”的解决方案中,除了加载实际加载项之外什么都不做。应该这样做的接口是IManagedAddin

我的问题是实现这个接口。我在 VC++ 中创建了一个简单的插件实现_IDTExtensibility2 的类在Connect.h 中,如下所示。但是,我不知道如何实现 IManagedAddin::Load 以将我的加载项加载到 Outlook 中,而且似乎没有很多关于此的文档。任何帮助将不胜感激!

编辑:更新以下代码

// Connect.h : Declaration of the CConnect

#pragma once
#include "resource.h"       // main symbols
#include "NativeAddin_i.h"
#include "IManagedAddin.h"
#include <Windows.h>
#include <iostream>



#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

using namespace ATL;

// CConnect

class ATL_NO_VTABLE CConnect :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CConnect, &CLSID_Connect>,
    public IDispatchImpl<IConnect, &IID_IConnect, &LIBID_PixelLLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispatchImpl<_IDTExtensibility2, &__uuidof(_IDTExtensibility2), &LIBID_AddInDesignerObjects, /* wMajor = */ 1, /* wMinor = */ 0>
{
public:
    static Outlook::_Application* outlookApp;
    static ext_ConnectMode connectMode;
    static LPDISPATCH addInInst;
    static SAFEARRAY** customArr;
    static UINT_PTR timerId;

    CConnect()
    {
    }

DECLARE_REGISTRY_RESOURCEID(106)


BEGIN_COM_MAP(CConnect)
    COM_INTERFACE_ENTRY(IConnect)
    COM_INTERFACE_ENTRY2(IDispatch, _IDTExtensibility2)
    COM_INTERFACE_ENTRY(_IDTExtensibility2)
END_COM_MAP()



    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

    static VOID CALLBACK TimerCallback(HWND hWnd, UINT nMsg, UINT timerId, DWORD dwTime)
    {
        KillTimer(NULL, timerId);

        HRESULT hr;
        BSTR manifestUrl = SysAllocString(L"file://path/to/manifest.vsto");

        // load add-in
        IID clsid;
        hr = IIDFromString(OLESTR("{99D651D7-5F7C-470E-8A3B-774D5D9536AC}"), &clsid); // VSTOAddinLoader CLSID

        IID iid;
        hr = IIDFromString(OLESTR("{B9CEAB65-331C-4713-8410-DDDAF8EC191A}"), &iid);
        IManagedAddin* loader;
        hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, iid, (void**)&loader);

        hr = loader->Load(manifestUrl, outlookApp);

        _IDTExtensibility2* ext;
        hr = loader->QueryInterface(IID__IDTExtensibility2, (void**)&ext);

        hr = ext->OnConnection(outlookApp, connectMode, addInInst, customArr);

        MSO::IRibbonExtensibility* ribbon;
        hr = (???)->QueryInterface(MSO::IID_IRibbonExtensibility, (void**)&ribbon);
    }


    STDMETHOD(OnConnection)(LPDISPATCH App, ext_ConnectMode ConnectMode, LPDISPATCH AddInInst, SAFEARRAY * * custom)
    {
        HRESULT hr;
        UINT time = 200;

        Outlook::_Application* app;
        hr = App->QueryInterface(__uuidof(Outlook::_Application), (void**)&app);

        // init static members
        outlookApp = app;
        connectMode = ConnectMode;
        addInInst = AddInInst;
        customArr = custom;

        timerId = SetTimer(NULL, 0, time, (TIMERPROC)&TimerCallback);
        return S_OK;
    }

    STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom)
    {
        return S_OK;
    }

    STDMETHOD(OnAddInsUpdate)(SAFEARRAY * * custom)
    {
        return S_OK;
    }

    STDMETHOD(OnStartupComplete)(SAFEARRAY * * custom)
    {
        return S_OK;
    }

    STDMETHOD(OnBeginShutdown)(SAFEARRAY * * custom)
    {
        return S_OK;
    }
};

OBJECT_ENTRY_AUTO(__uuidof(Connect), CConnect)
Outlook::_Application* CConnect::outlookApp = NULL;
ext_ConnectMode CConnect::connectMode = ext_cm_AfterStartup;
LPDISPATCH CConnect::addInInst = NULL;
SAFEARRAY** CConnect::customArr = NULL;

UINT_PTR CConnect::timerId = 0;
// IManagedAddin.h
#pragma once
#include "resource.h"
#include "NativeAddin_i.h"

struct __declspec(uuid("B9CEAB65-331C-4713-8410-DDDAF8EC191A"))
IManagedAddin : IUnknown
{
public:
    virtual STDMETHOD(Load)(BSTR bstrManifestUrl, LPDISPATCH pdisApplication) = 0;
    virtual STDMETHOD(Unload)() = 0;
};
// pch.h
#ifndef PCH_H
#define PCH_H

// add headers that you want to pre-compile here
#include "framework.h"

#import "libid:AC0714F2-3D04-11D1-AE7D-00A0C90F26F4" raw_interfaces_only, raw_native_types, named_guids, auto_search, no_namespace
#import "libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52" raw_interfaces_only, raw_native_types, named_guids, auto_search, rename_namespace("MSO")
#import "libid:00062FFF-0000-0000-C000-000000000046" raw_interfaces_only, raw_native_types, named_guids, auto_search, rename_namespace("Outlook")

#endif //PCH_H

【问题讨论】:

    标签: c++ outlook outlook-addin


    【解决方案1】:

    我所做的是等到OnConnection 回调触发并启动一个计时器 - 您可能可以使用单独的线程,但在我的情况下,由于某些功能必须在主线程上完成处理线程亲和性。 OnConnection 会给你Outlook.Application 对象。

    在计时器回调中(Outlook 不查看),使用CoCreateInstance(CLSID_IManagedAddin, ...) 创建IManagedAddin COM 对象的实例。致电IManagedAddin::Load。路径的格式必须为 file://c:/the/folder/myaddin.vsto

    QI IDTExtensibility2 接口的IManagedAddin 对象并使用从本机OnConnection 回调保存的参数调用IDTExtensibility2::OnConnection()

    如果 Outlook 已经调用了 OnStartupComplete 的 C++ 实现,请在 VSTO 插件上调用 OnStartupComplete。如果没有,你可以稍后再做。

    联系IRibbonExtensibility 并致电IRibbonExtensibility::GetCustomUI。或者,您可以在 C++ 插件中对功能区 XML 进行硬编码。请注意,如果 Outlook 在您的 C++ 插件上调用 IRibbonExtensibility::GetCustomUI,并且您必须在有机会运行计时器代码之前将调用委托给 VSTO 插件,您别无选择,只能立即调用上述代码,而不是在定时器回调,因为GetCustomUI 不能被推迟。

    如果您正在使用任务窗格(即 ICustomTaskPaneConsumer 接口由您的 VSTO 插件实现),您的 C++ 插件也必须实现它(IDTExtensibility2ICustomTaskPaneConsumer 接口除外)。 QI IManagedAddin 用于IServiceProvider 接口并使用它来调用IServiceProvider::QueryService(GUID_NULL, IID_ICustomTaskPaneConsumer, ...)。然后调用ICustomTaskPaneConsumer.CTPFactoryAvailable - 请注意ICustomTaskPaneConsumer 不是来自您的VSTO 插件的IDTExtensibility2 接口,而是来自IServiceProvider 对象来自IManagedAddin 接口。

    【讨论】:

    • 感谢您的帮助!我似乎仍然无法加载加载项。我在调用loader-&gt;Load(manifestUrl, app) 时收到Exception thrown at 0x00007FFE2D97B8BD (Mso20win32client.dll) in OUTLOOK.EXE: 0xC0000005: Access violation writing location 0x0000000000000000.(上面的代码已更新)。调试器无法在VSTOLoader.dll 中加载符号,所以我也无法单步执行。我怀疑我传递了错误的应用程序对象。
    • outlookApplication从何而来?
    • 另外,请尝试为 Outlook.Application 对它进行 QI,而不是仅使用通用 IDispatch。
    • 好的,我想我已经成功加载了.vsto 文件。在您的第三段中,您向 QI 提到了 IDTExtensibility 的“返回对象”,这个对象应该来自哪里? Load 不返回任何对象..
    • 是的,抱歉,您需要在调用 Load 后对 IManagedAddin 对象的 IDTExtensibility 进行 QI。
    猜你喜欢
    • 1970-01-01
    • 2019-11-26
    • 2020-07-29
    • 2014-05-10
    • 1970-01-01
    • 2017-04-20
    • 2023-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多