2012-11-19 10 views
5

मैं इस सवाल के रूप में कई अलग अलग संस्करण पढ़ा है के रूप में स्टैक ओवरफ़्लो पर हैं, साथ ही 3 अलग गूगल खोजों के पहले पन्ने पर हर नीले लिंक ट्यूटोरियल्स के साथ-साथ एमएसडीएन (जो असेंबली निष्पादित करने से परे उथले प्रकार की तरह है)। मैं केवल ताओ को एक अच्छे परीक्षण मामले के रूप में काम करने के अपने प्रयासों के बारे में सोच सकता हूं, लेकिन मेरा विश्वास करो, मैंने एक साधारण स्ट्रिंग रिटर्न, एक डबल, पैरामीटर के साथ एक समारोह के साथ भी कोशिश की है। जो भी मेरी समस्या है, यह ताओ नहीं है।सी # गतिशील लोड हो रहा है/DLLs Redux की उतराई (AppDomain का उपयोग कर, निश्चित रूप से)

मूल रूप से मैं GLPlugin नाम स्थान में मेरी ड्रा वर्ग के एक testLibraryDomain.CreateInstance() बनाना चाहते हैं।

namespace GLPlugin 
{ 
    public class DrawingControl : MarshalByRefObject 
    { 
     public DrawingControl() 
     { 
      Gl.glColor3f(1.0f , 0.0f , 0.0f); 

      //this is a test to make sure it passes 
      //to the GL Rendering context... success 
     } 
    } 
} 

वास्तव में कलम रंग बदल जाता है:

 if(usePlugin) 
     { 
       AppDomain testLibraryDomain = AppDomain.CreateDomain("TestGLDomain2"); 

       //What the heck goes here so that I can simply call 
       //the default constructor and maybe a function or two? 

       AppDomain.Unload(testLibraryDomain); 
     } 
     Gl.glBegin(Gl.GL_TRIANGLES); 

मैं एक तथ्य यह है कि के लिए पता है। यह तब काम करता है जब मैं इसे static void Main(string args[]) एंट्री पॉइंट देता हूं और मैं testLibraryDomain.ExecuteAssembly(thePluginFilePath) पर कॉल करता हूं या नहीं, प्रत्यक्ष निष्पादन एस्क्रिप्बल्स काम करेगा या नहीं, क्योंकि मुझे यकीन नहीं था कि जीएल कॉल इसे "शीर्ष स्तर" ऐपडोमेन के ओपनजीएल संदर्भ में "शीर्ष स्तर" में लाएगा। यह मुझे असेंबली को ओवरराइट करने और पेन कलर को दूसरी बार बदलने देता है। दुर्भाग्य से इसे निष्पादन योग्य प्रविष्टि बिंदु देने का अर्थ है कि एक पॉपअप कंसोल मुझे बाधित करता है और फिर चला जाता है। यह तब भी काम करता है जब मैं इसे परियोजना में संदर्भ देता हूं और एक नियमित GLPlugin.DrawingTool tool = new GLPlugin.DrawingControl() बनाता हूं, या यहां तक ​​कि someAssembly = Assembly.LoadFrom(thePluginFilePath) (जो निश्चित रूप से और दुर्भाग्य से, असेंबली को ताला लगाता है, प्रतिस्थापन/पुनर्मूल्यांकन को रोकता है) बनाते हैं।

विभिन्न तरीकों की कोशिश की है मैं के किसी भी उपयोग करते हैं, मैं हमेशा "दिया विधानसभा नाम या उसके आधार कोड अमान्य है।" मिल मैं वादा करता हूं, यह मान्य है। जिस तरह से मैं इसे लोड करने की कोशिश कर रहा हूं, वह कुछ नहीं है।

एक बात मैं जानता हूँ कि मैं कमी कर रहा हूँ के रूप में मैं बता सकता हूँ, AssemblyName तर्क विधानसभा फाइल करने के लिए filepath नहीं है जहाँ तक testLibraryDomain.CreateInstance(string assemblyName , string typeName);

