InvokeRequired формы == false и InvokeRequired содержимого элемента управления == true - PullRequest
7 голосов
/ 25 октября 2010

как это возможно?У меня есть элемент управления Windows Form, полученный из System.Windows.Forms.Form с элементом управления WebBrowser, содержащийся в этой форме.Экземпляр объекта Webbrowser создается в конструкторе формы (в методе InitializeComponent ()).Затем в фоновом потоке я манипулирую содержимым WebBrowser и обнаружил, что в некоторых случаях Form.InvokeRequired == false, а WebBrowser.InvokeRequired == true.Как это может быть?

Ответы [ 3 ]

9 голосов
/ 25 октября 2010

Form.InvokeRequired возвращает false до отображения формы.

Я сделал простой тест:

Form2 f2 = new Form2();
Thread t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();

f2.Show();

t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();

с помощником

private void PrintInvokeRequired(Form form)
{
    Console.WriteLine("IsHandleCreated: " + form.IsHandleCreated + ", InvokeRequired: " + form.InvokeRequired);
}

вывод

IsHandleCreated: False, InvokeRequired: False
IsHandleCreated: True, InvokeRequired: True

Также обратите внимание, что это несколько документировано на MSDN :

Если дескриптор элемента управления еще не существует, InvokeRequired выполняет поиск в родительской цепочке элемента управления, пока не найдет элемент управления или форму, которая имеет дескриптор окна.Если не удается найти соответствующий дескриптор, метод InvokeRequired возвращает значение false.

Это означает, что InvokeRequired может возвращать значение false, если Invoke не требуется (вызов происходит в том же потоке), или если элемент управления был создан вдругой поток, но дескриптор элемента управления еще не создан.

В случае, когда дескриптор элемента управления еще не создан, вы не должны просто вызывать свойства, методы или события в элементе управления.Это может привести к созданию дескриптора элемента управления в фоновом потоке, изолируя элемент управления в потоке без насоса сообщений и делая приложение нестабильным.

Вы можете защитить от этого случая, также проверив значение IsHandleCreated, когдаInvokeRequired возвращает значение false в фоновом потоке.Если дескриптор элемента управления еще не создан, вы должны подождать, пока он не будет создан, прежде чем вызывать Invoke или BeginInvoke.Как правило, это происходит только в том случае, если фоновый поток создается в конструкторе первичной формы для приложения (как в Application.Run (new MainForm ()), до того, как форма была показана или был вызван Application.Run.

Ваше решение также состоит в том, чтобы проверить IsHandleCreated.

Редактировать:
Handle может быть создан в любое время внутри элемента управления WebBrowser.или внешне. Это не создает автоматически описатель родительской формы.

Я создал пример:

public Form2()
{
    InitializeComponent();

    Button button1 = new Button();
    this.Controls.Add(button1);

    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
    var tmp = button1.Handle; // Forces the Handle to be created.
    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
}

с выводом:

button1:False this: False
button1: True this: False

1 голос
/ 25 октября 2010

Вот подробное исследование соответствующей и более общей проблемы: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

0 голосов
/ 17 февраля 2011

Я расследовал то же самое странное поведение.Мне нужно управлять некоторыми элементами управления из разных потоков (например, показывать информацию об устройстве, подключенном к хосту, или запускать действия в зависимости от состояния различных устройств).

Эта ссылка дала мне хороший совет: http://csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx

Я до сих пор не знаю, как люди из MS намеревались использовать свои собственные вещи (и во многом не согласны), но в одном приложении мне пришлось сделать следующий грязный и грязный обходной путь:

  • Создайте элемент управления / форму в главном потоке (убедитесь, что это основной поток).
  • В той же процедуре проверьте дескриптор элемента управления.Эта простая проверка заставит его создать и в нужном потоке!

Как уродливо, не так ли?Я хотел бы знать, есть ли у кого-нибудь еще лучший способ сделать это.

_my_control = new ControlClass( );
_my_control.Owner = this;

IntPtr hnd;

// Force Handle creation by reading it.
if ( !_my_control.IsHandleCreated || _my_control.Handle == IntPtr.Zero )
    hnd = _my_control.Handle;

(Извините за публикацию в этом несколько старом сообщении, но я просто подумал, что это может быть кому-то полезно).*

...