2009-09-28 20 views
7

रनटाइम पर अनलोड नहीं करेगा, मैं एक डीएलएल को अनलोड करने और इसके संशोधित संस्करण को फिर से लोड करने में सक्षम होना चाहता हूं। मेरा पहला प्रयोग आग में चला गया। क्या कोई मुझे बता सकता है क्यों?मेरा ऐप डोमेन

private static void Main() 
{ 
    const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll"; 

    // Starting out with a version of MyLibrary.dll which only has 1 method, named Foo() 
    AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath); 
    AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); 
    appDomain.Load(assemblyName); 
    appDomain.DomainUnload += appDomain_DomainUnload; 
    AppDomain.Unload(appDomain); 

    // Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method, named Goo() 
    AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath); 
    AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); 
    Assembly asm2 = appDomain2.Load(assemblyName2); 

    foreach (Type type in asm2.GetExportedTypes()) 
    { 
     foreach (MemberInfo memberInfo in type.GetMembers()) 
     { 
     string name = memberInfo.Name; 
     // Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo. 
     } 
    } 
} 

private static void appDomain_DomainUnload(object sender, EventArgs e) 
{ 
    // This gets called before the first breakpoint 
} 

संपादित करें:

ठीक है, यह स्पष्ट रूप से मेरी पहली बार पोस्टिंग है। मेरे कोड को स्वरूपित करने के लिए धन्यवाद डैनियल (अब मैं ऐसा करने के लिए टूलबार बटन देखता हूं, और पूर्वावलोकन फलक भी!)। मुझे उन लोगों के जवाब में "टिप्पणी" पोस्ट करने का कोई तरीका नहीं दिख रहा है, जिन्होंने मूल पोस्ट या 1 उत्तर के लिए स्पष्टीकरण मांगा है, इसलिए मैं वार्तालाप जारी रखने के लिए बस एक और "उत्तर" पोस्ट करूंगा। (पॉइंटर्स ने इस बात की सराहना की कि टिप्पणियां कैसे की जाती हैं) की सराहना की जाएगी।

पोस्टिंग पर टिप्पणियां: मिच - आग के कारण में नीचे आ गया मेरा फोरच लूप संशोधित डीएलएल में प्रकारों पर फिर से चल रहा था, पहले लोड/अनलोड किए गए नहीं। मैथ्यू - यह काम कर सकता है, लेकिन मुझे वास्तव में काम करने के लिए उसी फ़ाइल नाम के मामले की आवश्यकता है। माइक टू - दृढ़ता से नाम नहीं दिया गया।

उत्तर पर टिप्पणियां: ब्लू & माइक टू - मैं आपके सुझावों पर नूडल करूंगा, लेकिन पहले मुझे एक महत्वपूर्ण पहलू को समझने की आवश्यकता है। मैंने पढ़ा था कि आपको मुख्य एप डोमेन में असेंबली खींचने की परवाह नहीं है, और पहले कोड को अनलोड से पहले फ़ोरैच लूप की प्रतिलिपि थी। इसलिए, संदेह है कि MethodInfos को एक्सेस करना मुख्य ऐप डोमेन में असेंबली को चूस रहा था, मैंने लूप हटा दिया। यही वह समय था जब मुझे पता था कि मुझे मदद मांगने की ज़रूरत है, 'पहला डीएलएल अभी भी अनलोड नहीं होगा!

तो मेरे सवाल है: मुख्य अनुप्रयोग डोमेन का कारण बनता है निम्नलिखित कोड खंड में क्या कभी सीधे (या परोक्ष रूप से) का उपयोग dll में कुछ भी ... यह क्यों कारण होगा करने के लिए विधानसभा को भी मुख्य अनुप्रयोग में लोड करने के लिए डोमेन:

appDomain.Load(assemblyName); 
appDomain.DomainUnload += appDomain_DomainUnload; 
AppDomain.Unload(appDomain); 

मुझे बहुत आत्मविश्वास मैंने कभी वास्तव में यह उतारने से पहले DLL उपयोग करने में सक्षम हो जाएगा नहीं दिया।


संपादित करें 2:

माइक दो - अपने हठ ... DoCallBack के भीतर से विधानसभा लोड हो रहा है के लिए धन्यवाद गुप्त सॉस था। किसी भी दिलचस्पी के लिए, यहां कुछ और जानकारी दी गई है जो सड़क के नीचे उपयोगी हो सकती हैं:

लगता है जैसे कोई भी वास्तव में मेरी शर्तों को पुन: पेश नहीं कर सकता है। मूल समस्या का प्रदर्शन करने के लिए, मैंने इस तरह से अपने डीएलएस उत्पन्न किए: 1. समाधान के लिए क्लास लाइब्रेरी प्रोजेक्ट जोड़ा गया 2. फू के साथ निर्मित संस्करण 1.0.0; परिणामस्वरूप असेंबली MyLibrary.dll.f के रूप में बदल दिया गया। 3. गुओ को नामित फू और एक और संस्करण 1.0.0 बनाया; परिणामस्वरूप असेंबली MyLibrary.dll.g के रूप में बदल दिया गया। 4. समाधान से हटाया गया परियोजना। रन शुरू करने से पहले, मैंने .f को हटा दिया और ब्रेकपॉइंट पर चला गया (अनलोड के बाद पहली पंक्ति)। तब मैंने .f को वापस चालू कर दिया और दूसरे डीएल से .g हटा दिया और अगले ब्रेकपॉइंट पर चला गया। विंडोज ने मुझे नाम बदलने से नहीं रोका। नोट: हालांकि यह बेहतर अभ्यास होगा, मैंने संस्करण संख्या को नहीं बदला क्योंकि मैं अपने ग्राहकों को हमेशा यह नहीं मानना ​​चाहता था, क्योंकि Assembly इन्फ्लू में डिफ़ॉल्ट प्रविष्टि वाइल्डकार्ड संस्करण नहीं है। यह संभालने के लिए उलझन मामले की तरह लग रहा था।

इसके अलावा, मैं अभी-अभी कुछ है कि जल्दी ही में मुझे clued है | पता चला:

AssemblyName assemblyName = AssemblyName.GetAssemblyName(FullPath); 
AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); 
appDomain.Load(assemblyName); 
Assembly[] tempAssemblies = appDomain.GetAssemblies(); 
// MyLibrary.dll has been loaded into the temp domain...good 
Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 
// MyLibrary.dll has been loaded into the main domain, too...bad! 

तो मुझे यकीन है कि क्या AppDomain.Load की बात है नहीं कर रहा हूँ, लेकिन यह बोनस पक्ष प्रभाव है लगता है असेंबली को मुख्य ऐप डोमेन में भी लोड करने का। इस प्रयोग का उपयोग करना, मैं माइक दो समाधान देख सकते हैं सफाई से केवल अस्थायी डोमेन में लोड:

AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); 
appDomain.DoCallBack(CallBackDelegate); // Executes Assembly.LoadFrom 
Assembly[] tempAssemblies = appDomain.GetAssemblies(); 
// MyLibrary.dll has been loaded into the temp domain...good 
Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 
// MyLibrary.dll has NOT been loaded into the main domain...great! 

तो, माइक दो, वास्तव में कैसे इस StackOverflow नौसिखिया के रूप में "स्वीकार किए जाते हैं" अपने जवाब को चिह्नित करता है? या मैं ऐसा नहीं कर सकता क्योंकि मैं यहां केवल अतिथि हूं?

अब मैं वास्तव में का उपयोग सीखने के लिए बंद हूं MyLibrary मुख्य ऐप डोमेन में असेंबली को चूसने के बिना। भाग लेने के लिए सभी को धन्यवाद।


संपादित करें 3:

BlueMonkMN - मैं आगे चला गया और घटना सदस्यता बाहर ले गई और एक ही परिणाम मिलता है। पूरा कार्यक्रम अब नीचे सूचीबद्ध है:

const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll"; 

// Starting out with a version of MyLibrary.dll which only has 1 method, named Foo() 
AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath); 
AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); 
appDomain.Load(assemblyName); 
AppDomain.Unload(appDomain); 

// Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method, named Goo() 
AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath); 
AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); 
Assembly asm2 = appDomain2.Load(assemblyName2); 

foreach (Type type in asm2.GetExportedTypes()) 
{ 
    foreach (MemberInfo memberInfo in type.GetMembers()) 
    { 
     string name = memberInfo.Name; 
     // Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo. 
    } 
} 

लगता है जैसे कोई रास्ता विधानसभा इन 2 लाइनों के बीच मुख्य अनुप्रयोग डोमेन में खींच लिया जा रहा है होना चाहिए:

appDomain.Load(assemblyName); 
AppDomain.Unload(appDomain); 
+0

क्या आप "आग में नीचे चला गया" समझा सकते हैं? –

+0

मैं जांचता हूं कि आप वास्तव में दूसरी असेंबली को देख रहे हैं। उन्हें एक ही नाम बनाने के बजाय उन्हें दो अलग-अलग फ़ाइल नामों के रूप में आज़माएं। –

+0

असेंबली मजबूत नामित हैं? जीएसी में एक असेंबली डाउनलोड कैश है जो मजबूत नामित असेंबली पर निर्भर करता है। आप जांच सकते हैं कि यह gacutil/ldl चलाकर वहां है और gacutil/cdl चलाकर इसे साफ़ करें। –

उत्तर

5

मैंने इसे दोहराने की कोशिश की। लोड करने के लिए डीएल में (MyLibrary.dll) मैंने दो संस्करण बनाए। पहले में एक वर्ग था जिसमें फू नामक एक विधि थी और इसका संस्करण 1.0.0.0 था। दूसरे के पास एक ही कक्षा थी लेकिन विधि का नाम बदल दिया गया था बार (मैं एक परंपरावादी हूं) और 2.0.0.0 का संस्करण संख्या।

मैंने अनलोड कॉल के बाद ब्रेकपॉइंट लगाया। फिर मैंने पहले संस्करण के शीर्ष पर दूसरे संस्करण की प्रतिलिपि बनाने की कोशिश की। मुझे लगता है कि आप यही कर रहे हैं क्योंकि पथ कभी नहीं बदलता है। विंडोज़ मुझे संस्करण 1 पर संस्करण 2 की प्रतिलिपि नहीं करने देगा। डीएलएल बंद कर दिया गया था।

मैंने DoCallback का उपयोग कर AppDomain के अंदर निष्पादित कोड का उपयोग करके डीएल लोड करने के लिए कोड बदल दिया। वह काम किया। मैं डीएलएल को स्वैप कर सकता हूं और नई विधि ढूंढ सकता हूं। कोड यहाँ है।

class Program 
{ 
    static void Main(string[] args) 
    { 
     AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); 
     appDomain.DoCallBack(loadAssembly); 
     appDomain.DomainUnload += appDomain_DomainUnload; 

     AppDomain.Unload(appDomain); 

     AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); 
     appDomain2.DoCallBack(loadAssembly); 
    } 

    private static void loadAssembly() 
    { 
     string fullPath = "LoadMe1.dll"; 
     var assembly = Assembly.LoadFrom(fullPath); 
     foreach (Type type in assembly.GetExportedTypes()) 
     { 
      foreach (MemberInfo memberInfo in type.GetMembers()) 
      { 
       string name = memberInfo.Name; 
       Console.Out.WriteLine("name = {0}", name); 
      } 
     } 
    } 

    private static void appDomain_DomainUnload(object sender, EventArgs e) 
    { 
     Console.Out.WriteLine("unloaded"); 
    } 
} 

मैंने असेंबली का मजबूत नाम नहीं बनाया। यदि आप करते हैं तो आपको पहले कैश किए जाने की संभावना है। आप कमांड लाइन से gacutil/ldl (सूची डाउनलोड कैश) चलाकर बता सकते हैं। यदि आपको डाउनलोड कैश साफ़ करने के लिए इसे gacutil/cdl चलाया जाता है।

1

मैं ने पाया है कि यदि आप असेंबली में सीधे पहुंच प्रकारों को सीधे अपने डोमेन में लोड किया जाता है। तो मुझे जो करना है वह एक तीसरी असेंबली है जो दोनों असेंबली के लिए आम इंटरफेस लागू करती है। वह असेंबली दोनों डोमेन में लोड हो जाती है। फिर बाहरी असेंबली के साथ बातचीत करते समय केवल उस तीसरे असेंबली से इंटरफेस का उपयोग करने के लिए सावधान रहें। इससे आपको अपने डोमेन को उतारकर दूसरी असेंबली को उतारने की अनुमति मिलनी चाहिए।

+3

मुख्य डोमेन में प्रकार खींचने से बचने का एक और तरीका है AppDomain.DoCallBack का उपयोग करना । यह आपको लोड किए गए डोमेन में कोड निष्पादित करने की अनुमति देता है। ओपी के उदाहरण में foreach ... लोड किया गया AppDomain को पारित किया जा सकता है। –

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