.NET-র রানটাইমের সময় কীভাবে ফোল্ডারটিকে সমাবেশ অনুসন্ধানের পথে যুক্ত করবেন?


130

আমার ডিএলএলগুলি একটি তৃতীয় পক্ষের অ্যাপ্লিকেশন দ্বারা লোড হয়, যা আমরা কাস্টমাইজ করতে পারি না। আমার সমাবেশগুলি তাদের নিজস্ব ফোল্ডারে থাকতে হবে। আমি এগুলিকে জিএসি তে রাখতে পারি না (এক্সপ্লোর পরিচালনা ব্যবহারের মাধ্যমে আমার অ্যাপ্লিকেশনটির প্রয়োজনীয়তা রয়েছে)। রুট ডিএলএল যখন অন্য ডিএলএল (একই ফোল্ডারে) থেকে রিসোর্স লোড করার বা টাইপ করার চেষ্টা করে তখন লোডিং ব্যর্থ হয় (ফাইলনটফাউন্ড)। আমার ডিএলএলগুলি যে প্রোগ্রামটি অ্যাসেম্বলি অনুসন্ধানের পথে রয়েছে সেগুলি ফোল্ডার যুক্ত করা সম্ভব (মূল ডিএলএল থেকে)? আমাকে অ্যাপ্লিকেশনটির কনফিগারেশন ফাইলগুলি পরিবর্তন করার অনুমতি নেই।

উত্তর:


154

আপনি অ্যাপডোমাইন ব্যবহার করতে পারেন এমন শব্দগুলি.অসাধারণের সমাধান করুন ইভেন্টটি সমাধান করুন এবং ম্যানুয়ালি আপনার ডিএলএল ডিরেক্টরি থেকে নির্ভরতা লোড করুন।

সম্পাদনা করুন (মন্তব্য থেকে):

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);

static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
{
    string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
    if (!File.Exists(assemblyPath)) return null;
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    return assembly;
}

4
ধন্যবাদ, মাতিয়াস! এটি কাজ করে: অ্যাপডোমাইন কারেন্টডোমাইন = অ্যাপডোমাইন.কন্টেনডোমাইন; কারেন্টডোমাইন.অ্যাস্পেসারলসলভ + = নতুন রেজলভএভেন্টহ্যান্ডলার (LoadFromSameFolderResolveEventHandler); স্ট্যাটিক অ্যাসেম্বলি লোডফ্র্যামসেমফোল্ডার রিসলভ এভেন্টহ্যান্ডলার (অবজেক্ট প্রেরক, রেজলভ এভেন্টআর্গ আর্গ স্ট্রিং অ্যাসেমব্লিংপথ = পাথ.কোমাইন (ফোল্ডারপথ, args.Name + ".dll"); অ্যাসেম্বল অ্যাসেমব্লিং = এসেম্বলি.লয়েডফ্রোম (এসেম্বলিপথ); ফিরতি সমাবেশ; }
আইসোব্রেটেল

1
আপনি যদি বেসিক রেজলভারটিতে "ফ্যালব্যাক" করতে চান তবে আপনি কী করবেন। যেমনif (!File.Exists(asmPath)) return searchInGAC(...);
টোমর ডাব্লু

57

আপনি আপনার অ্যাপ্লিকেশন .config ফাইলে একটি পরীক্ষামূলক পাথ যুক্ত করতে পারেন তবে এটি কেবল তখনই কাজ করবে যদি পরীক্ষার পাথটি আপনার অ্যাপ্লিকেশনটির বেস ডিরেক্টরিতে অন্তর্ভুক্ত থাকে।


3
এটি যুক্ত করার জন্য ধন্যবাদ। আমি AssemblyResolveসমাধানটি বহুবার দেখেছি , অন্য (এবং সহজ) বিকল্পটি পাওয়া ভাল।
স্যামুয়েল নেফ

1
আপনি যদি অন্য কোথাও আপনার অ্যাপ্লিকেশনটি অনুলিপি করেন তবে অ্যাপকনফাইগ ফাইলটি আপনার অ্যাপ্লিকেশনটির সাথে স্থানান্তর করতে ভুলবেন না ..
ম্যাক্সটার

12

ফ্রেমওয়ার্ক 4 এর জন্য আপডেট

