【问题标题】:Interaction between an Outlook VSTO add-in and an external C++ applicationOutlook VSTO 加载项与外部 C++ 应用程序之间的交互
【发布时间】:2018-09-19 05:05:11
【问题描述】:

我有一个 C++ 应用程序,我正在编写一个新的 Outlook 加载项,我认为它将与 VSTO 一起使用。我想在那之间进行交流,我正在努力找出最好的方法。在MS docs 中,他们提到了如何使用RequestComAddInAutomationService 将您的COM 类公开给外部解决方案。我对 COM 很陌生,但我在网上阅读了一些内容并得到了以下解决方案。我读到您应该构建加载项(对于 x86 作为我的 Outlook 版本而不是 AnyCPU),获取创建的 .tlb 文件并使用 #import 指令将其转换为 .tlh,然后使用 @987654330 @.tlh 文件以具有适当的类型。

ThisAddin.cs

namespace FirstOutlookAddIn
{

public partial class ThisAddIn
{
    Outlook.Inspectors inspectors;
    private AddInUtilities gUtilities;

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        inspectors = this.Application.Inspectors;
        inspectors.NewInspector +=
        new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
    }

    void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
    {
        Outlook.MailItem mailItem = Inspector.CurrentItem as Outlook.MailItem;
        if (mailItem != null)
        {
            if (mailItem.EntryID == null)
            {
                gUtilities.SetMailItem(mailItem);
                mailItem.Subject = "This text was added by using code";
                mailItem.Body = "This text was added by using code";
            }

        }
    }

    protected override object RequestComAddInAutomationService()
    {
        if (gUtilities == null)
            gUtilities = new AddInUtilities();

        return gUtilities;
    }

    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
        // Note: Outlook no longer raises this event. If you have code that 
        //    must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
    }

    #region VSTO generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InternalStartup()
    {
        this.Startup += new System.EventHandler(ThisAddIn_Startup);
        this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
    }

    #endregion
}

}

IAddInUtilities.cs

using System.Runtime.InteropServices;
namespace FirstOutlookAddIn
{
    [ComVisible(true)]
    public interface IAddInUtilities
    {
        void MyExportedFunction();
    }
}

AddInUtilities.cs

using Outlook = Microsoft.Office.Interop.Outlook;
using System.Runtime.InteropServices;
namespace FirstOutlookAddIn
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class AddInUtilities : StandardOleMarshalObject, IAddInUtilities
    {
        Outlook.MailItem globalMailItem;
        public void SetMailItem(Outlook.MailItem item) => globalMailItem = item;
        public void MyExportedFunction()
        {
            globalMailItem.Body = "I was called from outside!";
        }
    }
}

ma​​in.cpp

//#import "FirstOutlookAddIn.tlb" named_guids raw_interfaces_only

#include <iostream>
struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive-
#include <Objbase.h>
#include "Debug\FirstOutlookAddIn.tlh"

int main() {
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    FirstOutlookAddIn::IAddInUtilities* pIFace;
    // create the object and obtain a pointer to the sought interface
    auto res = CoCreateInstance(
        FirstOutlookAddIn::CLSID_AddInUtilities,
        nullptr,
        CLSCTX_LOCAL_SERVER,
        FirstOutlookAddIn::IID_IAddInUtilities,
        (LPVOID*)&pIFace);
    if (res != S_OK)
    {
        std::cout << "Failed with: " << res;
    }
    auto res1 = pIFace->MyExportedFunction(); // use the object
    std::cout << "Res: " << res1;
    pIFace->Release(); // free the object
    CoUninitialize();
}

问题是CoCreateInstance 返回REGDB_E_CLASSNOTREG Class not registered。相关的注册表树如下所示:

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0} = FirstOutlookAddIn.AddInUtilities 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\InprocServer32: 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\InprocServer32\1.0.0.0: 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\ProgId = FirstOutlookAddIn.AddInUtilities

我做错了什么?我是否正确理解这里的可能性,将我的 DLL 加载到 Outlook.exe 中并能够从外部应用程序调用其中的函数? 提前谢谢!!!

