12

First of all I need to emphasize that this is slightly different question than the one in this thread. Additionally, installing KB2468871 doesn't help.

I tried to simplify this problem as much as possible. In general it about loading PCL assemblies in Desktop application with Assembly.LoadFile(...).

Let's say there is a .NET 4.0 Console Application (called "C"). It references .NET 4.0 assembly (called "N4") and PCL assembly (called "PCL").

where N4 looks like this:

using System.Linq;

namespace N4
{
    public class ClassInN4
    {
        public static string Greet()
        {
            return new string(
                "hello from N4"
                .ToCharArray()
                .Select(char.ToUpper)
                .ToArray()
            );
        }
    }
}

PCL looks like this:

using System.Linq;

namespace PCL
{
    public class ClassInPCL
    {
        public static string Greet()
        {
            return new string(
                "hello from pcl"
                .ToCharArray()
                .Select(char.ToUpper)
                .ToArray()
            );
        }
    }
}

and C look like this:

using System;
using System.IO;
using System.Reflection;
using N4;
using PCL;

namespace C
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
            Console.ReadLine();
        }

        private static void Test()
        {
            Test("N4", ClassInN4.Greet);
            Test("PCL", ClassInPCL.Greet);
        }

        private static void Test(
            string title, 
            Func<string> generator)
        {
            try
            {
                Console.WriteLine(
                    "{0}: {1}", title, generator());
            }
            catch (Exception e)
            {
                Console.WriteLine(
                    "{0}: {1} -> {2}", title, e.GetType(), e.Message);
            }
        }
    }
}

When you run this application you get absolutely correct results:

N4: HELLO FROM N4
PCL: HELLO FROM PCL

Let's add AssemblyResolve event to CurrentDomain in Program.Main:

AppDomain.CurrentDomain.AssemblyResolve += (_, a) => {
    var fileName = Path.GetFullPath(
        new AssemblyName(a.Name).Name + ".data");
    Console.WriteLine("Probing '{0}'", fileName);
    return 
        File.Exists(fileName) 
        ? Assembly.LoadFile(fileName) 
        : null;
};

So, what it does if assembly cannot be found it tries to load it from ".data" file.

Let's go application folder and rename "N4.dll" to "N4.data" and run "C.exe".

Probing 'C:\xxx\C\bin\Debug\N4.data'
N4: HELLO FROM N4
PCL: HELLO FROM PCL

So it goes through AssemblyResolve and finally loads "N4.data" and works as good as original.

Let's revert "N4.data" to "N4.dll" and rename "PCL.dll" to "PCL.data" and...

Probing 'C:\xxx\C\bin\Debug\PCL.data'
N4: HELLO FROM N4
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
PCL: System.IO.FileNotFoundException -> Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The system cannot find the file specified.

Please note that PCL assembly was loaded just fine, the problem is, it only can't find its dependencies (System.Core) anymore.

It's like Assembly.LoadFile(fileName) is-a no-no if loaded assembly is portable.

Did anyone have this problem? Did anyone solved this problem?

You can find all files here.

EDIT: Thanks to leppie for forcing me to check other options. I actually wanted to be sure that I'm not lying while I answer "Yeah, yeah, I tried". Apparently it was worth checking.

From Suzanne Cook's .NET CLR Notes:

Be careful - these aren't the same thing.

LoadFrom() goes through Fusion and can be redirected to another assembly at a different path but with that same identity if one is already loaded in the LoadFrom context. LoadFile() doesn't bind through Fusion at all - the loader just goes ahead and loads exactly* what the caller requested. It doesn't use either the Load or the LoadFrom context.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Milosz Krajewski
  • 1,160
  • 1
  • 12
  • 19

3 Answers3

5

You can return the System.Core assembly of your platform (e.g. version 4.0.0.0 for .NET Framework 4.0) from the AssemblyResolve event, when asked for the 2.0.5.0 version.

I am loading all my referenced assemblies stored as resources via Load(byte[]), which also fails to resolve the 2.0.5.0 assembly, and I retrieve both System and System.Core from AppDomain.CurrentDomain.GetAssemblies().

Mena
  • 47,782
  • 11
  • 87
  • 106
  • That makes sense. I didn't thought it though, but I'm not sure if it will work as expected in my scenario. AssemblyResolve is in reusable library (targeting 4.0), but main application can be targetting 4.5. So, if other library references 2.0.5.0 should I load 4.0 or 4.5. How do I know from library that main application is 4.5? Another question is "to what end?". System, System.Core, mscorlib, System.Xml, System.Data, System.???. +1 for idea, but no "answer tick" as it does not exhaust all the questions. – Milosz Krajewski Sep 05 '13 at 15:19
  • @MiloszKrajewski, `AppDomain.CurrentDomain.GetAssemblies()` gives you the correct assemblies and you don't need to care about .NET 4 or 4.5. – Lex Li Oct 29 '13 at 05:49
3

I think you are getting these issues because:

You're getting an exception because you haven't got the latest .NET updates.

http://www.paraesthesia.com/archive/2013/01/21/using-portable-class-libraries-update-net-framework.aspx

Take note of the WSUS part - you may think you have the latest updates, but you don't cause your WSUS server is out of date.

This patch may help, but you're better just to get all the .net updates:

http://support.microsoft.com/kb/2468871

(from a comment above)

Try LoadFrom(...) rather than LoadFile(...) / Load(byte[]) and see if that fixes your issue? :)

Tod Thomson
  • 4,773
  • 2
  • 33
  • 33
  • 1
    Nope. I explicitly said: "Additionally, installing KB2468871 doesn't help.". It's not about updates, it just about using LoadFile(). – Milosz Krajewski Nov 04 '13 at 14:03
  • Hmmm... I had the same issue with LoadFile() and using Windows Update to install all of the .net patches solved it for me... Just checking, do you have all the .net patches (required / optional) installed? – Tod Thomson Nov 05 '13 at 23:33
  • 1
    This was my problem, server with W2k8 SP1 only and .NET 4.0 exe. Applying SP2 + patches fixed it. – fiat Jan 21 '14 at 23:05
1

I had the same problem and ended up with the following solution: invoke the following code before dynamically loading the PCL assembly.

Assembly.Load("System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes");
Assembly.Load("System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes");

If any other dependency is missing when you load your PCL assembly, you just need to add a line to code above. For some strange and ununderstandable reason, it works.

picrap
  • 1,236
  • 1
  • 13
  • 35