Question don't want ref dll and exe in same folder.

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
Hi.
I wrote a very simple client to send data to OPC UA server. I am using VS2017 Comm, Win10 x64, oficial Dll library of OPCLabs - purchased with license.
OpcLabs.EasyOpcUA, Version=5.57.125.1, Culture=neutral, PublicKeyToken=6faddca41dacb409, processorArchitecture=MSIL"

Now, everything works just fine - I make reference to dll/copy local, using them, instant. client, write value. Value is on the server, super.

But what if I don't want the dll in the same folder as exe file? Let's say dll will be always in c:/test/lib.dll... How to tell the program, to load the library from other place than basedir?
Something like exe.config, but not just the subdir, but anywhere.
exe.config works... for subdirectory.
I am playing with Assembly.Load/LoadFile/LoadFrom, but no luck.
I am playing with AssemblyResolver using embeded ressource but no luck.
I am very new to C#.
Thanks in advance for yor advices.
1588148753963.png


1588148679840.png
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
I am playing with Assembly.Load/LoadFile/LoadFrom, but no luck.
What exactly do you mean by "no luck"?

Are you getting an exception from calling LoadFrom()? If so what exception are you getting? From the screenshot above, it looks like it is succeeding. If you are not getting an exception, what problem are you running into?

If you absolutely, must load the assembly, but the assembly can't be in the same directory as your .EXE, why can't the assembly be in the GAC? The GAC would be a different location than your executable? Seeing a public key token on that assembly name above, it looks like it is strongly named, so it can live in the GAC.

Out of curiosity, why can't the assembly be in the same directory as your executable? Is your license above only for personal use and you don't have redistribution right? And so now you are forcing other people who are using your program to get their own copy of the assembly (by hook or by crook) and you'll load the assembly once they tell you where to find it?

The reason I am questioning your want/need to have the reference assembly in a different location is that Microsoft specifically designed the defaults of the .NET Framework to search in the subdirectories of the application install to minimize DLL Hell. Microsoft listened to it's customers when Windows users were complaining about DLL Hell in earlier versions of Windows. One way to minimize it is to have applications bring the assemblies that it needs with it when it installs, and specifically install it in a location relative to the application, so that there is no danger of overwriting an existing copy elsewhere.
 
Last edited:

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
Hi.
I mean the client cannot be instantiated due to (it seems to me) missing part of the library:
1588582394949.png

1588582468443.png
1588582716760.png
1588582513115.png

1588583394575.png

Gacutil seems to work, but in VS the same error, I DO think it is just some greenhorn of me error, as I said I am quite new to it.

The reason of .dll being outside of .exe is pure aesthetical. I propably will give it up and put them in subdirectory of EXE, but I wanted to learn something new.
Maybe silly question, do you have some dll, with which I can test it - to find out if I am doing something wrong or it is just in dll. I would use some system dll but I don't know what and how to call on it.
 

Attachments

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
Taking back Gacutil works - when I load te assemblies into GAC and DO NOT USE Resolver, then it works. It is quite sufficient. But do you have some functional dll to try the AssemblyResolver?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Look closely at the path you are passing to LoadFrom() in your resolver. Why the double forward slashes?
Also, did you check the ResolverEventArgs to make sure you are trying to load the right assembly? What if the framework is trying to load a dependency of the main assembly, but you are still returning the main assembly again?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Also, you can make your own assembly. Why do you need one from someone else to be able to test? All .NET Framework executable binaries (.exe and .dll) are assemblies. It just happens that some Program Manager in Microsoft's DevDiv decided that when you start a project in Visual Studio that .exe should be called "applications", and .dll be called "class libraries". And after that point people outside MS who don't bother to read the documentation think that assemblies are .dlls and nothing else because the most common sample code for loading assemblies shows loading a .dll.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
I changed slashes to backward and to @ + one slash, same result. How can I check the ResolverEventArgs?
1588602637001.png
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
args.Name
Look at your screenshot. The requested assembly is "OpcLabs.EasyOpcForms,...", but you are loading the assembly "OpcLabs.EasyOpcUA..."

Take time to read the documentation:

And in particular take note of What the event handler should not do:
The primary rule for handling the AssemblyResolve event is that you should not try to return an assembly you do not recognize. When you write the handler, you should know which assemblies might cause the event to be raised. Your handler should return null for other assemblies.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
args.Name
Look at your screenshot. The requested assembly is "OpcLabs.EasyOpcForms,...", but you are loading the assembly "OpcLabs.EasyOpcUA..."

Take time to read the documentation:

And in particular take note of What the event handler should not do:


As I said Novice error, thanks mister Skydiver. I pushed bad dll into resolver. Now it works:


