WebBrowser नियंत्रण एक नए थ्रेड में


84

मेरे पास एक सूची है उरी की जिसे मैं "क्लिक" करना चाहता हूं। इसे प्राप्त करने के लिए मैं प्रति उरी में एक नया वेब-ब्राउज़र बनाने की कोशिश कर रहा हूं। मैं उरी में एक नया थ्रेड बनाता हूं। मुझे जो समस्या हो रही है वह दस्तावेज़ से पहले थ्रेड एंड है। पूरी तरह से भरा हुआ है, इसलिए मुझे कभी भी डॉक्यूमेंट.कॉम इवेंट का उपयोग करने के लिए नहीं मिलता है। मैं इसे कैसे दूर कर सकता हूं?

var item = new ParameterizedThreadStart(ClicIt.Click); 
var thread = new Thread(item) {Name = "ClickThread"}; 
thread.Start(uriItem);

public static void Click(object o)
{
    var url = ((UriItem)o);
    Console.WriteLine(@"Clicking: " + url.Link);
    var clicker = new WebBrowser { ScriptErrorsSuppressed = true };
    clicker.DocumentCompleted += BrowseComplete;
    if (String.IsNullOrEmpty(url.Link)) return;
    if (url.Link.Equals("about:blank")) return;
    if (!url.Link.StartsWith("http://") && !url.Link.StartsWith("https://"))
        url.Link = "http://" + url.Link;
    clicker.Navigate(url.Link);
}

जवाबों:


151

आपको एक STA थ्रेड बनाना होगा जो एक संदेश लूप को पंप करता है। WebBrowser जैसे ActiveX घटक के लिए यह एकमात्र मेहमाननवाज वातावरण है। अन्यथा आपको डॉक्यूमेंट कम्‍प्‍लिकेटेड इवेंट नहीं मिलेगा। कुछ नमूना कोड:

private void runBrowserThread(Uri url) {
    var th = new Thread(() => {
        var br = new WebBrowser();
        br.DocumentCompleted += browser_DocumentCompleted;
        br.Navigate(url);
        Application.Run();
    });
    th.SetApartmentState(ApartmentState.STA);
    th.Start();
}

void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
    var br = sender as WebBrowser;
    if (br.Url == e.Url) {
        Console.WriteLine("Natigated to {0}", e.Url);
        Application.ExitThread();   // Stops the thread
    }
}

8
हाँ! बस System.Windows.Forms जोड़ें। मेरा दिन भी बचा लिया। धन्यवाद
ज़ी

4
मैं इस कोड को अपनी स्थिति के अनुकूल बनाने का प्रयास कर रहा हूं। मुझे WebBrowserऑब्जेक्ट को जीवित रखना है (राज्य / कुकीज़ आदि को बचाने के लिए) और Navigate()समय के साथ कई कॉल करने हैं। लेकिन मुझे यकीन नहीं है कि मेरे Application.Run()कॉल को कहां रखा जाए , क्योंकि यह आगे के कोड को निष्पादित करने से रोकता है। कोई सुराग?
डॉटनेट

आप वापसी Application.Exit();करने के लिए कॉल कर सकते Application.Run()हैं।
माइक डी क्लार्क

26

यहां गैर-यूआई थ्रेड पर एक संदेश लूप को व्यवस्थित करने के लिए, WebBrowserस्वचालन जैसे अतुल्यकालिक कार्यों को चलाने का तरीका बताया गया है । यह async/awaitसुविधाजनक रैखिक कोड प्रवाह प्रदान करने के लिए उपयोग करता है और एक लूप में वेब पृष्ठों का एक सेट लोड करता है। कोड एक रेडी-टू-रन कंसोल ऐप है जो आंशिक रूप से इस उत्कृष्ट पोस्ट पर आधारित है ।

