2020年8月アーカイブ

VC++フォームでスレッドといっても、余程大きなプロジェクトじゃないと、効果は無いのでは。と思ったりもします。

ただ前回デリゲートをやった事だし、スレッドもデリゲートにほぼ同じだし、動作確認だけでも何とかしたいとDOS窓(コマンドプロンプト)にcl入れてコンパイル。

試しに、VC++フォームでサブスレッドを呼び出すだけのサンプルを作ってみました。


<注意点>

・前回のサンプルに、スレッド部分を追加しただけです。

・メインスレッド、サブスレッド1、サブスレッド2とあるので、競合違反が起きても良さそうな気がするのですが、何故か今のところ例外は発生していません。

・リッチテキストへの書き込みで、競合自体は起きているようです。


【サブスレッド生成】

Thread ^newThread1 = gcnew Thread(gcnew ThreadStart(this, &suForm::subThread1));

上記のスレッド生成を、下記の様にすると、デリゲートと同様であると分かるような気が・・・。

ThreadStart ^delethr = gcnew ThreadStart(this, &suForm::subThread1);
Thread ^newThread1 = gcnew Thread(delethr);

ややこしいのは、2行目でスレッドの呼び出しをカプセル化(多分、だと思う)してるところでしょうか。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- VC++ Form 作成 ----------------- コマンドライン --------------------*/

D:\vc2008\dllchk>cl /clr testpp.cpp /link /subsystem:windows /entry:mainCRTStartup

/*----------------------------------------------------------------------------*/


/*---- testpp.cpp -------------------- お試しソース ------------------------*/

#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>

using namespace System::Windows::Forms;
using namespace System::Threading;

ref class suText : public System::Windows::Forms::RichTextBox {};
ref class suButton : public System::Windows::Forms::Button {};

ref class suForm : public System::Windows::Forms::Form {
public :
    suForm() {
        initCompo();
    }
    ~suForm() {}

private :
    suText ^text1;
    suButton ^button1;
    suButton ^button2;
    System::Windows::Forms::Label ^label1;

    System::Drawing::Point pt;
    System::String ^str;

    void initCompo()
    {
        text1 = gcnew suText;
        text1->Location = System::Drawing::Point(50, 50);
        text1->Size = System::Drawing::Size(100, 150);
        text1->BackColor = System::Drawing::Color::FromArgb(255, 255, 180);
        text1->Name = "text1";
        text1->Text = "求む入力!";
        text1->MouseDown += gcnew System::Windows::Forms::MouseEventHandler(this, &suForm::text1_MouseDown);

        button1 = gcnew suButton;
        button1->Location = System::Drawing::Point(75, 220);
        button1->Size = System::Drawing::Size(70, 20);
        button1->BackColor = System::Drawing::Color::FromArgb(180, 255, 255);
        button1->Name = "button1";
        button1->Text = "未確認";
        button1->Click += gcnew System::EventHandler(this, &suForm::button1_Click);

        button2 = gcnew suButton;
        button2->Location = System::Drawing::Point(160, 220);
        button2->Size = System::Drawing::Size(70, 20);
        button2->BackColor = System::Drawing::Color::FromArgb(180, 255, 255);
        button2->Name = "button2";
        button2->Text = "スレッド";
        button2->Click += gcnew System::EventHandler(this, &suForm::button2_Click);

        label1 = gcnew Label;
        label1->Location = System::Drawing::Point(200, 120);
        label1->Size = System::Drawing::Size(150, 20);
        label1->Name = "label1";
        label1->Text = "コンパイルエラーが来たぁ";

        this->Location = System::Drawing::Point(50, 50);
        this->ClientSize = System::Drawing::Size(400, 300);
        this->StartPosition = FormStartPosition::Manual;
        this->Name = "form1";
        this->Text = "サンプルプログラム";
        this->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &suForm::form1_Paint);

        this->Controls->Add(text1);
        this->Controls->Add(button1);
        this->Controls->Add(button2);
        this->Controls->Add(label1);
    }

    System::Void text1_MouseDown(System::Object ^sender, System::Windows::Forms::MouseEventArgs ^event) {
        pt = System::Drawing::Point(event->X, event->Y);
        str = "X = " + event->X + ", Y = " + event->Y + ", Button = " + event->Button.ToString();
        Invalidate();
    }

    System::Void button1_Click(System::Object ^sender, System::EventArgs ^event) {
        label1->Text = "ボタンが押されました。";
        label1->ForeColor = System::Drawing::Color::Red;
        button1->Text = "押下";
        button1->Enabled = false;
    }

    System::Void form1_Paint(System::Object ^sender, System::Windows::Forms::PaintEventArgs ^e) {
        System::Drawing::Graphics ^g = e->Graphics;
        System::Drawing::Brush ^brush = gcnew System::Drawing::SolidBrush(System::Drawing::Color::Blue);
        g->DrawString(str, Font, brush, 200, 160);
        g->FillRectangle(brush, pt.X + 150, pt.Y, 10, 10);
        label1->Text = "コンパイルエラーが来たぁ";
        label1->ForeColor = System::Drawing::Color(ForeColor);
        button1->Text = "未確認";
        button1->Enabled = true;
    }

    System::Void button2_Click(System::Object ^sender, System::EventArgs ^event) {
        button2->Text = "開始";
        button2->Enabled = false;
        Thread ^newThread1 = gcnew Thread(gcnew ThreadStart(this, &suForm::subThread1));
        newThread1->IsBackground = true;
        Thread ^newThread2 = gcnew Thread(gcnew ThreadStart(this, &suForm::subThread2));
        newThread2->IsBackground = true;
        newThread1->Start();
        newThread2->Start();
    }

    System::Void subThread1() {
        for(int i = 0; i < 5; i++) {
            text1->Text += ", i = " + i.ToString();
            Thread::Sleep(1000);
        }
        button2->Text = "スレッド";
        label1->Text = "例外発生1?";
    }

    System::Void subThread2() {
        for(int j = 5; j < 15; j++) {
            text1->Text += ", j = " + j.ToString();
            Thread::Sleep(500);
        }
        button2->Enabled = true;
        label1->Text = "例外発生2?";
    }
};

