जब तक विंडो हैंडल नहीं बनाया जाता है तब तक इनवोक या बिगिनव को एक नियंत्रण पर नहीं बुलाया जा सकता है


82

मेरे पास यहां पर चर्चा करने वाले एक ग्रेग डी के समान एक सेफ इंवोक कंट्रोल एक्सटेंशन विधि है (माइनस इज़हंडलेक्रिएट चेक)।

मैं इसे System.Windows.Forms.Formइस प्रकार से बुला रहा हूं :

public void Show(string text) {
    label.SafeInvoke(()=>label.Text = text);
    this.Show();
    this.Refresh();
}

कभी-कभी (यह कॉल विभिन्न थ्रेड्स से आ सकती है) इस परिणाम में निम्नलिखित त्रुटि है:

System.InvalidOperationException हुआ

Message= "जब तक विंडो हैंडल नहीं बनाया जाता है तब तक इनवोक या बिगिनवॉक को एक नियंत्रण पर नहीं बुलाया जा सकता है।"

Source= "System.Windows.Forms"

StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action) 
in C:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16

क्या चल रहा है और मैं इसे कैसे ठीक करूं? मैं जानता हूं कि यह फॉर्म निर्माण की समस्या नहीं है, क्योंकि कभी-कभी यह एक बार काम करेगा और अगली बार विफल हो जाएगा तो समस्या क्या हो सकती है?

पुनश्च। मैं वास्तव में WinForms पर भयानक हूं, क्या किसी को लेखों की एक अच्छी श्रृंखला पता है जो पूरे मॉडल को समझाती है और इसके साथ कैसे काम करना है?


1
लिंक के साथ कुछ अजीब चल रहा है ... मार्कअप और पूर्वावलोकन सही हैं ... अजीब।
जॉर्ज मौअर

शो को किस संदर्भ में कहा जाता है? क्या इसे कभी फॉर्म के निर्माता से कहा जाता है, जैसे? यह देखने के लिए कॉल के लिए संदेशों को लॉग करना उपयोगी हो सकता है कि हैंडललेक्रिएटेड ईवेंट द्वारा ट्रिगर किए गए संदेशों के खिलाफ दिखाने के लिए कि आप केवल उन ऑब्जेक्ट पर शो बुला रहे हैं जिनके पास पहले से ही उनके हैंडल बनाए गए थे।
ग्रेग डी

के लिए आवेदन क्या है / यह कैसे डिज़ाइन किया गया है? यह क्या करता है। () क्या करते हैं? (मैं मान रहा हूँ कि यह सिर्फ इस से अधिक कुछ करता है। अदृश्य = सच?) क्या आपका संदर्भ एक टाइपो वेबफॉर्म के लिए है?
ग्रेग डी

यह .Show () आधार है फार्म .how () तो जो कुछ भी करता है। संवाद को एक रचनाकार से ऊपर नहीं लाया जाता है। इसे एक इनोटिफ़ायर सेवा के कार्यान्वयन के द्वारा कहा जाता है जिसमें एक सरल अधिसूचना (स्ट्रिंग) विधि है
जॉर्ज

4
इसे फिर से देखते हुए, एक साल बाद, ऐसा लगता है कि आप त्रुटि का ठीक कारण अनुभव कर रहे हैं कि IsHandleCreatedचेक मौजूद है। आप एक नियंत्रण (जो एक संदेश भेजें) की एक संपत्ति को बदलने की कोशिश कर रहे हैं एक नियंत्रण जो अभी तक नहीं बनाया गया है। इस स्थिति में एक चीज जो आप कर सकते हैं, वह उन प्रतिनिधियों की कतार है जो नियंत्रण के निर्माण से पहले प्रस्तुत किए जाते हैं, फिर उन्हें HandleCreatedघटना में चलाएं ।
ग्रेग डी

जवाबों:


77

यह संभव है कि आप गलत धागे पर अपना नियंत्रण बना रहे हों। MSDN से निम्नलिखित प्रलेखन पर विचार करें :

इसका अर्थ है कि InvokeRequired गलत वापस आ सकता है यदि Invoke की आवश्यकता नहीं है (कॉल उसी धागे पर होती है), या यदि नियंत्रण एक अलग थ्रेड पर बनाया गया था, लेकिन नियंत्रण का हैंडल अभी तक नहीं बनाया गया है।