संबंधित उत्तर:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApplicationWebBrowser
{
    // by Noseratio - https://stackoverflow.com/users/1768303/noseratio
    class Program
    {
        // Entry Point of the console app
        static void Main(string[] args)
        {
            try
            {
                // download each page and dump the content
                var task = MessageLoopWorker.Run(DoWorkAsync,
                    "http://www.example.com", "http://www.example.net", "http://www.example.org");
                task.Wait();
                Console.WriteLine("DoWorkAsync completed.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("DoWorkAsync failed: " + ex.Message);
            }

            Console.WriteLine("Press Enter to exit.");
            Console.ReadLine();
        }

        // navigate WebBrowser to the list of urls in a loop
        static async Task<object> DoWorkAsync(object[] args)
        {
            Console.WriteLine("Start working.");

            using (var wb = new WebBrowser())
            {
                wb.ScriptErrorsSuppressed = true;

                TaskCompletionSource<bool> tcs = null;
                WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
                    tcs.TrySetResult(true);

                // navigate to each URL in the list
                foreach (var url in args)
                {
                    tcs = new TaskCompletionSource<bool>();
                    wb.DocumentCompleted += documentCompletedHandler;
                    try
                    {
                        wb.Navigate(url.ToString());
                        // await for DocumentCompleted
                        await tcs.Task;
                    }
                    finally
                    {
                        wb.DocumentCompleted -= documentCompletedHandler;
                    }
                    // the DOM is ready
                    Console.WriteLine(url.ToString());
                    Console.WriteLine(wb.Document.Body.OuterHtml);
                }
            }

            Console.WriteLine("End working.");
            return null;
        }

    }

    // a helper class to start the message loop and execute an asynchronous task
    public static class MessageLoopWorker
    {
        public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
        {
            var tcs = new TaskCompletionSource<object>();

            var thread = new Thread(() =>
            {
                EventHandler idleHandler = null;

                idleHandler = async (s, e) =>
                {
                    // handle Application.Idle just once
                    Application.Idle -= idleHandler;

                    // return to the message loop
                    await Task.Yield();

                    // and continue asynchronously
                    // propogate the result or exception
                    try
                    {
                        var result = await worker(args);
                        tcs.SetResult(result);
                    }
                    catch (Exception ex)
                    {
                        tcs.SetException(ex);
                    }

                    // signal to exit the message loop
                    // Application.Run will exit at this point
                    Application.ExitThread();
                };

                // handle Application.Idle just once
                // to make sure we're inside the message loop
                // and SynchronizationContext has been correctly installed
                Application.Idle += idleHandler;
                Application.Run();
            });

            // set STA model for the new thread
            thread.SetApartmentState(ApartmentState.STA);

            // start the thread and await for the task
            thread.Start();
            try
            {
                return await tcs.Task;
            }
            finally
            {
                thread.Join();
            }
        }
    }
}

1
उस शानदार और जानकारीपूर्ण उत्तर के लिए धन्यवाद! यह वही है जिसकी मुझे तलाश थी। हालाँकि आपको लगता है कि (जानबूझकर?) ने डिस्पोजल () स्टेटमेंट को गलत बताया है।
वोडज़ू

@ Paweł, आप सही कह रहे हैं, उस कोड को भी संकलित नहीं किया गया :) मुझे लगता है कि एक गलत संस्करण चिपकाया गया है, जो अब तय हो गया है। इसे स्थान देने के लिए धन्यवाद। आप अधिक सामान्य दृष्टिकोण की जाँच करना चाहते हैं: stackoverflow.com/a/22262976/1768303
noseratio

मैंने इस कोड को चलाने की कोशिश की, हालांकि यह अटक गया task.Wait();। मैं कुछ गलत कर रहा हूं?
0014

1
नमस्ते, शायद आप मेरी इसमें मदद कर सकते हैं: stackoverflow.com/questions/41533997/… - विधि अच्छी तरह से काम करती है, लेकिन अगर MessageLoopWorker से पहले फ़ॉर्म को तत्काल किया गया था, तो यह काम करना बंद कर देता है।
एलेक्स नेटक्कोव

3

अतीत में मेरे अनुभव से वेबब्रोसर को मुख्य एप्लिकेशन थ्रेड के बाहर संचालन करना पसंद नहीं है।

इसके बजाय httpwebrequests का उपयोग करने का प्रयास करें, आप उन्हें एसिंक्रोनस के रूप में सेट कर सकते हैं और यह जानने के लिए प्रतिक्रिया के लिए एक हैंडलर बना सकते हैं कि यह कब का है।

