Click here to Skip to main content
15,867,330 members
Please Sign up or sign in to vote.
5.00/5 (10 votes)
See more:
Hi all,

I have an app that depends on some assemblies stored in a subfolder. I'd like to get rid of the app.config file (and the privatePath="MySubFolder" within) and I don't want to install the assemblies into the GAC.

What I'm trying to do is to use the AssemblyResolve event to tell the app where to look for the assemblies. However, when I run the app I get a FileNotFoundException before the execution hits the AssemblyResolve handler. In fact I get that error before anything else happens. I placed a breakpoint on the entry point of the app and the exception comes before the very first line of code is executed.

This is the same with both .NET 3.5 and 4.0.

Any advice is highly appreciated.
Posted
Comments
Sergey Alexandrovich Kryukov 20-Apr-11 21:00pm    
Interesting, my 5.
--SA

It seems you are not loading ClientClass dynamically. Can you check if dll having that class is referenced correctly? I mean the version and file paths. You can also try Dependency Walker[^] if that helps.
 
Share this answer
 
Comments
lackonagy 20-Apr-11 23:39pm    
Thanks for the post.
After reading your post I deleted the reference then I added it again. I set the following values for the referenced dll:

Aliases = global
Copy Local = False
Embed Interp Types = False
Specific Version = False

If I copy it next to the exe then the execution hits the breakpoint at the beginning of the Main method, otherwise Main is not called, but the exception is raised before.
dan!sh 20-Apr-11 23:51pm    
You may want to keep CopyLocal to true so that it goes along with the exe file of your application and you do not have to do this manually.
lackonagy 20-Apr-11 23:54pm    
Thanks, but this is what I'm trying to avoid. I really need to keep my satellite assemblies in a separate folder.
dan!sh 21-Apr-11 0:01am    
In that case the reference must refer to that path and the set up for your application must create those folders in the client computers. If you make that sure, you can remove copy local from the references. Check the path for the dll in the references properties if that is correct and the dlls are there.
lackonagy 21-Apr-11 0:13am    
I've checked. Path is Ok, the dll is there. At this time I'm only testing this (would be) solution. Both projects (the exe and the dll) are loaded into the IDE, reference is set as expected, builds just fine, but it doesn't run.
Hi there.
I've just checked the code and tested, and came out with a solution. The problem is that the library needs to be loaded somewhere before it's used. But if you use it in the Main then it should be loaded before the Main ( = before adding the AppDomain.CurrentDomain.AssemblyResolve event handler). All you have to do is move your code in a separate procedure cleaning your Main from all code that uses the ClientClass, then call that procedure in the Main, but not before creating the event handler, just like this:

C#
static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    UseClientClass();
    Console.ReadLine();
}
static void UseClientClass()
{
    Console.Write("Enter name: ");
    string name = Console.ReadLine();
    ClientClass.ClientClass clientClass = new ClientClass.ClientClass();
    Console.WriteLine(clientClass.SayHello(name));
}
 
Share this answer
 
v3
Comments
BobJanova 21-Apr-11 9:18am    
If this works (and you say it does), definite 5.
lackonagy 21-Apr-11 10:56am    
I've tested it. It works. I guess 5 means 5 stars rating.
lackonagy 21-Apr-11 10:57am    
Thanks Csongi. Excellent solution! This is the last thing I would've though of...
Sergey Alexandrovich Kryukov 21-Apr-11 16:15pm    
I need to understand the difference. Is the AssemblyResolve handler different? Can I see its code?
Is anything different with ClientClass? Is it defined in the referenced assembly? Is the assembly really referenced? Would you kindly show all code?
Thank you.
--SA
lackonagy 22-Apr-11 0:42am    
Hi SAKryukov,

The AssemblyResolve handler is the same. Here's the difference:
- if the Main() function references a class that is in another assembly then the AssemblyResolve won't be called and the app will fail at startup;
- if AssemblyResolve is in your Main() method, but the reference to the class declared in the other assembly is in a different method (that is eventually called by the Main()) then everything is OK.

I have uploaded the source here: http://dl.dropbox.com/u/7725453/DotNET_4_0.ZIP

If you move the 3 lines of code from the DoProcess() function into the Main() the app will fail as I mentioned in my first post.
MSDN help on the topic could look a bit misleading.
Did you see this code sample:

http://www.thegecko.org/[^]?


[EDIT 1]

The problem is that the crash happens before the handler of AppDomain.CurrentDomain.AssemblyResolve even is added to the event invocation list, before start of main.

So, it looks like this schema does not really work. (Maybe it need some more research.)
I would consider well-working alternative: using dynamically loaded assemblies.
Please see this:
Create WPF Application that uses Reloadable Plugins...[^],
AppDomain refuses to load an assembly[^].

Some designs I described in the above Solutions are overly difficult or problematic. In your case you need the simplest of the cases: non-reloadable (loaded-once) plug-in, which is really simple.

You can use a simple extra trick: my schema suggests you create a plug-in interface, but you don't want to add any shared libraries (you want all the libraries in one place, found by application). You can put this interface in the application: an application assembly (in EXE file) can be loaded exactly as a library, referenced by other assembly.

[EDIT 2]

It was interesting exercise. :-)
As we face such difficulties, let's do something more realistic.

Don't you think you over-estimated the hassle of application config? Did you use probing.
option? Look at this sample file:

XML
<configuration>
	<runtime>
		<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
			<probing privatePath=".\Mediasoft\Assembly"/>
		</assemblyBinding>
	</runtime>
</configuration>


It means that I place this file with my application in some directory, and all my library assemblies are put in its sub-directory ".\Mediasoft\Assembly", they are shared by some set of my applications and are automatically resolved under this sub-directory. It does not have to be a sub-directory, can be somewhere on top of directory structure.

—SA
 
Share this answer
 
v5
Comments
lackonagy 20-Apr-11 21:50pm    
Thanks for the link.
I have tried that, however as I mentioned in my initial post, the exception is thrown before any of my code to be executed, even before the static void Main(string[] args) is called.
Sergey Alexandrovich Kryukov 20-Apr-11 21:55pm    
OP commented.

Thanks for the link.
I have tried that, however as I mentioned in my initial post, the exception is thrown before any of my code to be executed, even before the static void Main(string[] args) is called.
Sergey Alexandrovich Kryukov 20-Apr-11 21:58pm    
Frankly, I did not try it yet, need to find some time for that...
So, you say the code I referenced above does not work, as is? Hm. Did you meet the assumptions on where referenced assembly is supposed to be? (If it's different from what you want -- hold on, make it just as in this example -- one step at a time.) Still does not work?
--SA
lackonagy 20-Apr-11 22:30pm    
Correct. I copied the code you referenced, pasted it into my app, passed the proper directory path, but it doesn't even get that far.

Here's my test console app, the execution does not hit the breakpoints:


static void Main(string[] args)
{ // <= I place a breakpoint here
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); // <= I place a breakpoint here as well

Console.Write("Enter name: ");
string name = Console.ReadLine();

ClientClass.ClientClass clientClass = new ClientClass.ClientClass();

Console.WriteLine(clientClass.SayHello(name));

Console.ReadLine();
}


All that ClientClass.SayHello(string name) does is it returns "Hello {name}".
This is the exception that is thrown:
"Could not load file or assembly 'ClientClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."

Thanks again!
Sergey Alexandrovich Kryukov 20-Apr-11 22:55pm    
Sorry for a boring question, did you check up that an assembly is loaded successfully by default resolution, when its module(s) is placed in the same directory as the referencing one?
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900