0

We have an extension running in TIBCO Spotfire with which it has a pretty complex interraction. It's a C# Winform application. I want to be able to catch all uncaught exceptions rather than letting them crash the app. One example happens in one of the event handlers we wrote. When I make Visual Studio catch the exception, the call stack below our event handler is:

    C1.Win.C1Command.4.dll!C1.Win.C1Command.C1Command.OnClick(C1.Win.C1Command.ClickEventArgs e)    Unknown
C1.Win.C1Command.4.dll!C1.Win.C1Command.C1Command.Invoke(C1.Win.C1Command.ClickEventArgs e) Unknown
C1.Win.C1Command.4.dll!C1.Win.C1Command.BarAddIn.InvokeCommandLink(C1.Win.C1Command.C1CommandLink cl, C1.Win.C1Command.BarAddIn.InvokedBy invokedBy)    Unknown
C1.Win.C1Command.4.dll!C1.Win.C1Command.ToolBarAddIn.OnMouseUp(System.Windows.Forms.MouseEventArgs e)   Unknown
C1.Win.C1Command.4.dll!C1.Win.C1Command.C1ToolBar.OnMouseUp(System.Windows.Forms.MouseEventArgs e)  Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp(ref System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button, int clicks)   Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m)   Unknown
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam)  Unknown
[Native to Managed Transition]  
[Managed to Native Transition]  
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)  Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)    Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown
Spotfire.Dxp.Forms.dll!Spotfire.Dxp.Forms.MainFormManager.Run() Unknown
Spotfire.Dxp.Main.dll!Spotfire.Dxp.Main.Program.StartMain.AnonymousMethod__0()  Unknown
Spotfire.Dxp.Framework.dll!Spotfire.Dxp.Framework.ApplicationModel.ApplicationThread.AssociateToCurrentThread(Spotfire.Dxp.Framework.DocumentModel.Executor executor)   Unknown
Spotfire.Dxp.Loader.dll!Spotfire.Dxp.Loader.ProgramLoader.StartMain(Spotfire.Dxp.Framework.ApplicationModel.ConnectivityService connectivityService, string startupFile, System.IDisposable startupFileLock, string qualificationExportPath, string applicationProfilerConfigPath, string startupUri, Spotfire.Dxp.Loader.SplashWrapper splashWrapper, Spotfire.Dxp.Framework.ApplicationModel.ModulesService modulesService, Spotfire.Dxp.Framework.ApplicationModel.LocalizationService localizationService, Spotfire.Dxp.Framework.AddIn.Registry.AddInRegistry addIns, Spotfire.Dxp.Framework.ApplicationModel.CobrandingService cobrandingService, bool disableMultipleViews, string pathToDxpExeWithConfig)   Unknown
Spotfire.Dxp.Loader.dll!Spotfire.Dxp.Loader.ProgramLoader.PrepareForStartingMain(System.Collections.Generic.Dictionary<string, object> state, string[] args, string upgradePath, System.Collections.Generic.List<string> moduleFolders, Spotfire.Dxp.Loader.StarterSplash starterSplash, bool isCalledByLegacyStarter, bool isLog4netConfigured)    Unknown
Spotfire.Dxp.Loader.dll!Spotfire.Dxp.Loader.ProgramLoader.InternalExecute(System.Collections.Generic.Dictionary<string, object> state, string[] args, object splash, System.Version starterVersion, System.Collections.Generic.IList<string> moduleFolderList, string upgradePath, bool isCalledByLegacyStarter)    Unknown
Spotfire.Dxp.Loader.dll!Spotfire.Dxp.Loader.ProgramLoader.Execute(System.Collections.Generic.Dictionary<string, object> state, string[] args, object splash, System.Version starterVersion, System.Collections.Generic.IList<string> moduleFolderList, string upgradePath)  Unknown
[Native to Managed Transition]  
[Managed to Native Transition]  
Spotfire.Dxp.exe!Starter.Program.ProgramLoaderWrapper.Execute(string fullLoaderAssemblyName, System.Collections.Generic.Dictionary<string, object> loaderState, string[] args, object splash, System.Version starterVersion, System.Collections.Generic.IList<string> moduleFolderList, string upgradePath) Unknown
[AppDomain (Spotfire.Dxp.exe, #1) -> AppDomain (Spotfire.ApplicationHost, #2)]  
Spotfire.Dxp.exe!Starter.Program.Run(Spotfire.Dxp.Starter.StarterSplashWrapper splash, System.Collections.Generic.Dictionary<string, object> loaderState, bool serverless)  Unknown
Spotfire.Dxp.exe!Starter.Program.ExecuteRunLoopWithBootstrapFlag(string[] cmdArgs, bool calledByBootstrapper)   Unknown
Spotfire.Dxp.exe!Starter.Program.Main(string[] cmdArgs) Unknown

As you can see we don't own the Main, Spotfire does.

As far as I can tell the earliest point of entry is OnApplicationInstanceCreated.

Normally in a Winform application you'd need to do these 2:

AppDomain.CurrentDomain.UnhandledException += ...
Application.ThreadException += ...

See https://stackoverflow.com/a/5762806/788301 . You also need to call

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

but that one crashes with "Thread exception mode cannot be changed once any Controls are created on the thread." meaning we are too late to set it. There is not a GetUnhandledExceptionMode so I don't know what it is, but I suspect it is set to not catch them, otherwise I'd expect
Application_ThreadException to catch our exception. From what I remember there may be a few other things that throw exceptions outside of these 2 paths - has to do with thread pools and/or timers and/or dispatchers, but I think in this case it's that we can't SetUnhandledExceptionMode is the reason it's not caught. The fact that there are a few switches between native and managed might also be a factor.

    protected override void OnApplicationInstanceCreated(AnalysisApplication application)
    {
        ...
        System.Windows.Forms.Application.ThreadException += Application_ThreadException; // Main thread exceptions. Currently not caught probably because of UnhandledExceptionMode
        try
        {
            // crashes with "Thread exception mode cannot be changed once any Controls are created on the thread."
            System.Windows.Forms.Application.SetUnhandledExceptionMode(System.Windows.Forms.UnhandledExceptionMode.CatchException);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; // unhandled exceptions on other threads 
        ...
    }

My hope was that Spotfire has someting like UnhandledException event, but I do not see any. Of course I can put a try catch around every method we wrote, but at this point there is so mauch code that it's not feasible.

ILIA BROUDNO
  • 1,306
  • 14
  • 22
  • I don't know anything about that library, but maybe you could start the program with your own executable and call the Spotfire one as a loaded assembly after setting the exception setting - https://stackoverflow.com/questions/5797205/how-to-load-an-exe-as-a-net-assembly – IllusiveBrian Oct 28 '21 at 03:01

0 Answers0