कैसे के लिए उपयोग-httpwebrequest निवल एसिंक्रोनस रूप से


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

@ArtW मुझे पता है कि यह एक पुरानी टिप्पणी है, लेकिन लोग शायद इसे हल कर सकते हैंwebRequest.Credentials = CredentialsCache.DefaultCredentials;
vapcguy

@vapcguy यदि यह API है तो हाँ, लेकिन अगर यह लॉग इन करने के लिए HTML तत्वों के साथ एक वेबसाइट है, तो इसे IE कुकीज़ या कैश का उपयोग करने की आवश्यकता होगी, अन्यथा क्लाइंट को यह नहीं पता है कि Credentialsऑब्जेक्ट प्रॉपर्टी के साथ क्या करना है और कैसे भरना है HTML
कॉलिनएम

@ColinM यह पूरा पृष्ठ जिस संदर्भ में बात कर रहा है, वह HttpWebRequest ऑब्जेक्ट और C # .NET का उपयोग कर रहा है, न कि सरल HTML और फॉर्म एलिमेंट पोस्ट किए जा रहे हैं, जैसे आप जावास्क्रिप्ट / AJAX के साथ कर सकते हैं। लेकिन परवाह किए बिना, आपके पास एक रिसीवर है। और लॉग-ऑन के लिए आपको विंडोज ऑथेंटिकेशन का उपयोग करना चाहिए और IIS इसे स्वचालित रूप से, वैसे भी संभालता है। यदि आपको उन्हें मैन्युअल रूप से परीक्षण करने की आवश्यकता है WindowsIdentity.GetCurrent().Name, तो आप प्रतिरूपण को लागू करने के बाद उपयोग कर सकते हैं और यदि आप चाहें तो एडी खोज के खिलाफ परीक्षण कर सकते हैं। यह निश्चित नहीं है कि कुकीज़ और कैश का उपयोग किसी के लिए कैसे किया जाएगा।
vapcguy

@vapcguy वह प्रश्न है WebBrowserजिसके बारे में यह इंगित करता है कि HTML पृष्ठ लोड किए जा रहे हैं, ओपी ने यहां तक ​​कहा कि WebRequestजो वह चाहता है वह हासिल नहीं करेगा, इसलिए यदि कोई वेबसाइट लॉगिन के लिए HTML इनपुट की अपेक्षा करती है तो Credentialsऑब्जेक्ट सेट करना काम नहीं करेगा। इसके अतिरिक्त, जैसा कि ओपी कहता है, साइटों में फेसबुक शामिल है; विंडोज ऑथेंटिकेशन इस पर काम नहीं करेगा।
कॉलिनम

0

एक सरल समाधान जिस पर एक साथ कई WebBrowsers का संचालन होता है

  1. एक नया विंडोज फॉर्म एप्लिकेशन बनाएं
  2. बटन 1 नाम का बटन रखें
  3. टेक्स्ट बॉक्स नाम का टेक्स्ट बॉक्स रखें
  4. पाठ फ़ील्ड के गुण सेट करें: बहु-सत्य और स्क्रॉलबार दोनों
  5. निम्नलिखित बटन 1 क्लिक हैंडलर लिखें:

    textBox1.Clear();
    textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
    int completed_count = 0;
    int count = 10;
    for (int i = 0; i < count; i++)
    {
        int tmp = i;
        this.BeginInvoke(new Action(() =>
        {
            var wb = new WebBrowser();
            wb.ScriptErrorsSuppressed = true;
            wb.DocumentCompleted += (cur_sender, cur_e) =>
            {
                var cur_wb = cur_sender as WebBrowser;
                if (cur_wb.Url == cur_e.Url)
                {
                    textBox1.AppendText("Task " + tmp + ", navigated to " + cur_e.Url + Environment.NewLine);
                    completed_count++;
                }
            };
            wb.Navigate("/programming/4269800/webbrowser-control-in-a-new-thread");
        }
        ));
    }
    
    while (completed_count != count)
    {
        Application.DoEvents();
        Thread.Sleep(10);
    }
    textBox1.AppendText("All completed" + Environment.NewLine);
    
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.