के लिए एक सही सेटअप है। क्या यह नामस्थान है, या यहां तक ​​कि सिर्फ असेंबली नाम, यानी: GLPlugin? यदि हां, तो मैं वास्तविक फ़ाइल कहां संदर्भित करूं? वहाँ कोई AppDomain.LoadFrom (कुछ Filename) नहीं है, हालांकि अगर वहाँ थे तो यह आसान होगा। इसके अतिरिक्त, बिल्ली क्या है, और स्ट्रिंग प्रकार नाम उस पर क्या है? मैं "Object" में नहीं डालना चाहता हूं, क्या मैं ऑब्जेक्ट के उदाहरण के अलावा अन्य प्रकार नहीं बना रहा हूं? मैंने ऐपडोमेन की मौलिक समझ की कमी के साथ CreateInstanceAndUnwrap(... , ...) भी कोशिश की है। आम तौर पर मैं ट्यूटोरियल्स के माध्यम से उलझन में काम कर सकता हूं और काम करने के लिए चीजें प्राप्त कर सकता हूं, भले ही मैं अक्सर "क्यों?" नहीं समझता ... यहां नहीं। आम तौर पर मेरे लिए छह अलग-अलग ट्यूटोरियल देखने में मददगार होता है ... इसलिए यहां फिर से नहीं, बल्कि इसलिए कि हर कोई मौलिक रूप से (या ऐसा प्रतीत होता है) दृष्टिकोण लेता है।

तो कृपया ईएलआई 5 ... मैं एक अलग ऐपडोमेन में एक डीएल से कक्षा का एक उदाहरण लोड करना चाहता हूं, शायद कुछ फ़ंक्शंस चलाएं, और इसे अनलोड करें। आखिरकार सूची के रूप में इन कार्यों की सूची बनाएं, आवश्यकतानुसार हटाएं/अपडेट करें ... मुझे उनके लिए भी तर्क पारित करने में सक्षम होना अच्छा लगेगा, लेकिन यह चरण 2 होगा। स्टैक ओवरव्लो के अनुसार, मुझे serializable के बारे में जानना है जो मैं एक और दिन के लिए बंद कर देंगे। (मुझे कल्पना है कि आप मेरे उदाहरण से क्या समझने में सक्षम होंगे।)

उत्तर

11

ठीक है, हमें कुछ चीजों को स्पष्ट करना होगा। सबसे पहले, यदि आप लोड और फ़ाइल ताला लगा iteslf बिना अलग AppDomain को DLLs अनलोड करने के लिए सक्षम होना चाहते हैं, तो शायद आप दृष्टिकोण इस तरह का उपयोग कर सकते हैं:

AppDomain apd = AppDomain.CreateDomain("newdomain"); 
using(var fs = new FileStream("myDll.dll", FileMode.Open)) 
{ 
    var bytes = new byte[fs.Length]; 
    fs.Read(bytes, 0, bytes .Length); 
    Assembly loadedAssembly = apd.Load(bytes); 
} 

इस तरह, आप फ़ाइल ताला लगा नहीं किया जाएगा, और आपको बाद में, डोमेन को अनलोड करने, फ़ाइल को पुन: संकलित करने और बाद में नए संस्करण के साथ लोड करने में सक्षम होना चाहिए। लेकिन मैं 100% निश्चित नहीं हूं कि यह आपके आवेदन को तोड़ नहीं देगा।

और यह दूसरी बात की वजह से है। यदि आप एमएसडीएन के अनुसार CreateInstanceAndUnwrap विधि का उपयोग करेंगे, तो आपको दोनों एपडोमेन में असेंबली लोड करना होगा - जिसे कॉल कर रहा है, और जिसे आप कॉल कर रहे हैं। और यह एक स्थिति में समाप्त हो सकता है, जब आपके पास AppDomains में दो अलग-अलग डीएल लोड होते हैं।

The assembly that contains unwrapped class must be loaded into both application domains, but it can load other assemblies that exist only in the new application domain.

मैं अभी याद नहीं है, लेकिन मुझे लगता है कि दोनों एप्लिकेशन डोमेन में ऑब्जेक्ट निर्माण के व्यवहार अलग है जब आप CreateInstanceAndUnwrap फोन करेगा हो जाएगा, लेकिन मैं विवरण याद नहीं है।

अपनी प्लगइन आर्किटेक्चर के लिए, आप इस ब्लॉग पोस्ट को पढ़ना चाहेंगे। About how to handle Dynamic Plugins using the AppDomain Class to Load and Unload Code