void main()
{
    Application::Run(gcnew suForm);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

VC++フォームのUI、何とかしたいのだけれど、ずぼらのめんどくさがり屋はIDEを避けて通りたいので、手打ち&DOS窓(コマンドプロンプト)にcl入れてコンパイル。

試しに、VC++フォームでUIのイベント処理を呼び出すだけのサンプルを作ってみました。


<注意点>

・下記clコマンドラインだと、エクスプローラから起動してもDOS窓は表示されません(つまり、Windows アプリケーションとして作成されたらしい)。

・デザイナを利用すれば、「デリゲートオブジェクトのインスタンス化」「イベント発生元にデリゲートを追加」「発生イベントを処理する空のメソッド」を三点セットで自動生成してくれるようです。

・Invalidate() はコントロール領域を無効化するので、ソフト的にイベント(再描画要求)を発生させる事が出来ます。

・最小化したフォーム画面の再描画後の状態は、手抜きです。


【デリゲートの概要】

・デリゲートは、CやC++の関数へのポインタに似た機能です。
・デリゲートのメソッド呼出しは、関数へのポインタと同様です。
・デリゲート自体、オブジェクトです。

①デリゲートの宣言
修飾子 delegate 戻り値の型 デリゲート型名(引数リスト); *1 *2

②暗黙的なデリゲートコンストラクタ
デリゲート型名(対象オブジェクト、メソッドのポインタ);

③デリゲートの実体化(インスタンス生成と追加)
デリゲート型名 ^変数名 = gcnew デリゲート型名(対象オブジェクト、メソッドのポインタ); *3

④デリゲートの使用例
private delegate void deletypename(System::String ^s);

ref class test {
public :
    void method(System::String ^str) {
        System::Console::WriteLine("String = " + str);
    }
};

void main()
{
    test ^test1 = gcnew test;
    deletypename ^callptr = gcnew deletypename(test1, &test::method);

    callptr("文字列表示。");
}

⑤実行結果
String = 文字列表示。


【イベントの概要】

・イベントの目的は、デリゲート型のフィールド(変数)を非公開にすることです。

・つまり、デリゲート型のオブジェクトを直接操作させること無く、コールバックの仕組みを実現することです *4。

①イベントの宣言
修飾子 event デリゲート型名 ^イベント名;


【上記を前提に、VC++フォームのUIイベントに当て嵌めると】

・対象のコントロールが公開しているイベントにデリゲートを登録(追加)する事で、イベント処理が呼び出されます。

・Click イベント
public delegate void EventHandler(Object ^sender, EventArgs ^e);
public event EventHandler ^Click;

button1->Click = gcnew EventHandler(this, &Form1::button1_Click);

(ボタンが押された時、コントロールから呼ばれるメソッド)
void button1_Click(Object ^sender, EventArgs ^e) {}

・Paint イベント
public delegate void PaintEventHandler(Object ^sender, PaintEventArgs ^e);
public event PaintEventHandler ^Paint;

this->Paint = gcnew PaintEventHandler(this, &Form1::form1_Paint);

(再描画要求が発生した時、コントロールから呼ばれるメソッド)
void form1_Paint(Object ^sender, PaintEventArgs ^e) {}


*1:対象オブジェクトは、メソッドを宣言しているマネージ型である必要があります。

*2:デリゲートの宣言で指定した同型の「戻り値や引数」と異なるメソッドを指定できません。

*3:デリゲートには、同型の「戻り値や引数」を持つメソッドを += 演算子を使って幾つでも追加することが出来ます。

*4:外部からデリゲートを登録する仕組みを提供することに同じ。

お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- VC++ Form 作成 ----------------- コマンドライン --------------------*/

D:\vc2008\dllchk>cl /clr testpp.cpp /link /subsystem:windows /entry:mainCRTStartup

/*----------------------------------------------------------------------------*/


/*---- testpp.cpp -------------------- お試しソース ------------------------*/

#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>

using namespace System::Windows::Forms;

ref class suText : public System::Windows::Forms::RichTextBox {};
ref class suButton : public System::Windows::Forms::Button {};

ref class suForm : public System::Windows::Forms::Form {
public :
    suForm() {
        initCompo();
    }
    ~suForm() {}

private :
    suText ^text1;
    suButton ^button1;
    System::Windows::Forms::Label ^label1;

    System::Drawing::Point pt;
    System::String ^str;

    void initCompo()
    {
        text1 = gcnew suText;
        text1->Location = System::Drawing::Point(50, 50);
        text1->Size = System::Drawing::Size(100, 150);
        text1->BackColor = System::Drawing::Color::FromArgb(255, 255, 180);
        text1->Name = "text1";
        text1->Text = "求む入力!";
        text1->MouseDown += gcnew System::Windows::Forms::MouseEventHandler(this, &suForm::text1_MouseDown);

        button1 = gcnew suButton;
        button1->Location = System::Drawing::Point(75, 220);
        button1->Size = System::Drawing::Size(70, 20);
        button1->BackColor = System::Drawing::Color::FromArgb(180, 255, 255);
        button1->Name = "button1";
        button1->Text = "未確認";
        button1->Click += gcnew System::EventHandler(this, &suForm::button1_Click);

        label1 = gcnew Label;
        label1->Location = System::Drawing::Point(200, 120);
        label1->Size = System::Drawing::Size(150, 20);
        label1->Name = "label1";
        label1->Text = "コンパイルエラーが来たぁ";

        this->Location = System::Drawing::Point(50, 50);
        this->ClientSize = System::Drawing::Size(400, 300);
        this->StartPosition = FormStartPosition::Manual;
        this->Name = "form1";
        this->Text = "サンプルプログラム";
        this->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &suForm::form1_Paint);

        this->Controls->Add(text1);
        this->Controls->Add(button1);
        this->Controls->Add(label1);
    }

    System::Void text1_MouseDown(System::Object ^sender, System::Windows::Forms::MouseEventArgs ^event) {
        pt = System::Drawing::Point(event->X, event->Y);
        str = "X = " + event->X + ", Y = " + event->Y + ", Button = " + event->Button.ToString();
        Invalidate();
    }

    System::Void button1_Click(System::Object ^sender, System::EventArgs ^event) {
        label1->Text = "ボタンが押されました。";
        label1->ForeColor = System::Drawing::Color::Red;
        button1->Text = "押下";
        button1->Enabled = false;
    }

    System::Void form1_Paint(System::Object ^sender, System::Windows::Forms::PaintEventArgs ^e) {
        System::Drawing::Graphics ^g = e->Graphics;
        System::Drawing::Brush ^brush = gcnew System::Drawing::SolidBrush(System::Drawing::Color::Blue);
        g->DrawString(str, Font, brush, 200, 160);
        g->FillRectangle(brush, pt.X + 150, pt.Y, 10, 10);
        label1->Text = "コンパイルエラーが来たぁ";
        label1->ForeColor = System::Drawing::Color(ForeColor);
        button1->Text = "未確認";
        button1->Enabled = true;
    }
};

