c # — Взаимодействие между надстройкой Outlook VSTO и внешним приложением C ++

У меня есть приложение на C ++, и я пишу новую надстройку Outlook, которая, я думаю, будет с VSTO. Я хочу иметь связь между тем, и я пытаюсь найти лучший способ сделать это. На MS документы они упоминают, как выставить ваш COM-класс внешним решениям, используя RequestComAddInAutomationService, Я очень новичок в COM, но я прочитал некоторые онлайн и получил следующее решение. Я прочитал, что вы должны построить надстройку (для x86, как моя версия Outlook, а не AnyCPU), возьмите созданный .tlb файл и преобразовать его в .tlh с использованием #import директива, а затем #include .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!";
}
}
}

main.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
Компьютер \ 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} \ InprocServer32 \ 1.0.0.0
Компьютер \ HKEY_LOCAL_MACHINE \ SOFTWARE \ WOW6432Node \ Classes \ CLSID {5008A102-08E5-3F59-AADD-03875524CAD0} \ ProgId = FirstOutlookAddIn.AddInUtilities

Что я делаю неправильно? Правильно ли я понимаю, что у меня есть возможность загрузки этой библиотеки из Outlook.exe и возможности вызова функций из внешнего приложения?
Заранее спасибо!!!

1

Решение

Вы не должны создавать экземпляр этого класса COM из своего приложения C ++ — создайте экземпляр Outlook.Application объект, использование Application.COMAddins коллекция, чтобы добраться до вашего дополнения, а затем использовать COMAddin.Object свойство для извлечения интерфейса, реализованного вашим надстройкой.
Смотрите, например, https://blogs.msdn.microsoft.com/andreww/2007/01/15/vsto-add-ins-comaddins-and-requestcomaddinautomationservice/

1

Другие решения

После некоторого рытья вокруг я понял это. Я использую Microsoft Пример CppAutomateOutlook

Он имеет 2 варианта реализации, один из которых использует интеллектуальные указатели COM (например, spMail->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
}
1

По вопросам рекламы [email protected]