So I have a VSTO addin for excel and I want to call code I have written taking advantage of some automation. I wrote in c++ using the import of lib files methods specified here:
C++ app automates Excel (CppAutomateExcel)
So I have an extension method:
public static bool isEmbeded(this Excel.Workbook Book)
{
return dllFuncs.IsEmbeded(Book);
}
And a dll imported function :
namespace dllFuncs
{
#region IsEmbeded
[DllImport("test.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool IsEmbeded(Excel.Workbook Book);
#endregion
}
That corresponds to an exported function that takes in an IUnknown
pointer and does a query interface on it to determine if it is an excel workbook, a word document, or a powerpoint presentation.
c++ code header:
bool WINAPI IsEmbeded(IUnknown* pOfficeDocument);
class EmbededHelperFunctions
{
public:
static bool isEmbeded(IUnknown* pOfficeDocument);
private:
static bool isEmbededHelper(IUnknown* pOfficeDocument);
EmbededHelperFunctions() = delete;
}
c++ cpp code:
bool WINAPI IsEmbeded(IUnknown* pOfficeDocument)
{
return EmbededHelperFunctions::isEmbeded(pOfficeDocument);
}
bool EmbededHelperFunctions::isEmbededHelper(IUnknown* pOfficeDocument)
{
Excel::_WorkbookPtr spXlBook = nullptr;
HRESULT hResult = pOfficeDocument->QueryInterface(&spXlBook);
if(hResult == S_OK)
{
_bstr_t path = spXlBook->Path;
return (path.GetAddress() == nullptr || path.length() == 0);
}
return false;
}
bool EmbededHelperFunctions::isEmbeded(IUnknown* pOfficeDocument)
{
if (isEmbededHelper(pOfficeDocument))
{
IOleObject *LobjOleObject = nullptr;
IOleClientSite *LobjPpClientSite = nullptr;
if (pOfficeDocument->QueryInterface(&LobjOleObject) == S_OK)
{
// get the client site
LobjOleObject->GetClientSite(&LobjPpClientSite);
if (LobjPpClientSite != nullptr)
{
// if there is one - we are embedded
return true;
}
}
}
// not embedded
return false;
}
Now the first issue I ran into is despite a workbook being a COM object. There is no neat way to get the pointer from it.
I tried the method described here: C# - How To Convert Object To IntPtr And Back?
but creating a GCHandle of a workbook crashes on the alloc call.
And if I just pass the object raw things seem to work until I shutdown when I get a rpc server is unavailable error
I changed the pinvoke to
#region IsEmbeded
[DllImport("test.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool IsEmbeded([MarshalAs(UnmanagedType.IUnknown)] object Book);
#endregion
things seem to be working AGAIN... but now the RPC error on shutdown is non-deterministic.
Is there a way to strip away the remote callable wrapper that .net encapsulates all these com objects with and just pass along the raw pointer? Does anybody know how I could do this?