none
Invokeを使用した別スレッドからのコントロール操作 RRS feed

  • 質問

  • お世話になっております。

    前に別スレッドからの非静的関数を使用したコントロール操作について質問し、上手くいったのですがInvokeを使用した処理にしようとしますと実行時にエラーとなってしまいます。

    おそらくDelegate関数の第一パラメータとして元のFormを渡せればいいのだと思うのですがやり方が分かりません。

    お手数ですがお分かりになる方がいらっしゃいましたらご教授ください。

    #pragma once

    namespace TestMonitor
    {
     using namespace System;
     using namespace System::ComponentModel;
     using namespace System::Collections;
     using namespace System::Windows::Forms;
     using namespace System::Data;
     using namespace System::Drawing;
     using namespace System::Threading;

     public __gc class Form1 : public System::Windows::Forms::Form
     {
     public:
      Form1(void)
      {
       InitializeComponent();
      }

     ・
     ・
     ・

    public:
     __delegate void TestttDelegate(int);

    void worker(void)
    {
      Testtt(0);
    }

    void Testtt(int a)
    {
      if (InvokeRequired)
       {
        // 別スレッドから呼び出された場合
        Invoke(new TestttDelegate(0,Testtt));  ←ここでWindowsエラー発生
        return;
      }
     this->text_Data01->Text = "test";
    }

    private: System::Void button_Start_Click(System::Object *  sender, System::EventArgs *  e)
    {
     if(0 == button_Start->get_Text()->CompareTo("START"))
     {
      button_Start->set_Text("STOP");

      Thread* oThread = new Thread(new ThreadStart(this, &Form1::worker));
      oThread->Start();
     }
     else
     {
      button_Start->set_Text("START");
     }
    }
    };
    }

    2007年2月16日 4:52

すべての返信

  • 前と同じで、TestttがFormのメソッドであれば this を指定すればいいのでは?

    >Invoke(new TestttDelegate(0,Testtt));  ←ここでWindowsエラー発生
    Object* args[] = new Object[1];
    args[0] = __box(a);
    this->Invoke(new TestttDelegate(this, Testtt), args);

    引数の指定はあっているか自信はないです。(確認もできないので。。。)
    2007年2月16日 5:31
  • 再度のご回答ありがとうございます。

    ご提示の方法でやってみました。

    まず変数argsのところでコンパイルがエラーとなってしまいましたので関数の方を引数無しに変更し、下記のようなコードになりました。

    this->Invoke(new TestttDelegate(this, Testtt));

    これでコンパイルは通ったので実行してみましたところ上記の行の実行後にプログラムが一切のコントロールを受け付けなくなってしまいました。

    デバッガ上で確認しましたところ、どうも「this」の内容が無効なようなのです。

    元のスレッド開始時点でのthisの値をグローバルな変数にでも入れておいて渡せばいいのか?と思っているのですがthisの型が分からないので困っております。

    2007年2月16日 6:11
  • >デバッガ上で確認しましたところ、どうも「this」の内容が無効なようなのです。
    その解釈は間違っているんじないかなぁ。
    thisが不正だったら Invoke すら呼べないかと。

    >関数の方を引数無しに変更
    はどこを変更しましたか?

    あと、ためしに

    this->Invoke(new TestttDelegate(this, &Form1::Testtt));

    でもだめかやってみてください。


    ちなみに
    >まず変数argsのところでコンパイルがエラーとなってしまいましたので
    はどのようなエラーが出ましたか?

    >Object* args[] = new Object[1];

    Object* args[] = new Object*[1];
    だったかも。
    2007年2月16日 6:28
  • >>関数の方を引数無しに変更
    >はどこを変更しましたか?
    下記のようにしました。

    public:
     __delegate void TestttDelegate(void);←ここ

    void worker(void)
    {
      Testtt();←ここ
    }

    void Testtt(void)←ここ
    {
      if (InvokeRequired)
       {
        // 別スレッドから呼び出された場合
        Invoke(new TestttDelegate(this,&Form1::Testtt));  ←ここでWindowsエラー発生
        return;
      }
     this->text_Data01->Text = "test";
    }

    >this->Invoke(new TestttDelegate(this, &Form1::Testtt));

    >でもだめかやってみてください。

    結果は同じでした、実行時にコントロール不可になります。

    >>まず変数argsのところでコンパイルがエラーとなってしまいましたので
    >はどのようなエラーが出ましたか?
    >
    >>Object* args[] = new Object[1];
    >は
    >Object* args[] = new Object*[1];
    >だったかも。

    エラーは「error C2691: 'System::Object' : __gc 配列の要素の型が正しくありません。」が出力されました。

    new Object*[1]にしましたところコンパイルは通りましたので引数付に戻し下記のようなコードになりましたが実行時にコントロール不可になるのは同じです。

    public:
     __delegate void TestttDelegate(int a);

    void worker(void)
    {
      Testtt(0);
    }

    void Testtt(int a)
    {
      if (InvokeRequired)
       {
        Object* args[] = new Object*[1];
        args[0] = __box(a);
        // 別スレッドから呼び出された場合
        this->Invoke(new TestttDelegate(this,&Form1::Testtt),args);  ←ここ実行後以降だんまり
        return;
      }
     this->text_Data01->Text = "test";
    }

    private: System::Void button_Start_Click(System::Object *  sender, System::EventArgs *  e)
    {
     if(0 == button_Start->get_Text()->CompareTo("START"))
     {
      button_Start->set_Text("STOP");

      Thread* oThread = new Thread(new ThreadStart(this, &Form1::worker));
      oThread->Start();
     }
     else
     {
      button_Start->set_Text("START");
     }
    }

    2007年2月16日 6:59
  • VS2005の環境しかないので、/clr:oldSyntaxでコンパイルして試してみたコードです。
    問題なく動いているようですが。

    準備:Form1上に textBox1 と button1 を用意
    ボタンを押すと現在時間を1秒間隔で表示していくシンプルなものです。
    private: 
        Thread* thread;
        // ボタンクリックイベント
        System::Void button1_Click(System::Object*  sender, System::EventArgs*  e)
        {
            if (this->thread)
                this->thread->Abort();
            this->thread = new Thread(new ThreadStart(this, &Form1::Worker));
            this->thread->Start();
        }
        // フォームClosingイベント
        System::Void Form1_Closing(System::Object*  sender,
                                   System::ComponentModel::CancelEventArgs*  e) 
        {
            if (this->thread)
                this->thread->Abort();
        }
        void Worker()
        {
            while (true)
            {
                if (this->InvokeRequired)
                {
                    Object* args[] = new Object*[1];
                    args[0] = DateTime::Now.ToString("HH:mm:ss");
                    this->Invoke(
                        new DisplayTextDelegate(this, &Form1::DisplayText), args);
                }
                else
                    this->DisplayText(DateTime::Now.ToString("HH:mm:ss"));
    
                Thread::Sleep(1000);
            }
        }
    
        __delegate void DisplayTextDelegate(String*);
        void DisplayText(String* text)
        {
            this->textBox1->Text = text;
        }
    
    2007年2月16日 12:40
  • サンプルソースのご提示ありがとうございます。

    こちらの環境でもサンプルは問題なく動作しました。

    と、するとInvokeの指定の仕方ではなく別の部分にあるようです(実際のソースには他にも色々入っているので・・・)。

    もう一度じっくり見直し、何か進展がございましたらこちらに書き込みさせていただきます。

    何度も丁寧なご回答いただき大変ありがとうございました。

    2007年2月19日 1:40