.NET

Reply
Valued Contributor
tonofsteel
Posts: 93
Registered: ‎12-04-2009
Message 31 of 41 (498 Views)

Re: Using AutoLoader with dependencies

01-24-2013 06:40 PM in reply to: DiningPhilosopher

In case anyone else is working with LoadFrom() in their code I am going to write a summary of what I have learnt.

 

From what I have read and observed I think what ends up happening is when you use Netload in your code it opens up a dialog to browse to the file that you want to load.  When you change directories and select the file, the dialog changes the Environment.CurrentDirectory to the path that you just selected.  Thus when LoadFrom runs the CurrentDirectory variable is pointing to the folder so if you only pass in an assembly name everything works.

 

I am wrong and jumped to conclusions about Autodesk changing something in their code directly, and I will admit that I was getting fairly frustrated with the whole scenario which may have lead me to that.  The Netload and Autoloader mechanics do work differently, just there is not as much of a mystery anymore as to what is really happening.

 

This is the fourth application I am using the offending library in, and they have all either been standalone or loaded with Netload, and since all of the assembly loading using LoadFrom happens at the initialization of any application I have written everything has worked fine.  If I did have a file dialog in my application and asked the user to load a file from someplace and then tried LoadFrom later on to load a feature then my standalone application would run into the same trouble.

 

So I totally agree with Fenton that using this variable is very dangerous and should be avoided.  I did not even realize that this situation was occurring since it is a third party library that I am using that has this code in it.  I have not worked with LoadFrom() until debugging lead me to that line of code.  If I was familiar enough with it to know about what was happening I could have solved this much more easily.

 

It is easy enough now that I know about this to build the full path and pass it into LoadFrom with my code, but what happens if I end up using a third party .dll that does not?  Luckily in this scenario I have the source code that I can modify, but if I didn't this could be a problem.

 

So I think that as a worst case work-around that if you set the CurrentDirectory as I did above before calling any code that initializes your libraries then this *should* take care of most of the issues that you may run into, and may be enough to get past these issues if you are using a closed source third party library that uses LoadFrom in this way.

 

Since the majority of the time that I am going to load some assembly I assume that it is in the same directory as my "main" assembly, or the assembly doing the loading I am going to build the path with the function calls as above, and pass the fully built path into LoadFrom instead of letting it try resolve the correct path (which will use Environment.CurrentDirectory if you do not specify the path, which very well could be wrong)

 

Anyways at the end of the day I ended up learning something new, and if this thread ends up helping anyone else with LoadFrom then I guess there was some point to all of this.  Thanks to everyone who took a look at this and gave feedback, especially DiningPhilospher and Fenton, I greatly appreciate the help and guidance.

ADN Support Specialist
fenton.webb
Posts: 352
Registered: ‎07-24-2007
Message 32 of 41 (479 Views)

Re: Using AutoLoader with dependencies

01-25-2013 10:45 AM in reply to: tonofsteel

My pleasure. The offer still stands, if you want to netmeet, let me know.





Fenton Webb

Developer Technical Services

Autodesk Developer Network


Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 33 of 41 (470 Views)

Re: Using AutoLoader with dependencies

01-25-2013 09:08 PM in reply to: tonofsteel

Sorry, I'm not clear on something.

 

The bottom line (for me at least), is that your code loads and runs using NETLOAD, but has a problem with the Autoloader, which shouldn't require a patch or intervention.

 

Per chance, did you try testing it using manual registration for demand-loading (either at AutoCAD startup or upon command invocation).

 

If the problem is in your third-party library then why doesn't it happen with NETLOAD?  And, does it happen with manual registration for demand-loading?

 

The documentation for LoadFrom() clearly states that when it is called to load an assembly with a based path, the load context includes the path to that assembly, and that probing for any dependent assemblies referenced from the assembly that was loaded using LoadFrom() will be done in the same location. 

 

That means that the runtime will look in the same folder as the assembly that was loaded using LoadFrom() with a full path, for dependent assemblies when they are requested, and should find them there, regardless of what the current directory is.

 

Perhaps it is that the Autoloader is not using LoadFrom(), but I haven't investigated that.

 

 

 

 

