1

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?

Community
  • 1
  • 1
noztol
  • 494
  • 6
  • 25
  • Have you tried just passing in a `Excel.Workbook` as the object that is P/Invoked? – Scott Chamberlain Jul 29 '16 at 20:21
  • @ScottChamberlain I tried that, It doesn't crash but when I do a QI on it in the unmanaged code it does not get recognized as an Excel::Workbook type. I just posted the code. Do you see anything wrong with my helper code that makes the QI call? – noztol Jul 29 '16 at 20:57
  • I don't know enough C++ to be much help at this level. – Scott Chamberlain Jul 29 '16 at 20:58
  • @ScottChamberlain ok then on the C# side could the pinvoke be wrong? I did not specify how to marshall the workbook. – noztol Jul 29 '16 at 21:06

0 Answers0