GUI追加

C#WindowsFormを使います

シンプルに説明するために,若干汚いコードを書いています.

Formを追加します

VisualStudioとC#の連携です.自分自身が不慣れなので,図を交えてすこし丁寧に説明します.

まずはメニューからフォームを追加します.

フォームに名前をつけてください.これがFormクラスの名前になります.

共通言語ランタイムサポートをONにします.「はい(Y)」をクリックしてください.

Formのデザインを変更します

この変はVBを使ったことがある人なら問題ないでしょう.

*2008年6月27日追記: VC2008を使っていると,うまくツールボックスを使えない障害があります.
これは.Net Frameworkの更新を怠っているからですが,「ソリューションのプロパティ」→「共通プロパティ」→「Frameworkと参照」で,「対象とするFramework(G)」を「.Net Framework 2.0」に選択すれば直ります.

プロパティビューからボタンのキャプションやオブジェクト名を変更できます.

ボタンとエディットボックスを追加しました.

最終的にそれぞれのプロパティの変更点は以下のようになっています.

オブジェクト button1 button2 TextBox
プロパティ デザイン−Name countUpButton sendButton textBox1
表示−Text CountUp(&C) Send(&S) Please Input Message

マネージドC++コードから,C#のFormを呼び出します

コードを編集していきます.とりあえず表示部分だけ説明しますね.Javaで言うJNIに良く似たインタフェースです.

ここまでの編集で作成したソースファイルは3つ.

SettingPanel.cppはC++コード.SettingPanel.hはC#コードになっています.不思議ですがSettingPanel.cppからSettingPanel.hをインクルードしてもコンパイルが通ります.これは共通言語ランタイムサポートをONにしている恩恵です.

つまりSettingPanel.cppを仲介役として,C#コードであるFormにアクセスができ,またその逆も可能ということです.

変更前(SettingPanel.cpp)

#include "SettingPanel.h"

変更後(SettingPanel.cpp)

#include "SettingPanel.h"

using namespace PeriodicConsoleOut;

void SettingPanel_createForm(void) {
  Application::Run(gcnew SettingPanel());
}

これで表示するための関数が実装されました.ただもともとのコンポーネントのコードから,このcreateFormメソッドを呼び出さなくてはなりません.

変更前(PeriodicConsoleOut.ccp) main関数内

int main (int argc, char** argv)
{
  RTC::Manager* manager;
  manager = RTC::Manager::init(argc, argv);

  〜中略〜

  // run the manager in blocking mode
  // runManager(false) is the default.

  manager->runManager();

  // If you want to run the manager in non-blocking mode, do like this
  // manager->runManager(true);


  return 0;
}

変更後(PeriodicConsoleOut.cpp) main関数内

void SettingPanel_createForm(void); //SettingPanel.cpp内のグローバル関数を使用するために,関数を宣言.

int main (int argc, char** argv)
{
  RTC::Manager* manager;
  manager = RTC::Manager::init(argc, argv);

  〜中略〜

  // run the manager in blocking mode
  // runManager(false) is the default.
  //manager->runManager();  //デフォルトのrunManagerメソッドをコメントアウト.

  // If you want to run the manager in non-blocking mode, do like this
  manager->runManager(true);  //runManagerメソッドをノンブロッキングモードで呼び出すと,コントロールがすぐに返ってきます.

  SettingPanel_createForm();  //そして直ちにFormを作成します.createFormメソッド内のApplication::Run()メソッドはモーダルなFormを作成するので,Formを閉じない限りmain関数は終了しません.

  return 0;
}

Formと変数のやり取りをします.

Formにイベントハンドラを追加します

それぞれのボタンをダブルクリックすると,自動的にイベントハンドラコードが生成されます.
今回追加した二つのボタンを両方ともダブルクリックすると,下記のようなコードが生成されます.

#pragma endregion
  private: System::Void countUpButton_Click(System::Object^ sender, System::EventArgs^ e) {
  }
  private: System::Void sendButton_Click(System::Object^ sender, System::EventArgs^ e) {
  }
};