Valued Contributor
tonofsteel
Posts: 93
Registered: ‎12-04-2009
Message 34 of 41 (460 Views)

Re: Using AutoLoader with dependencies

01-26-2013 08:26 AM in reply to: tonofsteel

I tried to find the original articles that I read that described the behavior of LoadFrom() but it is buried someplace in my bookmarks.  The Microsoft documentation is lacking a bit in this area and I did find people posting about their experiences with this, which provided the final clues.

 

Basically these two things conspire against you when trying to load an assembly using only the assembly name:

1. LoadFrom uses the Environment.CurrentDirectory to resolve relative paths if you do not supply a full path and filename.  This may or may not be the path to your executing assembly.

2. When you start AutoCAD and it throws the Idle event the value for Environment.CurrentDirectory is the My Documents folder.

 

As Fenton mentioned it is very dangerous to use because you never know what is setting the CurrentDirectory.  Another article I read mentioned that he avoided using it becuase he observed that if you run a open file dialog box, the path that the user navigates to will be the new value of Environment.CurrentDirectory. (Did not investigate if  the dialog result has to be ok for this to be set for sure though)

 

That is the key why Netload works and nothing else will.  I am manually setting the CurrentDirectory variable by selecting my .dll file in AutoCAD Netload file open dialog.  After I Netload the Environment.CurrentDirectory variable is pointing at my directory with all my dependent dll's.

 

I can emulate this by using the following code in the Initialize() method of my plugin:

 

string filePath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
string completeFilePath = System.IO.Path.GetDirectoryName(filePath);
Environment.CurrentDirectory = completeFilePath;

 

If I use the Autoloader or demand loading without doing the above the default Environment.CurrentDirectory has not been changed from MyDocuments and the LoadFrom fails.

 


DiningPhilosopher wrote:

The documentation for LoadFrom() clearly states that when it is called to load an assembly with a based path, the load context includes the path to that assembly, and that probing for any dependent assemblies referenced from the assembly that was loaded using LoadFrom() will be done in the same location.

 

That means that the runtime will look in the same folder as the assembly that was loaded using LoadFrom() with a full path, for dependent assemblies when they are requested, and should find them there, regardless of what the current directory is.


The documenation is misleading since "the runtime looking in the same folder as the assembly" actually in reality means that it is using Environment.CurrentDirectory.  This is the conclusion I get from what I have read and from messing around with coding this.  I only pass in the assembly name, and I do not see any way of forcing LoadFrom to resolve the actual assembly path rather than using Environment.CurrentDirectory.

 

I hope that is more clear of an answer?  In the end I think it is the documenation making it sound like it is going to use/resolve the current assembly path, when really it is cheating a bit and using Environment.CurrentDirectory assuming that it is the current assembly path.

 

I could not find anything like LoadFrom("AssemblyName", AssemblyLoadOptions.ResolveActualAssemblyPathInsteadOfUsingEnvironmentVar);  but if someone knows something to this effect I would be interested.

 

Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 35 of 41 (454 Views)

Re: Using AutoLoader with dependencies

01-26-2013 10:19 AM in reply to: tonofsteel

tonofsteel wrote:

The documenation is misleading since "the runtime looking in the same folder as the assembly" actually in reality means that it is using Environment.CurrentDirectory.  This is the conclusion I get from what I have read and from messing around with coding this.  I only pass in the assembly name, and I do not see any way of forcing LoadFrom to resolve the actual assembly path rather than using Environment.CurrentDirectory.

 

I hope that is more clear of an answer?  In the end I think it is the documenation making it sound like it is going to use/resolve the current assembly path, when really it is cheating a bit and using Environment.CurrentDirectory assuming that it is the current assembly path.

 

I could not find anything like LoadFrom("AssemblyName", AssemblyLoadOptions.ResolveActualAssemblyPathInsteadOfUsingEnvironmentVar);  but if someone knows something to this effect I would be interested.

 


Well, here's what I'll tell you.  I have countless plugins that have dependent assemblies that live in the same folder as the plugin, which is demand-loaded via the registry. When that happens, the current directory is not the folder the plugin is in, but the dependent assemblies in that same folder are still found and loaded, which according to you, should not be happening.

 

