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
 
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:
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.
 
From other forum I found the solution - could someone explain it to me please - works for both variable and function:
1593600730848.png
 
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.
 
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.
 
Agree with both of you, apologize and try to do better.

C#:
using OpcLabs.EasyOpc.UA;
using OpcLabs.BaseLib;
using System;
using System.IO;
using System.Reflection;
using System.Threading;
namespace RefTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread.Sleep(10000);
            AppDomain.CurrentDomain.AssemblyResolve += OpcAssRes;
            EasyUAClient client = new EasyUAClient();
            //ClCon();
        }

        //static void ClCon()
        //{
        //    EasyUAClient client = new EasyUAClient();
        //}

        //static Program()
        //{
        //    AppDomain.CurrentDomain.AssemblyResolve += OpcAssRes;
        //}

        private static Assembly OpcAssRes(object sender, ResolveEventArgs args)
        {
            var ProgramFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            string OpcAsmPath = Path.Combine(ProgramFiles, "OPC Labs QuickOPC 2020.2", "Assemblies", "net47");
            var ReqAss = new AssemblyName(args.Name).Name;
            foreach (string FAssName in Directory.GetFiles(OpcAsmPath, "*.dll"))
            {
                if (Path.GetFileNameWithoutExtension(FAssName) == ReqAss)
                {
                    var DLL = Assembly.LoadFrom(FAssName);
                    return DLL;
                }
            }
            return null;
        }
    }
}

15 - wont work now - immediately IO.FileNotFound exception - no sleep, immed. - WHY?
16 - 21 uncomment = Works. 15 commented

23-26 uncomment, 14 comment, works both ways.... also why?
 
I deleted my previous reply because I had an error in my test project. I would say the types in a method must be resolvable when the method is called, so adding the AssemblyResolve event handler in same method that the external type is used is too late. For example neither a Console.Write or a breakpoint in same method is hit before the exception occurs, it happens when method is called. Adding an event handler happens immediately when assigned (+=) and same goes for invocation of the handler when the event is raised.
 
Could you explain it to me ...slowly? So when handler is instantiated in Main, It does not fire on any event...? What is the difference between var some = new Something(); and Call(); where in Call is: var some = new Something(); ?
 
Somewhere you add AssemblyResolve. Later in a different method you can use types resolved by it.
 
Yes, that seems to be the observed behaviour. But where in the documentation does it say that that should be the expected behaviour when making use of the assembly resolved event?
 
So after month I am back.
So the solution was:

C#:
namespace File_To_UA_Sender

{
    class Program
    {
         /* 1. When Main is launched, it checks all direct methods and their references in Usings statements
               So when Resolver is in the Main, it isnt started, because exception is raised before any
               code is executioned (because there is direct var = new EasyUA...), so no resolver started = no loaded dlls = exception for Client here.
               So put it out like static will do the work... BUT*/

         static Program() {AppDomain.CurrentDomain.AssemblyResolve += OpcAssRes;}

           static void Main(string[] args)
           {
            EasyUAClient client = new EasyUAClient();
                if (File.Exists("Cykly.dat"))

But now I would like to have the client static again to be able to be seen in whole project, not just in Main like:
C#:
namespace File_To_UA_Sender

{
    class Program
    {
         static Program() {AppDomain.CurrentDomain.AssemblyResolve += OpcAssRes;}
         static EasyUAClient client = new EasyUAClient(); // here
           static void Main(string[] args)
           {
                if (File.Exists("Cykly.dat"))

Now the client is static, not in Main and is instantiated before Main, but the resolver is not fired, so no assembly is loaded and exception is raised.
What I need is some kind of method/class with client= new EasyUAClient inside, but available global. Is there a way to do it?
 
You can declare the field of that type, then create and assign the object to it later, for example in Main.
 
Wrap your object and use the Singleton (anti-)pattern.
 
You can declare the field of that type, then create and assign the object to it later, for example in Main.

1. So like this?:
C#:
namespace File_To_UA_Sender

{
    class Program
    {
         static Program() {AppDomain.CurrentDomain.AssemblyResolve += OpcAssRes;}
         static EasyUAClient client;
           static void Main(string[] args)
           {
            AppDomain.CurrentDomain.AssemblyResolve -= OpcAssRes;
            client = new EasyUAClient();

2. Skydiver: I have read about wrapper and singleton, but don't understand it. It is Class in Class? Will then the method be visible for whole project?

3. Hope last question in this thread: Yesterday I thought about loading the 2 libraries using Stream right in the start of the code. I know where they are located - Namespace.Resources.Library1 and 2 so like:
C#:
          Stream AssStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Namespace.Resources.Library1");
            byte[] AssRead = new byte[AssStream.Length];
            AssStream.Read(AssRead, 0, AssRead.Length);
            return Assembly.Load(AssRead);

Is there something wrong to get the assembly "hard" in memory, or does the resolver stamps somehow the assembly so the program can use it?
 
Back
Top Bottom