Additional .dll files as resource

antonio_hipolito
Enthusiast
Enthusiast

Additional .dll files as resource

antonio_hipolito
Enthusiast
Enthusiast

I'm developing a REVIT addin that needs a external .dll library.
In order to reduce the number of deployed files, I want to integrate that .dll library as a resource. However, I'm struggling with associating the event handler for the AssemblyResolve. The event dosen't fire up when a not found library is raised.

Where should I place the bellow AddHandler command? At OnStart event handler (that runs when REVIT loads the addin) or at the addin Execute (that runs when REVIT executes the command)?

        AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAssemblies

 

Any example on this?
Do you deploy additional dll libraries as a separate files, or do you integrate then in the main addin .dll as a resource?

 

0 Likes
Reply
2,444 Views
12 Replies
Replies (12)

jeremy_tammik
Autodesk
Autodesk

I believe most people ship them as separate DLLs. I have never heard of anyone doing what you propose, although it sounds useful.

 

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

josh.roth.MEI
Enthusiast
Enthusiast

I tried this to get out of some .dll hell issues I was having. It worked, but statically linking .dlls as an embedded resources like this can cause issues with other add-ins, I found (pyRevit in my case). If that doesn't happen for you, then great! There may still be some pros and cons of doing it this way vs. shipping separate though.

 

Add the usual event listener to your startup:

 

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

 

You will need to add the .dlls you're referencing as an embedded resources (In VS, Add->Existing Items, then under Properties, change Build Action to Embedded Resource). I put all of my .dlls in an Assemblies folder for tidyness. Then you can use the following code for your AssemblyResolve function:

 

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string assemblyName = args.RequestingAssembly.GetName().Name;
            if(assemblyName == "YourAssemblyName")
            {
                string resourceName = "YourAssemblyName.Assemblies." + new AssemblyName(args.Name).Name + ".dll";
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    if (stream != null)
                    {
                        byte[] assemblyData = new byte[stream.Length];
                        stream.Read(assemblyData, 0, assemblyData.Length);
                        return Assembly.Load(assemblyData); 
                    }
                }
            }
            return null;
        }

 

Let me know if this works for you. I got stuck trying to fix the issues it was causing with pyRevit and reverted to shipping the .dlls separately. I'd be curious to see if there is a way to workaround it. That's what I was trying to do with the lines that were checking the calling assembly name. Perhaps someone with more experience in how the CLR loads in assemblies could give some insight.

0 Likes

Kennan.Chen
Advocate
Advocate

Try subscribe to AssemblyResolve event in the static constructor of your implementation of IExternalApplication

ricaun
Advisor
Advisor

@josh.roth.MEIand @antonio_hipolito you could use Fody.Costura to embed the .dll references automatically, the Costura.Template has the ILTemplate.cs and Common.cs to handle all the load resources files, if the Assembly is already loaded the code does not force it to load again.

 

@jeremy_tammikI use this technic on the ConduitMaterial and others plugins.

 

Adding... ILTemplate.Attach(); on the IExternalApplication should do the trick.

 

See yaa

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

antonio_hipolito
Enthusiast
Enthusiast

Thank all for you suggestions.

I'll try them out.

Regards!I

0 Likes

kraftwerk15
Advocate
Advocate

I also wanted to chime in here with resources. Ehsan actually hopped on a call with me and showed me his process with submodules. Since then that's how I have been handling other libraries that I need to use in my project.

 

While I started with an AssemblyResolver, loading in a newer version of the assembly will not resolve correctly when a previous version has already been loaded. This came about because of the CTC addins still used the MahApps version 2 (I believe). Then I come barreling in to the situation with  the new 3.x versions of MahApps because I wanted to use the new, fancy features. Revit will resolve everything up to the point where you wanted to use one of the 3.x features that the 2.x didn't have. Then my app would throw an Exception telling me that it couldn't find that 3.x feature because CTC had loaded the 2.x version first.

 

So, Ehsan told me about submodules in git (which I had to learn about) and re-building MahApps open-source code under a new .dll name.

 

Opening the open-source project, changing the name, building the submodules, and then referencing the new submodules have been fantastic.

 

Attached is a snippet of my library with the re-built MahApps content. Also if you look in pyRevit's library, Ehsan does the same thing, or at least used to.

 

Kudos of course to Ehsan for showing me this and thanks to Jeremy for pointing me here from the blog.

 

Hopefully this helps someone in the future.

Kennan.Chen
Advocate
Advocate

Same issue with MahApps before and I also rebuilt the whole project.

 

Life can be easier to compile MahApps project with another Public/Private key pair(.snk file), which will sign a unique strong name to the dll. Referencing a strong-named dll is supposed to be a common practise to "dll hell" issue.

 

Strong-named assemblies | Microsoft Docs

0 Likes

jeremy_tammik
Autodesk
Autodesk

Many thanks to everybody for all your helpful advice!

 

I summarised and edited it on the blog for posterity:

  

https://thebuildingcoder.typepad.com/blog/2021/10/dll-as-resource-and-multi-version-add-ins.html#5

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open

antonio_hipolito
Enthusiast
Enthusiast

Thanks again for all your thoughts!

0 Likes

minet.axel
Enthusiast
Enthusiast

Hello, maybe you will provide an example, I'm trying to do the same thing but I don't know where to start.

0 Likes

minet.axel
Enthusiast
Enthusiast

Hello,

I need some information.

If I modify the signature of the MahApps project and I recompile the project without modifying the name of the assembly. Does this work? Because in the project there are lots of references to the MahApps assembly name.

 

Thk

0 Likes

Kennan.Chen
Advocate
Advocate

Theoretically, you don't need to rename the dll name once your have sign a strong name to the dll and reference that dll directly.

 

Weird thing is I went through my code written years ago and found that I actually did the renaming. I could not recall why I did that and whether it was necessary.

 

EDIT:

One thing to mention is that an add-in is a part of Revit and there could be other add-ins referencing MahApps without strong name. If your add-in uses a strong-named MahApps.dll, add-ins that loaded after your add-in will use your MahApps.dll, which could cause those add-ins to fail to load because of the incompatible version. Renaming your strong-named MahApps dll can prevent your add-in from "polluting" the common .NET runtime in Revit.

0 Likes