Exceldna Packing tool and Location of calling assembly

Feb 10, 2013 at 3:49 PM
Hi,

I use
System.Reflection.Assembly.GetCallingAssembly().Location 
to get the path for a config file. I call this in the static constructor of a class that implements the IExcelAddIn interface (even in the constructor or in AutoOpen() method does not make a difference). In general this works fine for me.

If I use the Exceldna Packing tool (i.e. ExcelDnaPack.exe) it fails, i.e.
System.Reflection.Assembly.GetCallingAssembly().FullName 
seems to be correct, but
System.Reflection.Assembly.GetCallingAssembly().Location
returns ""! Which is obvious invalid. Even if I use System.Reflection.Assembly.GetAssembly(typof(MyExcelAddIn)) I get the same result. This happens if and only if I use the packed xll file. Any ideas?

As a workaround I have used the path of an other Assembly and build the path manually. This works but it is not optimal.

Regards,
Markus
Coordinator
Feb 10, 2013 at 5:17 PM
Hi Markus,

When your assembly is loaded from the packed resource (i.e. from memory), the assembly has no 'Location' and the result you see is as expected.

If you have a file called <Addin>.xll.config, it will be loaded as the configuration file for the add-in's .NET AppDomain. This file is also packed by the packing tool, and loaded as the configuration at runtime. So if your configuration can fit into the standard .NET .config files, then that's the best solution. Here is an example: https://groups.google.com/group/exceldna/browse_frm/thread/a1173f3b607cc7e0

If you have some reason to load another file on demand, you can always get the full path to the .xll by calling:
string myName = (string)XlCall.Excel(XlCall.xlGetName); 
Regards,
Govert

PS. Your library is looking great!
Feb 11, 2013 at 7:37 PM
Hi Govert,

thanks for your reply. I use configuration files in a similar fashion, more precisely I use
ConfigurationManager.OpenExeConfiguration(exePath)
where exePath = System.Reflection.Assembly.GetCallingAssembly().Location. The reason is that I want to use several configuration files. For example one configuration file that contains the configuration of nativ dll's to use for Linear Algebra operations (BLAS, Lapack) etc., an other is used for more exotic nativ dll's (Random number generators, optimization routines etc.). Finally a configuration file is needed for the configuration of the Excel interface.
The configuration files are linked to a specified Assembly, for example Dodoni.XLBasicComponents.dll.config, Dodoni.BasicLowLevelMathLibrary.dll.config etc. Especially the configuration file of the Excel interface should be independent of all other (for example if one change the user interface). This makes sense in my eyes.

You are right that packed resources may not have a 'Location'. I just wunder why I get a valid Location for an other Assembly, i.e.
System.Reflection.Assembly.GetAssembly(typof(TypeInDodoni.BasicComponents)).Location
is valid. The Assembly Dodoni.BasicComponents is used in my ExcelAddIn. Moreover it is signed, but I do not know whether this is the reason why it works.


In this context I found a second strange behaviour in the case of packed xll only:

In some cases the static constructor of my IExcelAddIn implementation will be called twice: Two Assemblies are used and loaded in the .dna file via externalLibrary Path=... Both of them contains an IExcelAddIn implementation. The second Assembly depends on the first. The first contains a static event that allow the second Assembly to register specified Configuration forms etc. In the AutoOpen() method of the second Assembly I store a specified method in the static event property of the first Assembly, thus the static constructor of the first Assembly will be called. After that the static constructor of the first Assembly will be called a second time and the event property is null.
Perhaps it helps if I move the event property to an other class that does not implement the IExcelAddIn interface, but I haven't tested it yet.


Regards,
Markus

PS. Thank you very much! My library is not nearly as good as your exceldna project!
Coordinator
Feb 11, 2013 at 8:12 PM
Hi Markus,

I suggest you load the configuration files based on the .xll file location and the assembly names. If you are using the packing, the Location should not be present.

I'm not sure why some packed assemblies seem to have a valid Location. I presume the files do not exist at that Location? (Else they are just not being loaded from the packed resource - e.g. if a file is in the GAC it will be loaded from there - the packed resource is a fallback in case the assembly load fails.)

You second problem might indicate something more serious. Under some circumstances an assembly might be loaded more that once into the AppDomain. This can cause all kinds of unexpected problems, since a given type might effectively be defined more than once, and casts or checks for a particular type might go wrong. Your statics initialized more than once is an indication of this problem.

In your case, the dependency resolution and the 'ExternalLibrary' reference might be causing the one assembly to be loaded more that once. One (slightly hacky) way around it might be to rearrange the <ExternalLibrary> tags in your .dna file, to ensure that the dependent assembly is explicitly loaded first (or last - you'll have to test). Otherwise you might like to restructure that part so that you don't have the cross-dependency, either separating things out or combining the Excel-facing parts into a single assembly.

With Excel-DNA I've tried to achieve some specific goals, like no registration and a single-file add-in. But since we have little control over how the .NET runtime is hosted in the Excel process and how Excel interacts with the add-in, there is some smoke-and-mirrors behind the scenes. Keeping your side of the Excel interaction simple can help, even though that might mean some compromises to how you structure your solution.

Cheers,
Govert
Feb 13, 2013 at 10:20 AM
Hi Govert,

thanks for your remarks. It seems that I have fix my problems. I have tested your solution and it seems to work, but it seems that the configuration file is located somewere but not where I expected it.
Therefore I First use AppDomain.CurrentDomain.BaseDirectory to get the current path (most Assemblies are independent of the Excel GUI therefore I can't use your hint). But the main problem of using
ConfigurationManager.OpenExeConfiguration(exePath)
is that there must be file (xll, dll, exe etc.) at the specified location. If I want to use a configuration file like Assembly.dll.config there must be a file named Assembly.dll which is obviously not the case of a packed xll. Therefore instead of using the ConfigurationManager I read/write a xml file myself which is quite simple.

If I store my configuration file under the name NameOfTheXll.xll.config I get an error while opening the xll. It seems that ExcelDna tries to read a configuration file of exactly this name which does not work:
There was an error during processing of [...].dna:
There is an error in XML document (0,0).
The type initializer for 'System.Xml.Serialization.XmlSerializationReader' threw an exception.
It is not a problem for me, I can just store my configuration under another name. This also seems to solve the problem of calling a static constructor twice. An exception will be thrown in the static constructor of the first Assembly, i.e. it will not be loaded correctly. The second Assembly implies to call the static constructor of the first Assembly again.

Thank you very much.

Regards,
Markus
Coordinator
Feb 13, 2013 at 10:52 AM
Hi Markus,

A file called NameOfTheXll.xll.config will be loaded by Excel-DNA as the configuration file for the AppDomain, so it needs to be in the format that the ConfigurationManager understands.

It sounds like a very bad idea to depend on the config file exception to get your load order right. That would mean simple changes to Excel-DNA could break your add-in. I'd suggest you not depend on that, and investigate some other ways to get the assemblies to load correctly - either changing the order in the .dna file or changing your dependency structure.

Regards,
Govert
Feb 13, 2013 at 12:08 PM
Hi Govert,

off course errors in the loading of a config file should not break down the whole system. I didn't noticed the problem before using the packaging tool.because otherwise I do not have any problems. I will care about it.

Thanks.

Regards,
Markus
Coordinator
Feb 13, 2013 at 12:13 PM
Hi Markus,

My point is that an error in the loading of a config file should not be the thing that makes your system work!

An update to Excel-DNA might handle the error differently, or in a different sequence, and then your assemblies will be loaded in an unexpected order, causing problems again.

Regards,
Govert