在VC++中使用IManagedAddin加载VSTO VBA加载项
我得到了一个用VBA编写的VSTO Outlook外接程序,并被要求缩短启动时间。我对外接程序和COM对象完全陌生,所以我需要一些帮助。
插件的启动时间从0.2秒到2.0秒不等,如果平均启动时间为>;1000ms,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
解决方案
我所做的是等待OnConnection
回调被激发并启动计时器--您也许能够使用单独的线程,但在我的例子中,由于某些具有线程关联性的功能,处理必须在主线程上完成。OnConnection
将为您提供Outlook.Application
对象。
在计时器回调中(Outlook没有查看),使用CoCreateInstance(CLSID_IManagedAddin, ...)
创建IManagedAddin
COM对象的实例。调用IManagedAddin::Load
。路径的格式必须为file://c:/the/folder/myaddin.vsto
。
为IDTExtensibility2
接口QIIManagedAddin
对象,并使用从本机OnConnection
回调保存的参数调用IDTExtensibility2::OnConnection()
。
OnStartupComplete
的C++实现,请在VSTO加载项上调用OnStartupComplete
。如果没有,您可以稍后再进行。QI forIRibbonExtensibility
,调用IRibbonExtensibility::GetCustomUI
。或者,您可以在C++插件中对Ribbon XML进行硬编码。请注意,如果Outlook在C++外接程序上调用IRibbonExtensibility::GetCustomUI
,并且您必须在有机会运行计时器代码之前将调用委托给VSTO外接程序,则您别无选择,只能立即调用上述代码,而不是在计时器回调中调用,因为GetCustomUI
无法推迟。
ICustomTaskPaneConsumer
接口由您的VSTO插件实现),您的C++插件也必须实现它(除了IDTExtensibility2
和ICustomTaskPaneConsumer
接口)。齐IManagedAddin
为IServiceProvider
接口,使用它调用IServiceProvider::QueryService(GUID_NULL, IID_ICustomTaskPaneConsumer, ...)
。然后调用ICustomTaskPaneConsumer.CTPFactoryAvailable
-请注意,ICustomTaskPaneConsumer
不是来自您的VSTO插件的IDTExtensibility2
接口,而是通过IManagedAddin
接口上的IServiceProvider
对象。
相关文章