ফ্রেমওয়ার্ক 4 যেহেতু সংস্থানগুলি উত্থাপন করে উত্সগুলিও উত্সের জন্য বাস্তবে এই হ্যান্ডেলারটি আরও ভাল কাজ করে। এটি সেই ধারণার উপর ভিত্তি করে যে স্থানীয়করণ অ্যাপ্লিকেশন সাব-ডাইরেক্টরিগুলিতে রয়েছে (সংস্কৃতির নাম সহ স্থানীয়করণের জন্য একটি সি: \ মাই অ্যাপ App ইটালিয়ান ভাষায়) অভ্যন্তরে সংস্থান ফাইল রয়েছে। স্থানীয়করণটি দেশ-অঞ্চল অর্থাৎ এটি-আইটি বা পিটি-বিআর হলে হ্যান্ডলারটিও কাজ করে। এক্ষেত্রে হ্যান্ডলারটিকে "একাধিকবার ডাকা যেতে পারে: ফ্যালব্যাক চেইনের প্রতিটি সংস্কৃতির জন্য একবার" [এমএসডিএন থেকে]। এর অর্থ হ'ল আমরা যদি "আইটি-আইটি" রিসোর্স ফাইলের জন্য বাতিল হয়ে যাই তবে ফ্রেমওয়ার্কটি "এটি" জিজ্ঞাসা করে ইভেন্টটি উত্থাপন করে।

ইভেন্ট হুক

        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);

অনুষ্ঠান পরিচালনাকারী

    Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //This handler is called only when the common language runtime tries to bind to the assembly and fails.

        Assembly executingAssembly = Assembly.GetExecutingAssembly();

        string applicationDirectory = Path.GetDirectoryName(executingAssembly.Location);

        string[] fields = args.Name.Split(',');
        string assemblyName = fields[0];
        string assemblyCulture;
        if (fields.Length < 2)
            assemblyCulture = null;
        else
            assemblyCulture = fields[2].Substring(fields[2].IndexOf('=') + 1);


        string assemblyFileName = assemblyName + ".dll";
        string assemblyPath;

        if (assemblyName.EndsWith(".resources"))
        {
            // Specific resources are located in app subdirectories
            string resourceDirectory = Path.Combine(applicationDirectory, assemblyCulture);

            assemblyPath = Path.Combine(resourceDirectory, assemblyFileName);
        }
        else
        {
            assemblyPath = Path.Combine(applicationDirectory, assemblyFileName);
        }



        if (File.Exists(assemblyPath))
        {
            //Load the assembly from the specified path.                    
            Assembly loadingAssembly = Assembly.LoadFrom(assemblyPath);

            //Return the loaded assembly.
            return loadingAssembly;
        }
        else
        {
            return null;
        }

    }

আপনি AssemblyNameসমাবেশ স্ট্রিংকে বিশ্লেষণের উপর নির্ভর না করে সমাবেশের নামটি ডিকোড করতে কনস্ট্রাক্টর ব্যবহার করতে পারেন ।
সেবাজ্জজ

10

এমএস থেকেই সেরা ব্যাখ্যা :

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    //This handler is called only when the common language runtime tries to bind to the assembly and fails.

    //Retrieve the list of referenced assemblies in an array of AssemblyName.
    Assembly MyAssembly, objExecutingAssembly;
    string strTempAssmbPath = "";

    objExecutingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName[] arrReferencedAssmbNames = objExecutingAssembly.GetReferencedAssemblies();

    //Loop through the array of referenced assembly names.
    foreach(AssemblyName strAssmbName in arrReferencedAssmbNames)
    {
        //Check for the assembly names that have raised the "AssemblyResolve" event.
        if(strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
        {
            //Build the path of the assembly from where it has to be loaded.                
            strTempAssmbPath = "C:\\Myassemblies\\" + args.Name.Substring(0,args.Name.IndexOf(","))+".dll";
            break;
        }

    }

    //Load the assembly from the specified path.                    
    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);                   

    //Return the loaded assembly.
    return MyAssembly;          
}

AssemblyResolveAppDomain.CreateDomain
কারেন্টডোমেনের

8

সি ++ / সিএলআই ব্যবহারকারীদের জন্য এখানে @ মাটিয়াস এস এর উত্তর (যা আমার পক্ষে কাজ করে):

using namespace System;
using namespace System::IO;
using namespace System::Reflection;

static Assembly ^LoadFromSameFolder(Object ^sender, ResolveEventArgs ^args)
{
    String ^folderPath = Path::GetDirectoryName(Assembly::GetExecutingAssembly()->Location);
    String ^assemblyPath = Path::Combine(folderPath, (gcnew AssemblyName(args->Name))->Name + ".dll");
    if (File::Exists(assemblyPath) == false) return nullptr;
    Assembly ^assembly = Assembly::LoadFrom(assemblyPath);
    return assembly;
}

// put this somewhere you know it will run (early, when the DLL gets loaded)
System::AppDomain ^currentDomain = AppDomain::CurrentDomain;
currentDomain->AssemblyResolve += gcnew ResolveEventHandler(LoadFromSameFolder);

6

