Exceptions raised running COM DLL

0

I will try again .... :)

If I have this code:

void CTestDlg::OnBnClickedButtonTest()
{
    MSAToolsLibrary::IMSAToolsLibraryInterfacePtr p;
    HRESULT hr;
    hr = p.CreateInstance(__uuidof(MSAToolsLibrary::MSAToolsLibraryClass));
    if (FAILED(hr))
    {
        __int64 i;
        p->SetPathXML(m_strPublisherDatabaseXML.AllocSysString(), &i);
        p->ReadPublisherData(&i);
    }
}

And I run it, I get this silent exception:

Exception thrown at 0x00007FFCD00B7788 (KernelBase.dll) in Test.exe: 0x04242420 (parameters: 0x0000000031415927, 0x00007FFCBF6C0000, 0x00000099D88FBDC0).

But, if I use a wrapper (header:

#pragma once

#import "D:\\My Programs\\2017\\MSAToolsLibrary\\MSAToolsLibrary\\bin\\Release\\MSAToolsLibrary.tlb" raw_interfaces_only named_guids

class CMSATools
{
public:
    CMSATools();
    ~CMSATools();
    void SetPathXML(CString strPath);
    void OpenPublisherDatabase();

private:
    MSAToolsLibrary::IMSAToolsLibraryInterfacePtr m_pInterface;
}

Class:

#include "stdafx.h"
#include "MSATools.h"


CMSATools::CMSATools()
{
    m_pInterface = NULL;

    HRESULT hr;
    hr = m_pInterface.CreateInstance(__uuidof(MSAToolsLibrary::MSAToolsLibraryClass));
    if (FAILED(hr))
    {
        // TODO: Throw exception ?
    }
}


CMSATools::~CMSATools()
{
}

void CMSATools::SetPathXML(CString strPath)
{
    if (m_pInterface != NULL)
    {
        CComBSTR    bstrText = strPath.AllocSysString();
        __int64     iResult;

        m_pInterface->SetPathXML(bstrText, &iResult);
    }
}

void CMSATools::OpenPublisherDatabase()
{
    __int64 iResult;

    if (m_pInterface != NULL)
        m_pInterface->ReadPublisherData(&iResult);
}

And use this in MFC instead:

void CTestDlg::OnBnClickedButtonGetNames()
{
    CMSATools toolsMSA;

    UpdateData(TRUE);

    toolsMSA.SetPathXML(m_strPublisherDatabaseXML);
    toolsMSA.OpenPublisherDatabase();
}

It is doing the same thing, yet I get these silent exceptions:

Exception thrown at 0x00007FFCD00B7788 (KernelBase.dll) in Test.exe: 0x04242420 (parameters: 0x0000000031415927, 0x00007FFCBF6C0000, 0x00000090F277C040).
Exception thrown at 0x00007FFCD00B7788 in Test.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x00000090F277BB80.
Exception thrown at 0x00007FFCD00B7788 in Test.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000.
Exception thrown at 0x00007FFCD00B7788 in Test.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x00000090F277BB80.
Exception thrown at 0x00007FFCD00B7788 in Test.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000.
Exception thrown at 0x00007FFCD00B7788 in Test.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x00000090F277BB80.
Exception thrown at 0x00007FFCD00B7788 (KernelBase.dll) in Test.exe: 0xE0434352 (parameters: 0xFFFFFFFF80070002, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00007FFCBF6C0000).

Why should one way work and the other cause exceptions (that I can't seem to even detect)?

UPDATE

Please hold off - I see my stupid error now! One moment ....

UPDATE 2

void CTestDlg::OnBnClickedButtonTest()
{
    MSAToolsLibrary::IMSAToolsLibraryInterfacePtr p;
    HRESULT hr;
    hr = p.CreateInstance(__uuidof(MSAToolsLibrary::MSAToolsLibraryClass));
    if (SUCCEEDED(hr))
    {
        __int64 i;
        p->SetPathXML(m_strPublisherDatabaseXML.AllocSysString(), &i);
        p->ReadPublisherData(&i);
    }
}

I corrected the test case now and they both have the same exceptions raised. This is the method that seems to cause the problem in the DLL:

public void ReadPublisherData(out Int64 iResult)
{
    iResult = MakeResult(true);

    try
    {
        _PublisherData.Publishers.Clear(); // Reset

        XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
        using (StreamReader reader = new StreamReader(_strPathXML))
        {
            _PublisherData = (PublisherData)x.Deserialize(reader);
            _PublisherData.BuildPublisherDictionaryFromList();
            iResult = _PublisherData.PublisherDictionary.Count;
        }
    }
    catch
    {
        iResult = MakeResult(false);
    }
 }

As far as I am aware it doesn't cause any exceptions. It seems ...

c#
mfc
com
asked on Stack Overflow Jan 7, 2017 by Andrew Truckle • edited Jan 7, 2017 by Andrew Truckle

1 Answer

2

The only obvious problem you have is that you don't have a problem. The COM server you are using was written in a managed language. Pretty common, dead-simple to do with just an attribute. Probably C#, given that you've asked questions about it. The point is that these exceptions are silent, they are thrown and caught inside the managed code.

Passing exceptions across a COM boundary is illegal, the CLR gives you a rock-hard guarantee that this will never happen. If a managed exception is not caught then it gets turned into an HRESULT error code that indicates a failure. Since you are using the wrappers generated by #import, you'll never actually see these error codes, the wrapper turns them back into a C++ exception by throwing a _com_error. You make no attempt at catching them so if it happens then your program will keel over and die on terminate(), impossible to not notice that.

Fwiw, 0x04242420 is a non-fatal exception code and is an implementation detail of the debugger. Documented here.

The EEFileLoadException is an unmanaged exception used inside the CLR and triggered when Fusion is asked to load assembly and it can't find it. Turns into a managed FileLoadException. You might be slightly more concerned about it given that managed COM servers often have a problem finding dependent assemblies. But this commonly happens when the managed code uses XML serialization, we know you are using it. Using exceptions for flow-control isn't very nice but performance is a feature as well. More about it in this post.

So rather best to not fret about it. If you don't trust your COM server to be implemented correctly then simply debug both. Project > Properties > Debugging and change the Debugger Type setting from Auto to Mixed. And use Debug > Windows > Exception Settings and tick the checkbox for CLR Exceptions to force the debugger to break when a managed exception is thrown.

answered on Stack Overflow Jan 7, 2017 by Hans Passant • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0