2013-08-19 2 views
0

Пожалуйста, посмотрите на следующий кодКак запустить GUI-форму в другом потоке?

#pragma once 


    using namespace System::ComponentModel; 
    using namespace System::Collections; 
    using namespace System::Windows::Forms; 
    using namespace System::Data; 
    using namespace System::Threading; 

    /// <summary> 
    /// Summary for NotifyAlarm 
    /// </summary> 
    public ref class NotifyAlarm : public System::Windows::Forms::Form 
    { 
     int count; 


    public: 
     NotifyAlarm(void) 
     { 
      InitializeComponent(); 
      // 
      //TODO: Add the constructor code here 
      // 
      count = 10; 
     } 

    protected: 
     /// <summary> 
     /// Clean up any resources being used. 
     /// </summary> 
     ~NotifyAlarm() 
     { 
      if (components) 
      { 
       delete components; 
      } 
     } 
    private: System::Windows::Forms::Label^ label1; 
    protected: 
    private: System::Windows::Forms::Label^ secondsLabel; 
    private: System::Windows::Forms::Label^ label2; 
    private: System::Windows::Forms::Button^ sendNowBtn; 
    private: System::Windows::Forms::Button^ cancelBtn; 
    private: System::Windows::Forms::Timer^ timer1; 
    private: System::ComponentModel::IContainer^ components; 


    private: 
     /// <summary> 
     /// Required designer variable. 
     /// </summary> 


#pragma region Windows Form Designer generated code 
     /// <summary> 
     /// Required method for Designer support - do not modify 
     /// the contents of this method with the code editor. 
     /// </summary> 
     void InitializeComponent(void) 
     { 
      this->components = (gcnew System::ComponentModel::Container()); 
      this->label1 = (gcnew System::Windows::Forms::Label()); 
      this->secondsLabel = (gcnew System::Windows::Forms::Label()); 
      this->label2 = (gcnew System::Windows::Forms::Label()); 
      this->sendNowBtn = (gcnew System::Windows::Forms::Button()); 
      this->cancelBtn = (gcnew System::Windows::Forms::Button()); 
      this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); 
      this->SuspendLayout(); 
      // 
      // label1 
      // 
      this->label1->AutoSize = true; 
      this->label1->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
       static_cast<System::Byte>(0))); 
      this->label1->Location = System::Drawing::Point(13, 27); 
      this->label1->Name = L"label1"; 
      this->label1->Size = System::Drawing::Size(405, 20); 
      this->label1->TabIndex = 0; 
      this->label1->Text = L"Intruder Detected. An Email and SMS will be sent within "; 
      // 
      // secondsLabel 
      // 
      this->secondsLabel->AutoSize = true; 
      this->secondsLabel->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
       static_cast<System::Byte>(0))); 
      this->secondsLabel->ForeColor = System::Drawing::Color::Red; 
      this->secondsLabel->Location = System::Drawing::Point(408, 27); 
      this->secondsLabel->Name = L"secondsLabel"; 
      this->secondsLabel->Size = System::Drawing::Size(51, 20); 
      this->secondsLabel->TabIndex = 1; 
      this->secondsLabel->Text = L"label2"; 
      // 
      // label2 
      // 
      this->label2->AutoSize = true; 
      this->label2->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point, 
       static_cast<System::Byte>(0))); 
      this->label2->Location = System::Drawing::Point(465, 27); 
      this->label2->Name = L"label2"; 
      this->label2->Size = System::Drawing::Size(69, 20); 
      this->label2->TabIndex = 2; 
      this->label2->Text = L"seconds"; 
      // 
      // sendNowBtn 
      // 
      this->sendNowBtn->Location = System::Drawing::Point(370, 70); 
      this->sendNowBtn->Name = L"sendNowBtn"; 
      this->sendNowBtn->Size = System::Drawing::Size(75, 23); 
      this->sendNowBtn->TabIndex = 3; 
      this->sendNowBtn->Text = L"Send Now"; 
      this->sendNowBtn->UseVisualStyleBackColor = true; 
      this->sendNowBtn->Click += gcnew System::EventHandler(this, &NotifyAlarm::sendNowBtn_Click); 
      // 
      // cancelBtn 
      // 
      this->cancelBtn->Location = System::Drawing::Point(469, 70); 
      this->cancelBtn->Name = L"cancelBtn"; 
      this->cancelBtn->Size = System::Drawing::Size(75, 23); 
      this->cancelBtn->TabIndex = 4; 
      this->cancelBtn->Text = L"Cancel"; 
      this->cancelBtn->UseVisualStyleBackColor = true; 
      this->cancelBtn->Click += gcnew System::EventHandler(this, &NotifyAlarm::cancelBtn_Click); 
      // 
      // timer1 
      // 
      this->timer1->Enabled = true; 
      this->timer1->Interval = 1000; 
      this->timer1->Tick += gcnew System::EventHandler(this, &NotifyAlarm::timer1_Tick); 
      // 
      // NotifyAlarm 
      // 
      this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); 
      this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; 
      this->ClientSize = System::Drawing::Size(566, 105); 
      this->Controls->Add(this->cancelBtn); 
      this->Controls->Add(this->sendNowBtn); 
      this->Controls->Add(this->label2); 
      this->Controls->Add(this->secondsLabel); 
      this->Controls->Add(this->label1); 
      this->Name = L"NotifyAlarm"; 
      this->Text = L"NotifyAlarm"; 
      this->ResumeLayout(false); 
      this->PerformLayout(); 

     } 