আমি @ মাটিয়াস এস এর সমাধান ব্যবহার করেছি। আপনি যদি একই ফোল্ডার থেকে নির্ভরতাগুলি সমাধান করতে চান - আপনার নীচের চিত্রের মতো সমাবেশের অনুরোধটি ব্যবহার করার চেষ্টা করা উচিত । args.RequestingAs आशीर्वाद নালীর জন্য পরীক্ষা করা উচিত

System.AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    var loadedAssembly = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == args.Name).FirstOrDefault();
    if(loadedAssembly != null)
    {
        return loadedAssembly;
    }

    if (args.RequestingAssembly == null) return null;

    string folderPath = Path.GetDirectoryName(args.RequestingAssembly.Location);
    string rawAssemblyPath = Path.Combine(folderPath, new System.Reflection.AssemblyName(args.Name).Name);

    string assemblyPath = rawAssemblyPath + ".dll";

    if (!File.Exists(assemblyPath))
    {
        assemblyPath = rawAssemblyPath + ".exe";
        if (!File.Exists(assemblyPath)) return null;
    } 

    var assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
    return assembly;
 };

4

অ্যাপডোমাইন.অ্যাপেন্ডপ্রাইভেটপথ (অবমানিত) বা অ্যাপডোমাইনসেটআপ.প্রাইভেটবিনপথ দেখুন


11
এমএসডিএন থেকে : একটি অ্যাপডোমেনসেটআপ উদাহরণগুলির বৈশিষ্ট্যগুলি পরিবর্তন করা কোনও বিদ্যমান অ্যাপডোমাইনকে প্রভাবিত করে না। এটি কেবলমাত্র একটি নতুন অ্যাপডোমেন তৈরি করতে প্রভাবিত করতে পারে, যখন ক্রিয়েটডোমাইন পদ্ধতিটি প্যারামিটার হিসাবে অ্যাপডোমাইনসেটআপ উদাহরণ সহ কল ​​করা হয়।
নাথান

2
AppDomain.AppendPrivatePathএর ডকুমেন্টেশন থেকে মনে হয় যে এটির AppDomainঅনুসন্ধানের পথটি প্রসারিত করতে গতিশীলভাবে সমর্থন করা উচিত , কেবলমাত্র বৈশিষ্ট্যটি হ্রাস করা হয়েছে। যদি এটি কাজ করে, এটা ওভারলোডিং তুলনায় অনেক ক্লিনার সমাধান AssemblyResolve
বিনিকি

অবগতির জন্য, এটা দেখে মনে হচ্ছে AppDomain.AppendPrivatePath .NET কোর মধ্যে কিছুই না এবং আপডেট .PrivateBinPathপূর্ণ কাঠামোর মধ্যে
কেভিনয়েড

3

আমি App.Config ফাইলটিতে প্রোব ট্যাগ যুক্ত করার বিষয়ে অন্য একটি (চিহ্নিত নকল) প্রশ্ন থেকে এখানে এসেছি ।

আমি এটিতে একটি সিডনোট যুক্ত করতে চাই - ভিজ্যুয়াল স্টুডিও ইতিমধ্যে একটি App.config ফাইল তৈরি করেছে, তবে প্রেজেনেটেড রানটাইম ট্যাগটিতে প্রোব ট্যাগটি যুক্ত করা কার্যকর হয়নি! প্রোব ট্যাগটি অন্তর্ভুক্ত করার জন্য আপনার একটি পৃথক রানটাইম ট্যাগ দরকার। সংক্ষেপে, আপনার App.Config এর মতো দেখতে হবে:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

  <!-- Discover assemblies in /lib -->
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="lib" />
    </assemblyBinding>
  </runtime>
</configuration>

এটি বের করতে কিছুটা সময় নিয়েছে তাই আমি এটি এখানে পোস্ট করছি। প্রেটিবিন নুগেট প্যাকেজটিতেও ক্রেডিট । এটি এমন একটি প্যাকেজ যা dllsকে স্বয়ংক্রিয়ভাবে সরিয়ে দেয়। আমি আরও ম্যানুয়াল পদ্ধতির পছন্দ করেছি তাই আমি এটি ব্যবহার করিনি।

এছাড়াও - এখানে একটি পোস্ট বিল্ড স্ক্রিপ্ট যা সমস্ত .dll / .xML / .pdb / Lib এ অনুলিপি করে। এটি / ডিবাগ (বা / প্রকাশ) ফোল্ডারটি unclutters, আমার মনে হয় লোকেরা কী অর্জন করার চেষ্টা করে।

:: Moves files to a subdirectory, to unclutter the application folder
:: Note that the new subdirectory should be probed so the dlls can be found.
SET path=$(TargetDir)\lib
if not exist "%path%" mkdir "%path%"
del /S /Q "%path%"
move /Y $(TargetDir)*.dll "%path%"
move /Y $(TargetDir)*.xml "%path%"
move /Y $(TargetDir)*.pdb "%path%"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.