In fact, I write some code to deal with the general problem of shared assemblies, and what it does also seems to contradict what you say is happening. That code lets me keep assemblies that are shared by multiple plugins in a seperate folder from where the plugins that depend on them live (it uses an AssemblyResolve event handler and a path that I store in a .config file to locate the dependent assemblies).  In that case, the runtime cannot find the dependent assemblies, and so it invokes my AssemblyResolve event handler, which locates and loads the assemblies using LoadFrom(). Those assemblies that I'm resolving myself are themselves also dependent on other assemblies in the same folder, and after loading any assembly from that folder, the rest of them are found and loaded by the runtime, without having to change the current directory.

 

So, how can it be that I can load an assembly using LoadFrom() from any path (passing the entire path to the assembly), which references other assemblies in the same location, and without changing the current directory, the referenced assemblies are found by the runtime and loaded? I know the runtime is finding those other assemblies because my AssemblyResolve event handler is not being invoked for them.

 

So, something doesn't add up here, because I've been relying on the runtime to load dependent assemblies for ages, and have never seen this.

 

Valued Contributor
tonofsteel
Posts: 93
Registered: ‎12-04-2009
Message 36 of 41 (448 Views)

Re: Using AutoLoader with dependencies

01-26-2013 11:23 AM in reply to: DiningPhilosopher

I am not saying this should not be happening for you, I did not try to use demand loading via the registry so I am not sure if this would change the behavior of how my loading is working. 

 

This isn't the first time I have used the libraries that started giving me problems now.  The same method of loading up the dependencies occurs in at least four other pieces of software.  I have never run into trouble until the autoloader.

 

Try throw a message box up with Environment.CurrentDirectory at the init of your add in.  If it says like it does for me when I Netload that it is in fact the directory of your assembly, then your code is running just like mine is when I Netload, and if I would have tried this everything would be fine.  If it says My Documents then you are doing something right that I have been unable to without setting the CurrentDirectory manually.

 

I added an event handler to the AssembyResolve event like you showed but it was never called and I could never find out why. 

 

I used the following:

 

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

I added code to show a message box with the calling assembly name and what it was trying to resolve, but it never did show up. 

 

All I know is at the end of the day I can take the code that I first put up on here in my first message, add the lines:

 

string filePath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
string completeFilePath = System.IO.Path.GetDirectoryName(filePath);
Environment.CurrentDirectory = completeFilePath;

 And it will work.  So if my code throws errors trying to resolve assemblies in my documents, and then I add these three lines and everything works, *something* must be related to Environment.CurrentDirectory.  I am just describing why I think this is.

 

- This only occurs in my autoloader solution, all other software using this does not have this problem.

- When I use netload  the currentdirectory is the correct directory of my add in assembly

- When I use autoload the currentdirectory is my documents

- The only change between netload and autoload software is the above three lines

- After these three lines are added then the autoload solution works properly

 

Those above 5 statements are the only ones that I will stick to, those are the 5 facts I know for sure can be screen captured and the source files diff'd to verify there are in fact no embelishments.

 

The rest of it is my interpretation on what may be happneing in the background based on what I have read in other forums, and the above 5 things.

 

I am interested in getting assembly resolve working since this seems like a more robust way of handling these things, and adds the extra benefit of load from other directories when necessary.

 

I am also open to anyone who can take the above 5 things I have observed, and add the rest of the story to it if I am wrong.

 

I found post that got me looking into the LoadFrom CurrentDirectory operation:

 

http://stackoverflow.com/questions/3187088/could-not-load-file-or-assembly

 

Quote:

The method LoadFrom will use Environment.CurrentDirectory to build the full path to the assembly that will be loaded. The current directory is not the same as the application base path.

You should provide the full path to LoadFrom method, you can build it using AppDomain.CurrentDomain.BaseDirectory if the file is located in the same folder then the executable.

 

The loadfrom exception states it can't load my file from My Documents -> The Environment.CurrentDirectory is My Documents.  Change Environment.CurrentDirectory to the assembly path and it loads.  Remember though I am not trying to say your code should not work, this is the ONE time I have had to do this, there is only one way of using my source that I have had to modify to get working.  In the vast majority of cases when you look at this var it will be the correct path.

 