C#:
 private static Assembly OpcAssRes(object sender, ResolveEventArgs args)
        {
            string OpcAsmPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\OPC Labs QuickOPC 2020.1\\Assemblies\\net47";
            foreach (string FAssName in Directory.GetFiles(OpcAsmPath, "*.dll"))
            {
                string ReqAss = args.Name.Substring(0, args.Name.IndexOf(","));
                if (Path.GetFileNameWithoutExtension(FAssName) == ReqAss)
                {
                    var DLL = Assembly.LoadFrom(FAssName);
                    return DLL;
                }
            }
            return null;
        }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Line 6 can move outside of the loop since args.Name never changes.

As as alternative to parsing the args.Name yourself, you could use the AssemblyName class. Something like:
Code:
var requestedName = new AssemblyName(args.Name).Name;
As a quick aside about your naming convention: The .NET Framework highly recommends using Camel casing for local variables and parameters. Furthermore, they recommend using full words instead of abbreviations and acronyms (unless the acronyms are well-known or commonly used). So for example your FAssName should be something like fileAssemblyName. Personally, I would just use fileName.

Also this:
Code:
string OpcAsmPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\OPC Labs QuickOPC 2020.1\\Assemblies\\net47";
should be something like:
Code:
var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
var opcAssembliesPath = Path.Combine(programFiles, "OPC Labs QuickOPC 2020.1", "Assemblies", "net47");
It avoids the possibility of missing or extra backslashes, and future proofs your code for other OSes which may use forward slashes instead of backslashes.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
Hi.
Can I reopen this just for one question?
Two versions of code, one works, other not, when assemblies are in Debug folder, both works:
NOTWork: Exception pops immediately after code is started, even when try to debug . Like it just jumps over Resolver.
 

Attachments

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Set a breakpoint in the resolver and find out if it really being jumped over or not. I would expect both of them to work.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
It jumps over, even when I sleep it - it won't and immediately except. Could this behaviour relate to bad net framework? I have just latest 4.8. Dll documentation say it is necesary to have one of 4.7.1, 4.7.2 or 4.8 installed...
 

Attachments

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
It jumps over, even when I sleep it - it won't and immediately except. Could this behaviour relate to bad net framework? I have just latest 4.8. Dll documentation say it is necesary to have one of 4.7.1, 4.7.2 or 4.8 installed...
I tried it on other machine with the same result, so it is not in Framework. Nevertheless it still does not work. Is there some kind of deeper debugging in VS? Where I could see what is initialized when play i pushed or what VS would await next - something I could see that the resolver is jumped over. It is like VS checks the using libraries and when they are not in the folder, it starts scream....
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Look at the generated IL. Look at the assembler code generate by the JIT.

If I had to guess, I would suspect that assembly is registered in the GAC and therefore the assembly location doesn't have to be resolved. The GAC trumps everything as I recall.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
IL is some log? Where is it located and where is JIT log? So when it is registered in GAC, why it wont run - why it is not found?
Moreover:
1592834438554.png




Solution + Dlls - put test folder to C:\
Gofile
 
Last edited:

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
Good questions.

IL == Intermediate Language. It's an assembly like language that the C# compiler compiles C# code into. Later when you run your code, the JIT (Just-In-Time) compiler takes that IL and converts it into the machine code that will run on your processor.

If you are using the GAC, and there are issues loading from it, you'll need to run fuslogvw.exe to narrow down the cause. Come to think of it, fuslogvw.exe may also be able to tell you why it can't load the assembly even if it is not in the GAC.

Unfortunately, what fuslogvw.exe may not tell you is why your resolver is being called in one case, but not the other case.
 

Mimoa

Member
Joined
Apr 29, 2020
Messages
14
Programming Experience
1-3
From other forum I found the solution - could someone explain it to me please - works for both variable and function:
1593600730848.png
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
2,988
Location
Sydney, Australia
Programming Experience
10+
For future reference, while a picture may be worth a thousand words, words are still valuable. If you have moved that line of code from the Main method to a static constructor then just say that.

As for the explanation, if you set breakpoints on those two methods then you'll see that the constructor is executed before the Main method. If your app works with that line in the constructor but not with it in the Main method then obviously whatever effect it has is required some time between when those methods are executed. If that method is resolving the path of an assembly then obviously that is done before the Main method is executed.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
1,406
Location
Virginia Beach, VA
Programming Experience
10+
So that means that his earlier post (where he unfortunately also used a screenshot), where he added a sleep between setting the event handler and instantiating the object should have worked. If I recall the sleep was for about 1 second, but I can't check on my phone right now. Screenshots are hard to read on a small device and exactly why we discourage their use when presenting code.
 
Top Bottom