0

Thanks to an advanced initialization scenario (as warned about in the documentation), we're leveraging the ModuleInitializer attribute to run some code at module-initialization time like so...

[ModuleInitializer]
public static void InitModule() {
    // Do something with the app here
}

However, we found differences in behavior depending on how the module was loaded. Specifically...

  • If loaded via a project reference/dependency, Application.Current property is not set.
  • If dynamically loaded via reflection, Application.Current property is set.

Since we need access to the current application for part of our initialization (it doesn't have to run right then, only when the application exists), our initial approach was during our initializers, test if Application.Current was set. If so, use it as normal. If not, build some sort of 'actions queue' that would queue up all the actions we want to run, then from within the Application class's constructor, process that queue.

As such, the above changed to this...

[ModuleInitializer]
public static void InitModule() {

    void appRelatedInitialization(Application application) {
        // Do something with the app here
    }

    if(Application.Current is not null) {
        appRelatedInitialization(Application.Current);
    }
    else {
        // Add the method to a queue of actions which take an Application as an argument
        Globals.AppInitQueue.Add(appRelatedInitialization);
    }
}

But then we ran into an application that doesn't use an Application subclass, instead opting for a main method and doing all the app configuration there.

Of course we could add the queue processing there, just like we could with an initializer and subclassing, but what we really want is to know when the application is instantiated regardless of the type of application it is and have this execute automatically for us so we don't have to add it anywhere.

The issue is Application.Current is a static POCO property that doesn't use INotifyPropertyChanged or similar, so we're not sure how to determine when the Application instance is set. We don't want to use brute-force polling either as that is the worst kind of code-smell.

So, how can we determine when the current Application object is created and assigned to the running process?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • A very similar problem exists in C++, named the "static initialization order fiasco". It is actually worse in C#, it is deterministic in C++ but not in C#. Could work just fine when you debug, fall over in the release build. Or work fine in the release build but keel over when you enable multi-core jit. Writing is on the wall, a module initializer is *not* a substitute for a constructor call. As soon as you touch state of other assemblies then you'll easily lose in a very hard to fix way. – Hans Passant Sep 04 '22 at 15:12
  • @HansPassant, yeah, I've heard bad things about similar situations. I figured touching a global, shared state value in an assembly on which this one depends was a safe thing because it doesn't affect things like app thinning. That and there is no order issue, at least not that I can see, but I could be wrong. The other thing I could try is if there is no current app yet, registering a global class event handler for `Application.Startup` and perform the code I need there. That way I'm not touching any state, static or otherwise. What are your thoughts on that approach? – Mark A. Donohoe Sep 05 '22 at 16:18
  • Welp... Application.Startup is a straight-up delegate, not a routed event so no class handlers there. Still need the instance. – Mark A. Donohoe Sep 05 '22 at 16:55

1 Answers1

0

Dispatcher.CurrentDispatcher will create a Dispatcher for the current thread if not already created, which will be started by Application.Run(), so you should be able to do

Dispatcher.CurrentDispatcher.BeginInvoke(() => {})

I tried this, and it will run after the App constructor and App_Startup is called, if you leave Dispatcher.Priority as it's default.


(If you want it to run earlier, you can do

Dispatcher.CurrentDispatcher.BeginInvoke(() => {}, DispatcherPriority.Send);

in which case it will run after App construction but before App_Startup, although reading through the source code it seems they explicitly try to run OnStartup first, due to some bug, and this is circumventing that. I don't know the repercussions of doing that.)

Yitz
  • 946
  • 4
  • 8
  • 1
    This had potential, but unfortunately, we need access *before* `Run` is called. This is because the `Run` method event takes in the primary window, and that window is what needs the resources to be registered with the application. We could add them to the app's XAML file, but that's precisely what we're trying to avoid doing, having each module register their own resources with the application. Looks like I just need to add my own thing post `InitializeComponent` as we're doing now. – Mark A. Donohoe Sep 05 '22 at 16:59