To avoid these problems though and to avoid others I see you could run into you should:

- Not use LoadFrom -> Use Load instead

- Build the full path in your code and pass this into any loading functions

- Get the AssemblyResolve event handler working so you can catch these sorts of things and deal with them in one place.

 

 

Valued Mentor
jeff
Posts: 327
Registered: ‎05-12-2009
Message 37 of 41 (424 Views)

Re: Using AutoLoader with dependencies

01-26-2013 03:44 PM in reply to: tonofsteel

Skimming through some of the statements it does not seem to add up but might be just going through quickly.

 

For starters instead of using CodeBase

string filePath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath

What does Assembly.Location return?

 

Codebase returns where file was found, and Location returns path where file was actully loaded.

 

Are there any of the other dll's that might be getting loaded from that location that reference it?

 

As Tony mentioned the runtime will eventully look where the referencing dll is located unless it alredy has been loaded.

 

How the Runtime Locates Assemblies

 

 

 

 

 

 

 

 

You can also find your answers @ TheSwamp
Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 38 of 41 (411 Views)

Re: Using AutoLoader with dependencies

01-26-2013 09:27 PM in reply to: tonofsteel

If you look at this document

 

You'll notice the following:

  

Other Locations Probed:
Assembly location can also be determined using the current binding context. This most often occurs when the Assembly.LoadFrom method is used and in COM interop scenarios. If an assembly uses the LoadFrom method to reference another assembly, the calling assembly's location is considered to be a hint about where to find the referenced assembly. If a match is found, that assembly is loaded. If no match is found, the runtime continues with its search semantics and then queries the Windows Installer to provide the assembly. If no assembly is provided that matches the binding request, an exception is thrown. This exception is a TypeLoadException in managed code if a type was referenced, or a FileNotFoundException if an assembly being loaded was not found.
Valued Contributor
tonofsteel
Posts: 93
Registered: ‎12-04-2009
Message 39 of 41 (397 Views)

Re: Using AutoLoader with dependencies

01-27-2013 03:49 PM in reply to: tonofsteel

The part in bold highlights exactly why I am confused with this whole thing.

 

I write LoadFrom("Mapping.dll") in my code.  This caller .dll file has been found and loaded separately since it is not part of the top level add-in that is loaded by the Autoloader.

 

I get the FileNotFoundException, and it states it can't find the file in My Documents.  All of my .dll files are in the same directory.

 

Quote:

"the calling assembly's location is considered to be a hint about where to find the referenced assembly"

 

Quote:

The loadfrom exception states it can't load my file from My Documents -> The Environment.CurrentDirectory is My Documents.  Change Environment.CurrentDirectory to the assembly path and it loads.

 

 

I will thus revise my question:

 

Although the documentation states that it will consider the calling assembly's location a hint for using LoadFrom, and the .dll file that I am trying to load is in the same location I get a FileNotFoundException.  This is strange since when I manually set Environment.CurrentDirectory to the calling assembly's location in my add-in initialization method the FileNotFoundException is no longer thrown and the assembly is loaded. 

 

The structure of my software is the AutoCAD Autoloader loads a Main.dll which has a reference to LoadMapping.dll  LoadMapping.dll has a function that is called in MainDll.  The called method in LoadMapping.dll has a LoadFrom statement, of which this behavior is observed.

 

Is there any explanation or insight on why this observed behavior may be occurring?

ADN Support Specialist
fenton.webb
Posts: 352
Registered: ‎07-24-2007
Message 40 of 41 (381 Views)

Re: Using AutoLoader with dependencies

01-28-2013 10:51 AM in reply to: tonofsteel

Just a quick question - what is the reason why you are not supplying the actual path to LoadFrom() again? Did I miss that point?





Fenton Webb

Developer Technical Services

Autodesk Developer Network


Announcements
Are you familiar with the Autodesk Expert Elites? The Expert Elite program is made up of customers that help other customers by sharing knowledge and exemplifying an engaging style of collaboration. To learn more, please visit our Expert Elite website.
Need installation help?

Start with some of our most frequented solutions or visit the Installation and Licensing Forum to get help installing your software.