Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C#
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 20-Apr-11 15:11pm
Comments
SAKryukov at 20-Apr-11 21:00pm
   
Interesting, my 5.
--SA
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 3

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.
  Permalink  
Comments
lackonagy at 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.
d@nish at 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 at 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.
d@nish at 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 at 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.
SAKryukov at 21-Apr-11 0:14am
   
No, it does not help. I try to load dynamically, the problem is different. Please look at my updated solution: I explain what happens.
No.
--SA
d@nish at 21-Apr-11 4:51am
   
Ah..now I got what OP is trying to do. 5!
SAKryukov at 21-Apr-11 5:13am
   
You mean 5 to OP? I voted 5, too, interesting question -- doesn't work.
--SA
Csongi Varro at 21-Apr-11 6:05am
   
Interesting question indeed, also interesting alternatives 5 to you SAKryukov, for your patience and your alternative solutions. But check out my answer, I created a test project and tested it out. And it really works...
lackonagy at 21-Apr-11 10:49am
   
It works indeed.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 4

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:
 
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));
}
  Permalink  
v3
Comments
BobJanova at 21-Apr-11 9:18am
   
If this works (and you say it does), definite 5.
lackonagy at 21-Apr-11 10:56am
   
I've tested it. It works. I guess 5 means 5 stars rating.
lackonagy at 21-Apr-11 10:57am
   
Thanks Csongi. Excellent solution! This is the last thing I would've though of...
SAKryukov at 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 at 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.
Member 11220725 at 10-Nov-14 4:36am
   
Thanks CSongi. This solution works and is definitely can be used for solving problems with assemblies in a most intelligent way without any additional config changes. It took me couple of days of debugging before picking up your solution.
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

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. Smile | :)
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:
 
<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
  Permalink  
v5
Comments
lackonagy at 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.
SAKryukov at 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.
SAKryukov at 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 at 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!
SAKryukov at 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
SAKryukov at 20-Apr-11 22:57pm
   
And how the exception stack trace looks like (lines where exception is thrown/propagated)?
--SA
SAKryukov at 20-Apr-11 23:45pm
   
OK, I tried and fail to get any solution out of it. But I dug out where the problem is:
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.
d@nish at 20-Apr-11 23:52pm
   
The problem does not have anything to with dynamic assembly loading. It's more about the references OP has added already to his application.
SAKryukov at 21-Apr-11 0:00am
   
I know, I know. This is just an alternative out of desperation. It works.
--SA
lackonagy at 20-Apr-11 23:56pm
   
SAKryukov, thank you for taking the time to debug this. I don't know why so many posts out there (including the MS help) claim that this works when in reality it doesn't.
 
To answer your question: yes, I did try it with Copy Local = True and it works like charm. I can post the stack trace shortly if it is till needed.
SAKryukov at 21-Apr-11 0:02am
   
Not anymore, thank you. This code won't reach the exception catch! Look at my Solution update to see why.
--SA
SAKryukov at 21-Apr-11 0:12am
   
OK, another update: back to config. Did you use such style of config? It works well for me.
--SA
lackonagy at 21-Apr-11 0:16am
   
If you mean app.config - I used to have the <probing privatePath="MySubFolder"> set, but I've removed the app.config file. Is that what you mean?
SAKryukov at 21-Apr-11 0:43am
   
I don't understand how you understand it, maybe not exactly. You don't need app.config file in your project (or, rather you may or may not need it). What I say is: you create exactly the same file as in my example (with different directory name(s)) and put in in your application executable directory. Is your application's main module of the entry assembly is "A.B.C.exe", this file should be named "A.B.C.config". You can simply create it manually, not relying on the file copied by your project, because you may want to change the directory of the libraries. That's it.
--SA
lackonagy at 21-Apr-11 0:58am
   
Thank you for the solution, however this is pretty much the same as the app.config file (which eventually will be named MyAppName.exe.config).
What I'm trying to achieve is to keep a clean folder for my application with the executable in it and a subfolder with the satellite assemblies, no other files.
SAKryukov at 21-Apr-11 1:08am
   
This is just the same with only one difference: app.config (with this exact name) is part of the project with serves as a template for real .config in the output path. Use it or not, during run-time this is the same thing.
 
I put this alternative because it is most simple and robust/practical enough. I would stick to it.
 
--SA
lackonagy at 21-Apr-11 1:48am
   
Thanks again for your time.
SAKryukov at 21-Apr-11 2:34am
   
You're welcome.
I don't know if you can formally accept this Solution, but I would appreciate it...
(It won't prevent others to add a better one if any.)
For the time being, we don't have a solution for the schema you were hoping for, only the alternatives...
--SA

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

  Print Answers RSS
0 OriginalGriff 350
1 Jochen Arndt 190
2 Richard MacCutchan 135
3 Sergey Alexandrovich Kryukov 130
4 DamithSL 95
0 OriginalGriff 6,045
1 DamithSL 4,601
2 Maciej Los 4,087
3 Kornfeld Eliyahu Peter 3,480
4 Sergey Alexandrovich Kryukov 3,310


Advertise | Privacy | Mobile
Web03 | 2.8.141220.1 | Last Updated 21 Apr 2011
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100