2

My .net program is using an assembly, which should be installed in the GAC.

If the assembly is properly installed, it will be loaded as soon as I use it the first time, which is great. If, for example, I use a type from the assembly like this:

ESRI::ArcGIS::esriSystem::AoInitialize^  aoi = gcnew ESRI::ArcGIS::esriSystem::AoInitializeClass();

The assembly will be loaded.

Now sometimes the assembly will not be present in the GAC, and I need the program to be aware of this but not crash.

I tried wrapping the usage of the assembly into a try/catch block like this:

try
{
  ESRI::ArcGIS::esriSystem::AoInitialize^  aoi = gcnew  ESRI::ArcGIS::esriSystem::AoInitializeClass();
}
catch (System::Exception^ )
{
  // assembly not found
}

But the program will not throw an exception but crash instead.

How can I check in advance, if the assembly is in the GAC and could be used without crashing? Or how could I catch the crash and disable the corresponding menu items in my program?

ohmantics
  • 1,799
  • 14
  • 16
Sam
  • 28,421
  • 49
  • 167
  • 247
  • Possible duplicate: http://stackoverflow.com/questions/1933947/check-gac-for-an-assembly – Jason Down May 30 '11 at 13:46
  • At the pending edit: this is not c#, this is managed-c++! I'd edit it, but clicked too fast on the Reject button instead of editing it... – Stormenet May 30 '11 at 13:56

2 Answers2

2

You have the wrong mental model about the way assemblies get loaded. It does not happen when you create an instance of a type in the assembly. The JIT compiler is the one that needs the assembly first. To generate the machine code for the method. If you look at the call stack for the exception then you'll see that the exception was raised in the calling method.

At least, that's what normally happens when you use a Microsoft jitter, it generates machine code on demand, at the last possible moment. It is different for other jitters, like the Mono one, it compiles much more eagerly.

You could catch the exception in the calling method but that's pretty brittle. Other than the jitter dependency, you will also have to attribute the method with [MethodImpl(MethodImplOptions.NoInlining)] to ensure that this code never gets inlined. Because that will again cause the type to be needed too soon.

The proper fix is to use a plug-in architecture. You have to use an interface type to declare the properties and methods you'll want to use in your main program. That interface type needs to be declared in a separate assembly, reference by both your main program and the plugin. And deployed with your main program, whether or not the plugin option is available to your customer. Use Assembly.Load() to load the plugin assembly and Assembly.CreateInstance() to create the concrete instance of the type that implements the interface. And never refer to the concrete type in your code, only the interface type. You'll find lots of examples of this with a google query.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks for the explanation about jit, it explains a lot! So your recommendation would be to use the 3rd party assemblies from a plugin, so I'd get an error when I try to load the plugin and therefore would know not to use this functions? – Sam Jun 01 '11 at 12:36
  • Erm, not sure that sounds right. Key point is "never refer to the concrete type in your code", everything else follows from that. – Hans Passant Jun 01 '11 at 12:43
  • Well, I can't change the 3rd party dlls or their installation in any bit, so I can't change them into plugins. Therefore I'd need to use a plugin as wrapper if I want to follow your advice. – Sam Jun 01 '11 at 13:02
0

The best way would be to add all the necessary assemblies in the installation process.

However, what you want can be achieved with a simple type check before creating the type.

Something like this:

var t = System.Type.GetType("TypeName, Assembly");
if (t == null) throw CannotLoadTypeException();
Paulo Santos
  • 11,285
  • 4
  • 39
  • 65
  • The customers will not want to buy the component for every workstation, so I'll need to disable functions using it when it is not present. Pirating the dlls and installing them is not an option. – Sam May 30 '11 at 14:25
  • I tried to use this, but I always get nullptr as result, even when the assembly is present. I used System::Type^ t = System::Type::GetType("ESRI.ArcGIS.esriSystem.AoInitializeClass, ESRI.ArcGIS.System"); – Sam May 30 '11 at 14:35