संपादित

मैं भूल गया था कि यह कैसे काम करता है appdomains और मैं कुछ भ्रम की स्थिति को पेश हो सकता है। मैंने संक्षिप्त उदाहरण तैयार किया कि 'प्लगइन' आर्किटेक्चर कैसे काम कर सकता है। यह ब्लॉग में वर्णित किए गए वर्णन के समान ही है, और यहां मेरा नमूना है जो छाया प्रतिलिपि का उपयोग करता है। यदि कुछ कारणों से आप इसका उपयोग नहीं करना चाहते हैं, तो इसे AppDomain.Load(byte[] bytes)

हमारे पास 3 असेंबली हैं, पहला आधार प्लगइन असेंबली है, जो प्रॉक्सी के रूप में काम करेगा, और इसमें लोड किया जाएगा सभी ऐपडोमेन (हमारे मामले में - मुख्य ऐप डोमेन और प्लगइन ऐप डोमेन में)।

namespace PluginBaseLib 
{ 
    //Base class for plugins. It has to be delivered from MarshalByRefObject, 
    //cause we will want to get it's proxy in our main domain. 
    public abstract class MyPluginBase : MarshalByRefObject 
    { 
     protected MyPluginBase() 
     { } 

     public abstract void DrawingControl(); 
    } 

    //Helper class which instance will exist in destination AppDomain, and which 
    //TransparentProxy object will be used in home AppDomain 
    public class MyPluginFactory : MarshalByRefObject 
    { 
     //This method will be executed in destination AppDomain and proxy object 
     //will be returned to home AppDomain. 
     public MyPluginBase CreatePlugin(string assembly, string typeName) 
     { 
      Console.WriteLine("Current domain: {0}", AppDomain.CurrentDomain.FriendlyName); 
      return (MyPluginBase) Activator.CreateInstance(assembly, typeName).Unwrap(); 
     } 
    } 

    //Small helper class which will show how to call method in another AppDomain. 
    //But it can be easly deleted. 
    public class MyPluginsHelper 
    { 
     public static void LoadMyPlugins() 
     { 
      Console.WriteLine("----------------------"); 
      Console.WriteLine("Loading plugins in following app domain: {0}", AppDomain.CurrentDomain.FriendlyName); 
      AppDomain.CurrentDomain.Load("SamplePlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); 
      Console.WriteLine("----------------------"); 
     } 
    } 
} 

यहाँ हम हमारे डमी प्लगइन के साथ एक और विधानसभा होगा, SamplePlugin.dll कहा जाता है और "प्लग इन" फ़ोल्डर के अंतर्गत संग्रहीत। यह PluginBaseLib.dll संदर्भित

namespace SamplePlugin 
{ 
    public class MySamplePlugin : MyPluginBase 
    { 
     public MySamplePlugin() 
     { } 

     public override void DrawingControl() 
     { 
      var color = Console.ForegroundColor; 
      Console.ForegroundColor = ConsoleColor.Green; 
      Console.WriteLine("----------------------"); 
      Console.WriteLine("This was called from app domian {0}", AppDomain.CurrentDomain.FriendlyName); 
      Console.WriteLine("I have following assamblies loaded:"); 
      foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
      { 
       Console.WriteLine("\t{0}", assembly.GetName().Name); 
      } 
      Console.WriteLine("----------------------"); 
      Console.ForegroundColor = color; 
     } 
    } 
} 

और आखिरी विधानसभा (सरल सांत्वना एप्लिकेशन) का संदर्भ लेंगे जो केवल PluginBaseLib.dll और

namespace ConsoleApplication1 
{ 
    //'Default implementation' which doesn't use any plugins. In this sample 
    //it just lists the assemblies loaded in AppDomain and AppDomain name itself. 
    public static void DrawControlsDefault() 
    { 
     Console.WriteLine("----------------------"); 
     Console.WriteLine("No custom plugin, default app domain {0}", AppDomain.CurrentDomain.FriendlyName); 
     Console.WriteLine("I have following assamblies loaded:"); 
     foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      Console.WriteLine("\t{0}", assembly.GetName().Name); 
     } 
     Console.WriteLine("----------------------"); 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      //Showing that we don't have any additional plugins loaded in app domain. 
      DrawControlsDefault(); 