void main()
{
    Application::Run(gcnew suForm);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

DLL作成の序に、VC++フォームでも表示させてみようかと試してみましたが、改めてC++の複雑怪奇さを感じました。(VC++は約束事が多過ぎ)

柔軟性とか、汎用性とか、言葉巧みに誤魔化されちゃったっていう感じが・・・。ずぼらのめんどくさがり屋には、度を越した冗長性とか、オタッキーな適応性とか、それこそ意味不明瞭だし。

それはさて置き。

当然、IDEは難し過ぎるので、DOS窓(コマンドプロンプト)にcl入れてコンパイル!

試しに、VC++フォームを呼び出すだけのサンプルを作ってみました。

ただ、デザイナの利用は有りですね。

一寸凝りたければ、部品(コントロール)の配置も複雑になり、扱う部品数も多くなります。そんな時、グラフィカルに部品配置が出来るデザイナは優れものです。

手打ちは、画面サンプルや動作確認用(プレゼン?)などであればプログラムが小さくて済む分、読み易くなるには違いないけど、UIの作り込みは儘なりません!

<注意点>

・現状のclコマンドラインでは、エクスプローラから起動するとDOS窓が立ち上がった後に、VC++フォームが立ち上がります(つまり、コンソールアプリケーションであるらしい。Windows アプリケーションではない)。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- VC++ Form 作成 ----------------- コマンドライン --------------------*/

D:\vc2008\dllchk>cl /clr testpp.cpp

/*----------------------------------------------------------------------------*/


/*---- testpp.cpp -------------------- お試しソース ------------------------*/

#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>

using namespace System::Windows::Forms;

void main()
{
    Form ^form1 = gcnew Form;
    form1->Location = System::Drawing::Point(100, 100);
    form1->Size = System::Drawing::Size(400, 200);
    form1->StartPosition = FormStartPosition::Manual;
    form1->Name = "form1";
    form1->Text = "画面構成サンプル";

    Button ^button1 = gcnew Button;
    button1->Location = System::Drawing::Point(150, 100);
    button1->Size = System::Drawing::Size(100, 40);
    button1->Name = "button1";
    button1->Font = gcnew System::Drawing::Font("MS UI Gothic", 16);
    button1->Text = "ボタン";
    form1->Controls->Add(button1);

    Label ^label1 = gcnew Label;
    label1->Location = System::Drawing::Point(100, 30);
    label1->Size = System::Drawing::Size(240, 40);
    label1->Name = "label1";
    label1->Font = gcnew System::Drawing::Font("MS 明朝", 24);
    label1->Text = "エラーが来たぁ";
    form1->Controls->Add(label1);

    System::Windows::Forms::Application::Run(form1);
}

/*----------------------------------------------------------------------------*/