ไม่สามารถรับชนิดจากแอสเซมบลีหลังจาก ReflectionOnlyLoadFrom

Nov 07 2020

ฉันได้ปรึกษาโค้ดบนเว็บไซต์ http://codeproject.com ด้วยชื่อเรื่อง "Loading Assemblies from Anywhere into a New AppDomain" ของ Marius Bancila แต่ฉันได้ทดสอบข้อผิดพลาดดังภาพที่แนบมาตอนนี้ฉันไม่ทราบวิธีแก้ไขหวังว่าคุณจะช่วยได้ขอบคุณ

รหัสลิงก์ https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop

ทดสอบ

public class Program
{
    [STAThread]
    public static void Main()
    {
        var project = @"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";//Path to assembly
        var manager = new AssemblyReflectionManager();
        var success = manager.LoadAssembly(project, Guid.NewGuid().ToString());
        if (success)
        {
            var result = manager.Reflect(project, (a) =>
            {
                return a.GetTypes();
            });
            Console.WriteLine(string.Join("\n", result.Select(x => x.Name)));
        }            
        Console.ReadKey();
        manager.UnloadAssembly(project);
    }       
}

ข้อผิดพลาด

คำตอบ

1 MickyD Nov 07 2020 at 10:16

ไม่ได้ตั้งค่าตัวแก้ไขการประกอบ

รหัสของ Marius มีข้อบกพร่องAssemblyReflectionProxyเกี่ยวกับการที่ Assembly Resolver ไม่ได้ตั้งค่าไว้หากคุณเรียกLoadAssemblyไม่เหมือนReflect<>ที่ทำ

ขึ้นอยู่กับวิธีสร้างโดเมนแอปลูกเมื่อโหลดแอสเซมบลีอาจเข้าถึงโฟลเดอร์ตามที่ระบุไว้ในระหว่างการสร้างเท่านั้น หากคุณต้องการประกอบโพรบที่อื่นสำหรับแอสเซมบลีหรือการอ้างอิงคุณต้องมี Assembly Resolver เมื่อ. NET กำลังมองหาแอสเซมบลีสำหรับโดเมนจะเรียกตัวจัดการของคุณตามที่ระบุไว้ในReflectionOnlyAssemblyResolveเหตุการณ์ของแอสเซมบลี หากไม่ได้ระบุไว้หรือหากตัวแก้ไขล้มเหลวในการค้นหาชุดประกอบจะทำให้เกิดฟองขึ้นและทำให้เกิดข้อยกเว้นการโหลดล้มเหลว

ฉันขอแนะนำให้คุณเปลี่ยนรหัสจาก:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

...ถึง:

public class AssemblyReflectionProxy : MarshalByRefObject
{
  private string _assemblyPath;

  public void LoadAssembly(String assemblyPath)
  {
     try
     {
        AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve // <---- add me
             += OnReflectionOnlyResolve; 

        _assemblyPath = assemblyPath;
        Assembly.ReflectionOnlyLoadFrom(assemblyPath);
     }
     catch (FileNotFoundException)
     {
        // Continue loading assemblies even if an assembly 
        // cannot be loaded in the new AppDomain.
     }
  }

คุณสามารถดูสิ่งนี้ได้ในรหัสดั้งเดิมของ Sacha ซึ่ง Marius ใช้ของเขา

เพิ่มข้อกำหนดสำหรับการแก้ไขเส้นทาง

ปัญหาอื่น ๆ ของรหัสคือทั้งสองสมมติว่าเมื่อโหลดแอสเซมบลีหนึ่งชุดแอสเซมบลีที่อ้างอิงใด ๆ อยู่ในโฟลเดอร์เดียวกันซึ่งอาจไม่เป็นเช่นนั้นเสมอไป

แก้ไขAssemblyReflectionProxyเพื่อรวมรายการเส้นทางที่จะตรวจสอบ:

public List<string> ResolvePaths { get; set; }

จากนั้นแก้ไข OnReflectionOnlyResolve ให้มีลักษณะดังต่อไปนี้:

private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
     Assembly loadedAssembly =
         AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
             .FirstOrDefault(
               asm => string.Equals(asm.FullName, args.Name,
                   StringComparison.OrdinalIgnoreCase));

     if (loadedAssembly != null)
     {
        return loadedAssembly;
     }

    foreach (var tryFolder in ResolvePaths)
    {
        var asmName = args.Name.Split(',');
        var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");

        if (!File.Exists(assemblyPath))
            return null;

         return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
    }
}

ชื่ออะไร?

ReflectionOnlyLoadบทความทั้งสองละเลยที่จะชี้ให้เห็นพิมพ์ดีเมื่อใช้ แม้ว่าอย่างน้อย Sacha ก็พูดถึงรหัสของเขาว่าเป็น"ตัวสร้างโค้ด"แต่ฉันก็อดสงสัยไม่ได้ว่าบทความทั้งสองบทความที่มี" Loading Assemblies.... into a New AppDomain"อาจมีการตีความบ้าง

จุดมุ่งหมายของการReflectionOnlyLoadเป็นเพียงที่ - สำหรับการสะท้อน หากคุณโหลดแอสเซมบลีด้วยวิธีนี้คุณจะไม่สามารถรันโค้ดใด ๆ ในนั้นได้ นอกจากนี้และค่อนข้างน่าแปลกใจในตอนแรกสำหรับโปรแกรมเมอร์ตัวสะท้อนแอสเซมบลีส่วนใหญ่รวมถึงฉันก็คือการโทรGetCustomAttributesจะล้มเหลวด้วย (เพราะมันพยายามที่จะ"อินสแตนซ์"ประเภทในแอสเซมบลี)

หากคุณกำลังเขียนระบบปลั๊กอินของคุณเองซึ่งแต่ละปลั๊กอินมี App Domain ของตัวเองวิธีการAssemblyสะท้อนและโหลดจะมีประโยชน์สำหรับขั้นตอนต่างๆของไปป์ไลน์การโหลดระบบปลั๊กอิน:

  1. First pass - ใช้ReflectionOnlyLoadเป็นวิธีตรวจสอบปลั๊กอินเพื่อดูว่าถูกต้องหรือไม่ บางทีคุณอาจต้องการเรียกใช้การตรวจสอบความปลอดภัยบางอย่างอย่างปลอดภัยเนื่องจากทราบว่าไม่มีรหัสปลั๊กอินใดที่สามารถทำงานได้ในช่วงนี้
  2. ครั้งที่สอง - หลังจากตรวจสอบปลั๊กอินแล้วคุณสามารถLoad/ LoadFromประกอบและรันโค้ดได้อย่างปลอดภัย