【问题讨论】:

    标签: c# c++ outlook com vsto


    【解决方案1】:

    您不应该从您的 C++ 应用程序创建该 COM 类的实例 - 创建一个 Outlook.Application 对象的实例,使用 Application.COMAddins 集合访问您的插件,然后使用 COMAddin.Object 属性检索由您的插件实现的接口。
    例如,请参阅https://blogs.msdn.microsoft.com/andreww/2007/01/15/vsto-add-ins-comaddins-and-requestcomaddinautomationservice/

    【讨论】:

    • 您的评论帮助我找到了解决方案,但您在我询问 C++ API 时回答了 C#
    • @Hila's Master API 还是一样的——Outlook 不知道也不关心它是从 C++ 还是 C# 调用的。
    • 也许逻辑是一样的,但我不会说API是相似的。看我加的回复,跟我感觉很不一样
    • API 是一样的。管道是C++。这就是为什么它看起来不同。看看我的回答:它描述了你的 C++ 代码。
    • 好吧,很抱歉,但我想我的问题主要是关于要使用的正确 C++ 特定 API 是什么,以及确保我理解逻辑。 C++ API 缺乏文档,所以这是一个挑战。不用担心,感谢您的回复
    【解决方案2】:

    经过一番挖掘,我想通了。我正在使用微软的CppAutomateOutlook example

    它有 2 个实现选项,一个是使用 COM 的智能指针(例如 spMail-&gt;Subject = _bstr_t(L"Feedback of All-In-One Code Framework");),一个是使用原始的 IDispatch 接口。我使用了第二个选项并将CoCreateInstance 修改为GetActiveObject,因此我可以与已经运行的Outlook 实例进行交互。这是我当前的代码:

    DWORD WINAPI AutomateOutlookByCOMAPI(LPVOID lpParam)
    {
        // Initializes the COM library on the current thread and identifies 
        // the concurrency model as single-thread apartment (STA). 
        // [-or-] CoInitialize(NULL);
        // [-or-] CoCreateInstance(NULL);
        CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    
        // Define vtMissing for optional parameters in some calls.
        VARIANT vtMissing;
        vtMissing.vt = VT_EMPTY;
    
        // Get CLSID of the server
        CLSID clsid;
        HRESULT hr;
    
        // Option 1. Get CLSID from ProgID using CLSIDFromProgID.
        LPCOLESTR progID = L"Outlook.Application";
        hr = CLSIDFromProgID(progID, &clsid);
        if (FAILED(hr))
        {
            wprintf(L"CLSIDFromProgID(\"%s\") failed w/err 0x%08lx\n", progID, hr);
            return 1;
        }
        // Option 2. Build the CLSID directly.
        /*const IID CLSID_Application = 
        {0x0006F03A,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
        clsid = CLSID_Application;*/
    
        // Get the IDispatch interface of the running instance
    
        IUnknown *pUnk = NULL;
        IDispatch *pOutlookApp = NULL;
        hr = GetActiveObject(
            clsid, NULL, (IUnknown**)&pUnk
        );
    
        if (FAILED(hr))
        {
            wprintf(L"GetActiveObject failed with w/err 0x%08lx\n", hr);
            return 1;
        }
    
        hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pOutlookApp);
        if (FAILED(hr))
        {
            wprintf(L"QueryInterface failed with w/err 0x%08lx\n", hr);
            return 1;
        }
    
        _putws(L"Outlook.Application is found");
    
        IDispatch *comAddins = NULL;
        {
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_PROPERTYGET, &result, pOutlookApp, L"COMAddins", 0);
            comAddins = result.pdispVal;
        }
    
        IDispatch *myAddin = NULL;
        {
            VARIANT x;
            x.vt = VT_BSTR;
            x.bstrVal = SysAllocString(L"FirstOutlookAddIn");
    
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_METHOD, &result, comAddins, L"Item", 1, x);
            myAddin = result.pdispVal;
    
            VariantClear(&x);
        }
    
        IDispatch *myAddinObj = NULL;
        {
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_PROPERTYGET, &result, myAddin, L"Object", 0);
            myAddinObj = result.pdispVal;
        }
    
        {
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_METHOD, &result, myAddinObj, L"MyExportedFunction", 0);
        }
    
        // ... Cleanup code
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 2020-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-04
      • 1970-01-01
      相关资源
      最近更新 更多