ऐसे मामले में जहां नियंत्रण का हैंडल अभी तक नहीं बनाया गया है, आपको बस नियंत्रण पर गुणों, विधियों या घटनाओं को कॉल नहीं करना चाहिए। यह नियंत्रण के हैंडल को पृष्ठभूमि थ्रेड पर बनाए जाने का कारण हो सकता है, एक संदेश पंप के बिना एक थ्रेड पर नियंत्रण को अलग करना और एप्लिकेशन को अस्वीकार्य बनाना।

जब आप बैकग्राउंड थ्रेड पर InvokeRequired गलत रिटर्न करते हैं, तो आप इसहैंडलेक्रिएट के मूल्य की जांच करके भी इस मामले से बचाव कर सकते हैं। यदि नियंत्रण हैंडल अभी तक नहीं बनाया गया है, तो आपको इनवोक या बिगिनवॉक को कॉल करने से पहले इंतजार करना होगा। आमतौर पर, यह केवल तब होता है जब आवेदन के लिए प्राथमिक फॉर्म के कंस्ट्रक्टर में एक बैकग्राउंड थ्रेड बनाया जाता है (जैसा कि एप्लिकेशन में दिखाया गया है।

आइए देखें कि आपके लिए इसका क्या अर्थ है। (अगर हमने SafeInvoke के आपके क्रियान्वयन को देखा हो तो यह भी आसान होगा)

मान लें कि आपका कार्यान्वयन IsHandleCreated के विरुद्ध चेक के अपवाद के साथ संदर्भित के समान है , आइए तर्क का पालन करें:

public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
    if (uiElement == null)
    {
        throw new ArgumentNullException("uiElement");
    }

    if (uiElement.InvokeRequired)
    {
        if (forceSynchronous)
        {
            uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
        else
        {
            uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
    }
    else
    {    
        if (uiElement.IsDisposed)
        {
            throw new ObjectDisposedException("Control is already disposed.");
        }

        updater();
    }
}

उस मामले पर विचार करें जहां हम SafeInvokeगैर-गुई धागे से एक नियंत्रण के लिए बुला रहे हैं जिसका हैंडल नहीं बनाया गया है।

uiElementअशक्त नहीं है, इसलिए हम जांच करते हैं uiElement.InvokeRequired। प्रति MSDN डॉक्स (बोल्ड) InvokeRequiredवापस आ जाएगी false, क्योंकि भले ही यह एक अलग धागे पर बनाया गया था, संभाल निर्मित नहीं हुआ है! यह हमें उस elseस्थिति में भेजता है जहां हम जांच करते हैं IsDisposedया तुरंत प्रस्तुत कार्रवाई को कॉल करने के लिए आगे बढ़ते हैं ... पृष्ठभूमि थ्रेड से !

इस बिंदु पर, सभी दांव फिर से बंद हो जाते हैं: वह नियंत्रण क्योंकि इसका हैंडल एक थ्रेड पर बनाया गया है जिसमें इसके लिए एक संदेश पंप नहीं है, जैसा कि दूसरे पैराग्राफ में उल्लेख किया गया है। शायद यही वह मामला है जिसका आप सामना कर रहे हैं?


आप एक के EndInvokeबाद शामिल करना चाहिए BeginInvoke?
ओडीस

@odyodyodys: लघु उत्तर: नहीं। यह एक जादुई, सुपर-विशिष्ट मामला है जहां आपको नहीं करना है। लंबे समय तक उत्तर: इस उत्तर पर टिप्पणियाँ पढ़ें: stackoverflow.com/a/714680/6932
ग्रेग डी

1
यह उत्तर और MSDN आलेख सभी के बारे में है InvokeRequired गलत वापस क्योंकि हैंडल नहीं बनाया गया है। लेकिन ओप्पो को अपवाद तब मिल रहा है जब InvokeRequired रिटर्न सही होने के बाद Beginvoke / Invoke को बुलाया जाता है। जब इन्वॉल्व अभी तक नहीं बना है, तो InvokeRequired रिटर्न सही कैसे हो सकता है?
thewpfguy

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

37

मुझे InvokeRequiredविश्वसनीय नहीं मिला , इसलिए मैं बस उपयोग करता हूं

if (!this.IsHandleCreated)
{
    this.CreateHandle();
}

5
क्या यह कारण अलग-अलग धागों पर बनाए जा रहे दो हैंडल नहीं हो सकते हैं? हैंडल को बनाया जाना चाहिए, आपको बस अपने समय / घटनाओं के क्रम में सुधार करना होगा ..
Denise Skidmore

नाइस - मैं इसे सिर्फ इस तक पहुँचने के लिए पसंद करता हूं। (ए) के रूप में आपके पास अप्रयुक्त चर नहीं है और (ख) यह स्पष्ट है कि क्या चल रहा है
डन

5
MSDN: "उस स्थिति में जहां नियंत्रण का हैंडल अभी तक नहीं बनाया गया है, आपको नियंत्रण पर गुणों, विधियों, या कॉल को कॉल नहीं करना चाहिए। इससे नियंत्रण का हैंडल पृष्ठभूमि थ्रेड पर बनाया जा सकता है, नियंत्रण पर नियंत्रण को अलग कर सकता है। एक संदेश पंप के बिना धागा और आवेदन अस्थिर बना रही है। " अभ्यास का पूरा बिंदु गलत धागे पर हैंडल बनाने से बचने के लिए है। यदि यह कॉल एक ऐसे धागे से होता है, जो गुई धागा नहीं है, तो आप मर चुके हैं।
ग्रेग डी

25

यहाँ एक ऐसे ही सवाल का जवाब है :

मुझे लगता है कि (अभी तक पूरी तरह से यकीन नहीं है) कि यह इसलिए है क्योंकि InvokeRequired हमेशा गलत वापस आ जाएगा यदि नियंत्रण अभी तक लोड नहीं किया गया है / दिखाया गया है। मैंने एक वर्कअराउंड किया है जो इस समय के लिए काम करता है, जो कि इसके निर्माता में संबद्ध नियंत्रण के सरल संदर्भ के लिए है, जैसे:

var x = this.Handle; 

( Http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html देखें )


बहुत ही रोचक लेख btw। धन्यवाद।
यन ट्रेविन

धन्यवाद, इसने मेरे लिए काम किया क्योंकि मेरे पास एक छिपा हुआ रूप था जिसे पृष्ठभूमि के धागे से एनिमेटेड होना चाहिए। हैंडल को संदर्भित करना वह चीज थी जो मेरे लिए काम कर रही थी
जॉन मैक

यह अभी भी .net के नवीनतम संस्करणों में एक समस्या है, हालांकि यह "फीचर" से कम त्रुटि है। ध्यान देने योग्य बात यह है कि वस्तु पर "घड़ी" लगाना और उसके गुणों को ब्राउज़ करना भी हैंडल को देखने के समान है। आप कुछ क्वांटम डिबगिंग बकवास के साथ हवा देते हैं जहां आप इसे देखते समय काम करते हैं।
टोनी चीथम

5

पोस्ट में वह विधि जिसे आप कॉल से लिंक करते हैं Invoke/ BeginInvokeचेक करने से पहले यदि कंट्रोल का हैंडल उस स्थिति में बनाया गया है जहां इसे उस थ्रेड से कॉल किया जा रहा है जो कंट्रोल नहीं बनाता है।

जब आपकी विधि नियंत्रण बनाने वाले के अलावा किसी थ्रेड से कॉल की जाती है, तो आपको अपवाद मिलेगा। यह रीमोट करने की घटनाओं या कतारबद्ध कार्य उपयोगकर्ता मदों से हो सकता है ...

संपादित करें

यदि आप जांच करते हैं InvokeRequiredऔर HandleCreatedआह्वान करने से पहले आपको वह अपवाद नहीं मिलना चाहिए।


अगर मैं सही तरीके से समझूं, तो आप कह रहे हैं कि यह तब होगा जब इन्वोकेशन थ्रेड नियंत्रण पर बनाए गए से अलग हो। मैं इस बात की गारंटी नहीं दे सकता कि घटना को किस थ्रेड से बुलाया जाएगा। यह एक ऐसा हो सकता है जो इसे बनाया (लाइकेलियर) एक पूरी तरह से अलग धागा है। मैं इसका कैसे समाधान करूं?
जॉर्ज माउर

हाँ, यह सही है। मैंने पोस्ट को एक ऐसी स्थिति के साथ संपादित किया जिसमें समस्या को ठीक करना चाहिए।
शी

मैं आश्वस्त नहीं हूं कि यह मामला है। मैंने आपके प्रश्न, अर्नशी के आधार पर अपना प्रश्न अपडेट किया है।
ग्रेग डी

मुझे समझ नहीं आ रहा है। मुझे यह दिखाने के लिए कि खिड़की की आवश्यकता है, मुझे यह स्पष्ट नहीं है कि IsHandleCreated गलत क्यों है, लेकिन विंडो शो अप नहीं होना एक विकल्प नहीं है, मेरा सवाल यह है कि दुनिया में यह गलत क्यों होगा
जॉर्ज मौअर

मुझे विश्वास है कि यदि हैंडल बंद हो गया / नियंत्रण समाप्त हो गया तो IsHandleCreated झूठा वापस आ जाएगा। क्या आप निश्चित हैं कि आप एक नियंत्रण पर एक अतुल्यकालिक आह्वान द्वारा काटे नहीं जा रहे हैं जो मौजूद थे, लेकिन अब और नहीं?
ग्रेग डी

3

यदि आप Controlअन्य चीजों को दिखाने या करने से पहले किसी अन्य थ्रेड से उपयोग करने जा रहे हैं Control, तो कंस्ट्रक्टर के भीतर इसके हैंडल के निर्माण के लिए मजबूर करने पर विचार करें। यह CreateHandleफ़ंक्शन का उपयोग करके किया जाता है ।

मल्टी-थ्रेडेड प्रोजेक्ट में, जहां "कंट्रोलर" लॉजिक एक WinForm में नहीं है, यह फ़ंक्शन Controlकंस्ट्रक्टर्स में इस त्रुटि से बचने के लिए महत्वपूर्ण है ।



1

इसके निर्माता में संबंधित नियंत्रण के हैंडल को संदर्भित करता है, जैसे:

नोट : इस समाधान से सावधान रहें। यदि एक नियंत्रण के पास एक हैंडल होता है, तो यह काम करने के लिए बहुत धीमा होता है जैसे कि इसका आकार और स्थान निर्धारित करना। यह InitializeComponent को बहुत धीमा बनाता है । एक बेहतर उपाय यह है कि नियंत्रण से पहले कुछ भी पृष्ठभूमि में न रखें।


0

मुझे इस तरह के सरल रूप से यह समस्या थी:

public partial class MyForm : Form
{
    public MyForm()
    {
        Load += new EventHandler(Form1_Load);
    }

    private void Form1_Load(Object sender, EventArgs e)
    {
        InitializeComponent();
    }

    internal void UpdateLabel(string s)
    {
        Invoke(new Action(() => { label1.Text = s; }));
    }
}

तब nअन्य async थ्रेड्स के लिए मैं new MyForm().UpdateLabel(text)UI थ्रेड को कॉल करने और कॉल करने के लिए उपयोग कर रहा था , लेकिन कंस्ट्रक्टर UI थ्रेड इंस्टेंस को कोई हैंडल नहीं देता है, इसलिए अन्य थ्रेड्स को अन्य इंस्टेंस हैंडल मिलते हैं, जो Object reference not set to an instance of an objectया तो हैं Invoke or BeginInvoke cannot be called on a control until the window handle has been created। इसे हल करने के लिए मैंने UI हैंडल को रखने के लिए एक स्थिर ऑब्जेक्ट का उपयोग किया:

public partial class MyForm : Form
{
    private static MyForm _mf;        

    public MyForm()
    {
        Load += new EventHandler(Form1_Load);
    }

    private void Form1_Load(Object sender, EventArgs e)
    {
        InitializeComponent();
        _mf = this;
    }

    internal void UpdateLabel(string s)
    {
        _mf.Invoke((MethodInvoker) delegate { _mf.label1.Text = s; });
    }
}

मुझे लगता है कि यह ठीक काम कर रहा है, अब तक ...


0
var that = this; // this is a form
(new Thread(()=> {

    var action= new Action(() => {
       something
    }));

    if(!that.IsDisposed)
    {
        if(that.IsHandleCreated)
        {
            //if (that.InvokeRequired)
                that.BeginInvoke(action);
            //else
            // action.Invoke();
        }
        else
            that.HandleCreated+=(sender,event) => {
                action.Invoke();
            };
    }


})).Start();

यह सी # है - thisमंगलाचरण के आधार पर भिन्न नहीं होता है, कि जावास्क्रिप्ट-शैली तकनीक अनावश्यक होनी चाहिए।
जॉर्ज मौअर

यकीन है कि स्पष्ट करने की कोशिश की क्या पर लागू करने के लिए। - जो भी हो
शिमोन डूडकिन

0

इस बारे में क्या :


    public static bool SafeInvoke( this Control control, MethodInvoker method )
    {
        if( control != null && ! control.IsDisposed && control.IsHandleCreated && control.FindForm().IsHandleCreated )
        {
            if( control.InvokeRequired )
            {
                control.Invoke( method );
            }
            else
            {
                method();
            }
            return true;
        }
        else return false;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.