DLL runtime error crashes my c# app - how to avoid it?
Asked Answered
S

2

2

Within my windows app, i'm using a c++ DLL wrapped with a .NET DLLs (specifically - the quickfix engine). While running, once every day (not at any specific time), in one of the a constructor of one of the built-in classes throws a runtime error. Even though the error is caught and reported (to a log file, and the database), I still get the windows 'runtime error' dialog (which offers no recovery/debugging options) and after pressing the 'ok' button (the only one available) my app is terminated.

This happens when running in Debug, Release and even while running within the VS2005 debugger itself.

As a side-note, I have compiled the above-mentioned DLLs locally (since at least one of them includes auto-generated code based on an XML specification).

Anyone? (details follow)

My code:

try
{
    QuickFix.Symbol Symbol = new QuickFix.Symbol();
    report.get(Symbol);
    PairsType instrument = ToPairType(Symbol.getValue());

    if (PairsType.NONE == instrument)
        return;

    QuickFix.MDEntryDate entryDate = new MDEntryDate();
    QuickFix.MDEntryTime entryTime = new MDEntryTime();
    QuickFix.QuoteCondition quoteCondition = new QuoteCondition();
    QuickFix.MDEntryPx MDEntryPxBid = new QuickFix.MDEntryPx();
    QuickFix.MDEntryPx MDEntryPxAsk = new QuickFix.MDEntryPx();

    QuickFix.NoMDEntries noMDEntries = new QuickFix.NoMDEntries();
    report.get(noMDEntries);

    for (uint i = 1; i <= noMDEntries.getValue(); ++i)
    {
        QuickFix44.MarketDataSnapshotFullRefresh.NoMDEntries group =
           new QuickFix44.MarketDataSnapshotFullRefresh.NoMDEntries();

        report.getGroup(i, group);

        if (group.isSetQuoteCondition())
            group.get(quoteCondition);
        if (group.isSetMDEntryDate())
            group.get(entryDate);
        if (group.isSetMDEntryTime())
            group.get(entryTime);

        switch (group.getMDEntryType().getValue())
        {
            case MDEntryType.BID:
                group.get(MDEntryPxBid);
                break;
            case MDEntryType.OFFER:
                group.get(MDEntryPxAsk);
                break;
        }
    }

    // use data...
}
catch (Exception e)
{
    // log the error
}

Error details: Message: External component has thrown an exception Stack trace:

at FIX.message_order.=(message_order* , message_order* )
 at std._Tree_nod<std::_Tmap_traits<int,FIX::FieldBase,FIX::message_order,std::allocator<std::pair<int const ,FIX::FieldBase> >,1> >.{ctor}(_Tree_nod<std::_Tmap_traits<int\,FIX::FieldBase\,FIX::message_order\,std::allocator<std::pair<int const \,FIX::FieldBase> >\,1> >* , message_order* _Parg, allocator<std::pair<int const \,FIX::FieldBase> >* _Al)
 at std._Tree<std::_Tmap_traits<int,FIX::FieldBase,FIX::message_order,std::allocator<std::pair<int const ,FIX::FieldBase> >,1> >.{ctor}(_Tree<std::_Tmap_traits<int\,FIX::FieldBase\,FIX::message_order\,std::allocator<std::pair<int const \,FIX::FieldBase> >\,1> >* , message_order* _Parg, allocator<std::pair<int const \,FIX::FieldBase> >* _Al)
 at FIX.FieldMap.{ctor}(FieldMap* , Int32* order)
 at QuickFix.Group..ctor(Int32 field, Int32 delim, Int32[] message_order)
 at QuickFix44.MarketDataSnapshotFullRefresh.NoMDEntries..ctor()
 at PriceProviders.PriceProvider.onMarketDataRefresh(FixSession session, MarketDataSnapshotFullRefresh report)
Sligo answered 22/7, 2009 at 8:56 Comment(1)
which version of quickfix were you using?Syne
S
0

You could load the QuickFix DLL in a separate AppDomain. That would protect your app from it terminating unexpectedly.

You could test for the app domain terminating from your main program and reload it when required.

App domain

http://msdn.microsoft.com/en-us/library/system.appdomain.aspx

A bit more info on building an app using them

http://msdn.microsoft.com/en-us/library/yk22e11a(VS.71).aspx

I'm assuming you don't have access to the C++ code but. Ick.. what a nasty "plaster" fix.

Simonasimonds answered 22/7, 2009 at 9:3 Comment(5)
As I said in previous comments, I do have the code, but since to my company QuickFix is a solution package, I have no wish to mess around with it (nor the time, actually). So, such an nasty fix is all I require until I get a more stable (probably commercial :() package...Sligo
I will :-), however, it seems the .NET wrapper of QuickFix is not that good... the callbacks are not implemented as delegates, which means I cannot use another AppDomain - the callbacks are not domain-aware and always go to the default (initial) domain (the notorious "Cannot pass a GCHandle across AppDomains" issue).Sligo
Hm. You'd be looking to always call / restart QuickFix from the "primary" AppDomain. It's hard to tell how the domain is determined for unmanaged call backs, as they know nothing about them but I'm guessing they return to whatever is the default AppDomain? You might try continuing with the two AppDomain approach and see if there's a way to set the 2nd "QuickFix" AppDomain to be the default after it's created? That way the calls might find their way back there. This guy seems to know more lambert.geek.nz/2007/05/29/unmanaged-appdomain-callbackSimonasimonds
@Ray, link from last comment is dead and without any sample code words like "You could test for the app domain terminating from your main program and reload it when required" is not that good answer.Statuesque
You realise this answer is from 6 years ago? I'm not even sure AppDomains still work the way they did back then. I suggest googling something along the lines of "testing for AppDomain termination" and provide an updated answer below...Simonasimonds
K
0

Looks like you have a stack trace pointing to a bug inside the DLL.
Do you have its code? Is it supported by someone you can forward the stack trace to?

Without fixing the DLL itself, the issue will continue to occur, unless you identify the cases in your code that cause the crash and work around them - not a recommended solution, but sometime the only one available when you do not control the code.

Koppel answered 22/7, 2009 at 9:5 Comment(1)
Log every parameter you send to a function in that DLL. Pretty soon, a pattern will emerge. E.g. you're sending a number bigger than 32,768 and it fails -> maybe the function can only deal with signed ints. You send in a struct that contains a null field -> maybe the function does not check for null values... etc. etc.Koppel

© 2022 - 2024 — McMap. All rights reserved.