I have a C++ application and I'm writing a new Outlook add-in, which I think will be with VSTO. I want to have communication between then and I'm trying to figure out the best way to do it. On the MS docs they mention how to expose your COM class to external solutions using RequestComAddInAutomationService
. I am very new to COM but I read some online and got to the following solution. I read that you're supposed to build the Add-in (for x86 as my Outlook version and not AnyCPU), take the created .tlb
file and convert it to .tlh
using the #import
directive, and then #include
the .tlh
file to have the appropriate types.
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();
}
The problem is CoCreateInstance
returns REGDB_E_CLASSNOTREG Class not registered
. The relevant Registry tree looks like that:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0} = FirstOutlookAddIn.AddInUtilities Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\InprocServer32: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\InprocServer32\1.0.0.0: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID{5008A102-08E5-3F59-AADD-03875524CAD0}\ProgId = FirstOutlookAddIn.AddInUtilities
What am I doing wrong? Do I understand correctly the possibility here, having my DLL loading inside Outlook.exe and being able to invoke functions in it from an external application? Thanks in advance!!!