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

Mimoa

Member
Joined
Apr 29, 2020
Messages
24
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
 
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:
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

  • 1588582279963.png
    1588582279963.png
    100.2 KB · Views: 26
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?
 
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?
 
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.
 
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.
 
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;
        }
 
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:
C#:
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:
C#:
string OpcAsmPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\OPC Labs QuickOPC 2020.1\\Assemblies\\net47";
should be something like:
C#:
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.
 
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

  • REFNotWork.png
    REFNotWork.png
    61.8 KB · Views: 30
  • REFWorks.png
    REFWorks.png
    101.4 KB · Views: 30
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.
 
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

  • break1.png
    break1.png
    212.1 KB · Views: 28
  • break2.png
    break2.png
    200.1 KB · Views: 27
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....
 
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.
 
Back
Top Bottom