ちなみに,ソリューションエクスプローラからSettingPanel.hを開いても,デザインビューしか開きません.コードを変更するときは右クリックをして,メニューからコードの表示を選びます.

変数のやりとりについては2種類あります.

まずはSettingPanel.cppにあるグローバル変数を介して行います.

変更後(SettingPanel.cpp)

#include <string>

std::string stringBuffer= 0; //このようにヘッダーファイル(C#コード)の前で変数を宣言することで,C#コード内で変数を操作することができるようになる.

#include "SettingPanel.h"

using namespace PeriodicConsoleOut;


void SettingPanel_createForm(void) {
  Application::Run(gcnew SettingPanel());
}

ここでコードの変更

変更後(SettingPanel.h)

文字列はC#のマネージドコード上の形式と,アンマネージメモリ上の形式が異なりますので若干面倒です.

#pragma endregion
  private: System::Void countUpButton_Click(System::Object^ sender, System::EventArgs^ e) {
  }

  private: System::Void sendButton_Click(System::Object^ sender, System::EventArgs^ e) {
    IntPtr ptr = Marshal::StringToHGlobalAnsi(this->textBox1->Text); //テキストボックスの文字をアンマネージメモリに移動し,さらにANSI形式に変換します.
    stringBuffer = static_cast<const char*>(ptr.ToPointer());
  //んで代入.
  }
};

RTコンポーネント本体からもstringBufferを参照できます.

変更後(PeriodicConsoleOut.cpp内,onExecuteメソッド)

#include <string>
extern
std::string stringBuffer;
//外部変数にアクセスするために,変数名を宣言します.

RTC::ReturnCode_t PeriodicConsoleOut::onExecute(RTC::UniqueId ec_id)
{
  std::cout << "String = " << stringBuffer << std::endl; //このように直接アクセスできます.
  return RTC::RTC_OK;
}

逆にマネージドC++コードから,C#のオブジェクトにアクセスできます.

変更後(SettingPanel.h)

public: int count; //メンバ変数を追加
private: System::Void countUpButton_Click(System::Object^ sender, System::EventArgs^ e) {
  count++; //ボタンが押されたらインクリメント
}

private: System::Void sendButton_Click(System::Object^ sender, System::EventArgs^ e) {
  IntPtr ptr = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(this->textBox1->Text);
  stringBuffer = static_cast<const char*>(ptr.ToPointer());
}

C++コード側は面倒です.今回はSettingPanelクラスのオブジェクトがグローバル変数などになっていませんから.
そこで,Formを作成する際にApplicationクラスメソッドのRunをつかいましたよね.Form本体はApplicationクラスが管理しているはずなので,そちらを参照するコードを書いてみました.

変更後(SettingPanel.cpp)

int SettingPanel_getCount() {
  return ((PeriodicConsoleOut::SettingPanel^)Application::OpenForms[0])->count;
}

ApplicationクラスからOpenFormsメンバを介してFormの参照を受けとり,SettingPanelクラスへの参照にキャストしてからcountにアクセスしています.「^」はマネージドメモリに対するポインタを意味していますが,「*」の代わりだと思う程度でいいです.

んでRTコンポーネント側からはSettingPanel_getCountメソッドを介してcountパラメータを受け取ります.

変更後(PeriodicConsoleOut.cpp)内onExecute関数


#include <string>
extern std::string stringBuffer;

int SettingPanel_getCount(void); //SettingPanel.cpp内のSettingPanel_getCount関数にアクセスするために関数名を宣言します.

RTC::ReturnCode_t PeriodicConsoleOut::onExecute(RTC::UniqueId ec_id)
{
  std::cout << "Text = " << stringBuffer << std::endl;
  std::cout << "Count = " << SettingPanel_getCount() << std::endl; //関数を呼び出せばcountの値が返ってきます.
  return RTC::RTC_OK;
}

rrtc.conf

これまでどおりでよろしくです.

実行

コンポーネントをアクティブ化すれば,ボタンを押すと変更がコンソール出力に反映されているのがわかると思います.

PeriodicConsoleOutGUI.zip (ver. 0.4.1)

PeriodicConsoleOutGUI042.zip (ver. 0.4.2)


RTミドルウェア入門