      var appDir = AppDomain.CurrentDomain.BaseDirectory; 
      //We have to create AppDomain setup for shadow copying 
      var appDomainSetup = new AppDomainSetup 
           { 
            ApplicationName = "", //with MSDN: If the ApplicationName property is not set, the CachePath property is ignored and the download cache is used. No exception is thrown. 
            ShadowCopyFiles = "true",//Enabling ShadowCopy - yes, it's string value 
            ApplicationBase = Path.Combine(appDir,"Plugins"),//Base path for new app domain - our plugins folder 
            CachePath = "VSSCache"//Path, where we want to have our copied dlls store. 
           }; 
     var apd = AppDomain.CreateDomain("My new app domain", null, appDomainSetup); 

     //Loading dlls in new appdomain - when using shadow copying it can be skipped, 
     //in CreatePlugin method all required assemblies will be loaded internaly, 
     //Im using this just to show how method can be called in another app domain. 
     //but it has it limits - method cannot return any values and take any parameters. 

     //apd.DoCallBack(new CrossAppDomainDelegate(MyPluginsHelper.LoadMyPlugins)); 

     //We are creating our plugin proxy/factory which will exist in another app domain 
     //and will create for us objects and return their remote 'copies'. 
     var proxy = (MyPluginFactory) apd.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap(); 

     //if we would use here method (MyPluginBase) apd.CreateInstance("SamplePlugin", "SamplePlugin.MySamplePlugin").Unwrap(); 
     //we would have to load "SamplePlugin.dll" into our app domain. We may not want that, to not waste memory for example 
     //with loading endless number of types. 
     var instance = proxy.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin"); 
     instance.DrawingControl(); 

     Console.WriteLine("Now we can recompile our SamplePlugin dll, replace it in Plugin directory and load in another AppDomain. Click Enter when you ready"); 
     Console.ReadKey(); 

     var apd2 = AppDomain.CreateDomain("My second domain", null, appDomainSetup); 
     var proxy2 = (MyPluginFactory)apd2.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap(); 
     var instance2 = proxy2.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin"); 
     instance2.DrawingControl(); 

     //Now we want to prove, that this additional assembly was not loaded to prmiary app domain. 
     DrawControlsDefault(); 

     //And that we still have the old assembly loaded in previous AppDomain. 
     instance.DrawingControl(); 

     //App domain is unloaded so, we will get exception if we try to call any of this object method. 
     AppDomain.Unload(apd); 
     try 
     { 
      instance.DrawingControl(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 

     Console.ReadKey(); 
    } 
} 

}

छाया नकल बहुत सुविधाजनक हो रहा है है।

+1

लॉकिंग के बिना असेंबली लोड करने के लिए मुझे लगता है कि छाया प्रतिलिपि का उपयोग करना बेहतर है, http://msdn.microsoft.com/en-us/library/ms404279.aspx देखें। – Maarten

+0

मैं आपके उदाहरण के बारे में उत्सुक हूं, संभवत: मौलिक ज्ञान की कमी के कारण ... यह नए शीर्ष लोड किए गए एस्प्लॉर्ड्स को 'apd' ऐपडोमेन में डिफ़ॉल्ट शीर्ष स्तर के बजाय "लागू" करता है? क्या यह सिर्फ आदेश में उल्लिखित है, जैसा कि आपने ऐपडोमेन बनाया है, तो इसके नीचे सब कुछ 'अनलोड() 'तक इसका हिस्सा है? इसके अलावा, महान लेख, हालांकि मेरे सिर से थोड़ा ऊपर है। मैंने इसका एक हिस्सा पढ़ा था जिसे फटकारा गया था और एक विज्ञापन साइट पर रखा गया था लेकिन पूरा लेख होना अच्छा लगता है। मैं आज के दौरान वास्तव में इसे सीखने की कोशिश करूंगा। इच्छा की तरह यह एक कार्यात्मक स्रोत फ़ाइल थी। – Adam

+0

मुझे यकीन नहीं है कि मैं आपका प्रश्न समझता हूं, लेकिन मैं आज अपने पुराने आवेदन से कुछ उदाहरण और टिप्पणियों के साथ खेलने की कोशिश करूंगा। –

संबंधित मुद्दे