#pragma endregion 
    private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) 
      { 
       count--; 

       if(count>0 || count==0) 
       { 
        secondsLabel->Text = ""+count; 
       } 
       else 
       { 
        //code removed 
       } 
      } 
private: System::Void sendNowBtn_Click(System::Object^ sender, System::EventArgs^ e) 
     { 
      timer1->Stop(); 

      //code removed 
     } 

public: System::Void showGUI() 
        { 
         this->Show(); 
        } 



}; 

Это своего рода окно уведомления, которое я пытаюсь открыть в новом thared. Причина в том, что поток по умолчанию уже перегружен. Это, как я называю это из другого потока

na = gcnew NotifyAlarm(); 
     Thread ^alertThread = gcnew Thread(gcnew System::Threading::ThreadStart(na,&NotifyAlarm::showGUI)); 
     alertThread->Start(); 

К сожалению, когда я бегу эту тему, я получаю следующую ошибку

enter image description here

Это произошло, когда код достигает здесь

if(count>0 || count==0) 
    { 
     secondsLabel->Text = ""+count; 
    } 

Как вы можете видеть, я пытаюсь обновить ярлык там.

Итак, как я могу сделать эту форму GUI запущенной в другом потоке без этих ошибок? Пожалуйста помоги.

PS: Пожалуйста, обратите внимание, что я не прихожу из .NET-культуры, вместо этого я исхожу из Java и C++. Поэтому я был бы рад, если бы вы смогли внести исправления в код или что-то в этом роде.

ответ

4

Вы должны использовать BeginInvoke для synchonize (отправка) вызов в UI-потоке:

delegate void UpdateTextDelegate(int count); 

private: void DoUpdateText(int count) 
{ 
    ISynchronizeInvoke^ i = this; 

    if (i->InvokeRequired) 
    { 
     UpdateTextDelegate^ tempDelegate = 
     gcnew UpdateTextDelegate(this, &Form1::DoUpdateText); 
     cli::array<System::Object^>^ args = gcnew cli::array<System::Object^>(1); 
     args[0] = count; 
     i->BeginInvoke(tempDelegate, args); 
     return; 
    } 

    secondsLabel->Text = count.ToString(); 
} 

Затем вы можете вызвать метод DoUpdateText изнутри другого потока.

+0

Да, похоже, что это ответ. Спасибо :) –

3

Непонятно, в какой теме происходит присвоение свойства Text. Но ясно, что это неправильная тема, вам нужно использовать метод BeginInvoke() формы или метки для маршалирования вызова.

Обратите внимание, что создать исходный объект формы перед риском рискованно. Правило заключается в том, что любой поток, фактически созданный дескриптором окна (CreateHandle()), является владельцем окна. То, что может быть неправильной нитью, если конструктор формы случайно создает ручку. Не оставляйте проблемы, создавая объект формы только методом потока.

И стоит отметить, насколько это опасно. Существенным нарушителем проблемы является класс SystemEvents, множество элементов управления подписывают событие UserPreferenceChanged. Они делают это, чтобы перекрашивать себя, когда пользователь меняет настройки темы. Это событие также срабатывает в других случаях, блокировка рабочей станции является печально известным источником проблем. Переключатель рабочего стола может запустить событие.

У SystemEvents есть незавидная задача активировать это событие на правильной нити. Он не может, вы даете ему два потока на выбор. Ваш основной поток пользовательского интерфейса и этот новый «поток gui». Один из них собирается запустить событие в неправильном направлении. Результатом является тупиковая ситуация. Это может произойти задолго до того, как ваша форма будет закрыта, и поток больше не существует.

С этим очень трудно справиться. Есть больше проблем, окно имеет свою собственную жизнь и не имеет отношений Z-порядка с остальными окнами вашего приложения. Существенная проблема заключается в том, что у нее есть привычка показывать себя под окном окно, принадлежащее основной теме. Пользователь этого не видит. Эту проблему нужно решить, сделав ее принадлежащим окну, чтобы она была гарантирована. Это тоже не сработает, вы снова получите InvalidOperationException, когда вы вызываете перегрузку Show (owner).

Очень яркие проблемы, сообщение должно быть ясным: не делайте этого. Никогда не возникает необходимости, основной поток вашей программы может обрабатывать любое количество окон. Типичная ошибка заключается в том, чтобы использовать такое окно уведомлений, чтобы скрыть недостаток кода, который работает в основном потоке, и это занимает слишком много времени, что делает GUI неактивным. Реальное решение - запустить , что код на рабочем потоке.

+0

Большое спасибо за объяснение. Это здорово. Мой основной поток перегружен не из-за окон, а из-за распознавания речи в этом потоке –

+0

Большое спасибо за объяснение. Это здорово. Мой основной поток перегружен не из-за окон, а из-за распознавания речи в этом потоке. Это программное обеспечение AI, поэтому по какой-то неизвестной причине это не позволяет части зрения компьютера отображать материал, если я помещаю распознавание речи в другой поток. Вот почему я пытаюсь перескакивать другие stuffin другие темы –

+0

Звучит для меня, как будто вы задали неправильный вопрос. Я предполагаю, что вы ищете метод SpeechRecognitionEngine.RecognizeAsync(). Программное обеспечение AI обычно не очень хорошо разбирается в потоковом режиме, не забудьте использовать Thread :: SetApartmentState() для выбора STA. –