Module loading fails when the current directory is changed

Topics: Prism v4 - WPF 4
May 9, 2012 at 4:02 PM
Edited May 9, 2012 at 4:05 PM

Hi everybody,

we are using Prism V4.1 and use the module loading via XAML. The directory which contains the catalog file is relative to our application directory and we try to load it like this in CreateModuleCatalog():

using ( var xamlStream = new FileStream( @".\Bootstrapper\catalog\GraphicalCatalog.xaml", FileMode.Open, FileAccess.Read ))
{
    return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml( xamlStream );
}

The catalog file contains the info for the modules and looks something like this (but with more entries of course):
<Modularity:ModuleInfo Ref="file:///Tree.dll" 
    ModuleName="TreeModule"
    ModuleType="Plugins.Tree.Tree, TreeModule"
    InitializationMode="WhenAvailable">
</Modularity:ModuleInfo>
The problem now is that when the current working directory is not pointing to our application directory then the catalog loading fails. if I then adjust the path to the catalog file to be an absolute path, then the loading of the modules fail. I could then also adjust that path to the modules to be absolute, but somehow I get the feeling that I'm not doing it correctly. So maybe somebody can give me a hint how this is suppose to work. Thanks!
Regards Peter
Developer
May 9, 2012 at 8:56 PM

Hi Peter,

Based on my understanding, it seems that any relative URI will depend on the current working directory of your application to obtain the complete path; therefore, if the current working directory of your application is changed, it's expected that it could not find the corresponding files.

I am not aware of your current scenario or why the current working directory of your application is changed, but as a possible approach you could change the current working directory when the modules need to be loaded (for example, before running the Bootstrapper or when a module marked as "OnDemand" needs to be loaded) and change it back when the modules have been loaded.

For example, if I am not mistaken, you can obtain the path of where your application (the .EXE file) is located by doing something like this:

string directoryName = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;

Therefore, when loading modules (for example, before running the Bootstrapper), I believe you can save the current working directory of your application, change it to the directory where your application is currently stored, load the required modules, and then change it back to the previous working directory.

If this is not possible in your scenario, it would be helpful if you could provide us with more information about it so that we can help you further with this.

Damian Cherubini
http://blogs.southworks.net/dcherubini

May 10, 2012 at 8:47 AM
Edited May 10, 2012 at 8:49 AM

Hi Damian,

 I am not aware of your current scenario or why the current working directory of your application is changed, but as a possible approach you could change the current working directory when the modules need to be loaded (for example, before running the Bootstrapper or when a module marked as "OnDemand" needs to be loaded) and change it back when the modules have been loaded.

We have an associated file type with our application and when the user double clicks this file then our application gets started, but the current working directory is then pointing to the directory where the data file is located and not where the application is located. I think this is a very common scenario and that's why I have the 'feeling' that it should work out of the box, without me having to change the current working directory or adjusting the paths to the catalog file or the module files. For example I'm currently 'patching' the Ref property in ModuleInfo like this:

const string FILE_PREFIX = "file:///";

string applicationDirectory = Path.GetDirectoryName( Environment.GetCommandLineArgs()[ 0 ] );

foreach ( var moduleInfo in moduleCatalog.Modules )
{
    if ( moduleInfo.Ref.StartsWith( FILE_PREFIX ))
    {
        string moduleName = moduleInfo.Ref.Substring( FILE_PREFIX.Length ); 
        string absoluteModuleName = Path.Combine( applicationDirectory, moduleName );
        string absoluteReferencedModuleName = FILE_PREFIX + absoluteModuleName;
        moduleInfo.Ref = absoluteReferencedModuleName;
    }
}

Therefore, when loading modules (for example, before running the Bootstrapper), I believe you can save the current working directory of your application, change it to the directory where your application is currently stored, load the required modules, and then change it back to the previous working directory.

I think you're right, this might be a lot easier then to patch the Ref property and the catalog file path.

But I'm surprised that this normal application startup scenario via a double click on the associated file isn't properly handled by PRISM.

Regards Peter

May 11, 2012 at 8:05 AM

For those who have the same problem: I solved it now with overriding MefBootstrapper.Run:

public override void Run( bool runWithDefaultConfiguration )
{
    string oldCurrentDirectory = Directory.GetCurrentDirectory();
    Directory.SetCurrentDirectory( <applicationDirectory> );

    base.Run( runWithDefaultConfiguration );

    Directory.SetCurrentDirectory( oldCurrentDirectory );
}

Regards Peter