VC++ CLIでよく忘れていること【随時更新中】

たまにVC++ CLI (.NETのC++) を使うと忘れているこのメモです。

「ライブラリ」でなく「追加の依存ファイル」と記述されている。Windows標準ライブラリなら「追加のライブラリディレクトリ」は設定しなくてよい。

win32APIやMFC特有の型WORDDWORDは、CLIでは追加includeが必要。

オブジェクトの頭^

Java、C#の感覚で書いてしまうとBuildエラーになり悩む。new でなく gcnew もであることもお忘れなく。

以下を選択。Publicメソッドを普通に書けば、C#等からも利用が可能。MFC ActiveX Controlのように特別な設定はいらない。

.NETクラスライブラリを使うとき

C#等から、CLIで生成したの.dllを使用時に「間違ったプログラムのフォーマットを読み込もうとしました。」とエラーが発生する場合、x86、x64を指定する。C#単体だと「Any CPU」でWindows上で動作するが、、、

オブジェクトの排他制御

MFCではなんとなくでよかったスレッド間のオブジェクト排他制御は、必須になります。デリゲートを使います。デリゲートは .NET 独自概念です。Swfitのとは違います。別スレッドで動作されるイベントの例です。排他ありと無で2分岐作ります。

	// 受信が別Threadのため、threadまたぎコントロールアクセスのため、
	// デリケード(オブジェクトとメソッドをカプセル化した型)を使う。

	// 「受信データ表示」デリゲート型
	// メソッドへの引数がデリゲート型の引数になります
		delegate void displayReceiveData_Delegate(void);	
		displayReceiveData_Delegate^ displayReceiveData_Obj;		// 「受信データ表示」デリゲート実体
		delegate void displayReceiveError_Delegate(System::IO::Ports::SerialError);
		displayReceiveError_Delegate^ displayReceiveError_Obj;		// 「受信エラー表示」デリゲート実体
		delegate void sendData_Delegate(void);	
		sendData_Delegate^ sendData_Obj;						// 「送信データ」デリゲート実体
		
/* ****************************************************************************
 * 機能名 : シリアルポート受信イベント
 * ***************************************************************************/
private: System::Void serialPort1_DataReceived(
   System::Object^ sender, 
   System::IO::Ports::SerialDataReceivedEventArgs^ e
) {

	if ( dgrdReceive->InvokeRequired ) {		// thread排他が必要か判別
		// 「受信データ表示」デリゲート実体を確保。
		// デリゲートのメソッドは、Formのメソッドとして宣言し、
		// form実体と、メソッド型を、デリゲートに割り当てる。
		// &はC++時のおまじない的記述。C#では不要らしい。
		displayReceiveData_Obj = gcnew displayReceiveData_Delegate(this, &Form1::displayReceiveData);

		// デリゲートを排他ありで呼び出す
		this->Invoke(this->displayReceiveData_Obj);
	}
	else {
		displayReceiveData();			// 受信値表示
	}

			if ( serialPort1->IsOpen ) {
				if ( dgrdReceive->InvokeRequired ) {		// thread排他が必要か判別
					sendData_Obj = gcnew sendData_Delegate(this, &Form1::SendData);
					// デリゲートを排他ありで呼び出す
					this->Invoke(this->sendData_Obj);
				}
				else {
					SendData();			// 次の送信
				}
			}
	}

シリアルポートの受信などトラフィックの高いデリケートの場合、デッドロック的な事象が発生するようです。その場合は、

		formatReceiveData_Obj = gcnew formatReceiveData_Delegate(this, &Form1::formatReceiveData);

		// デリゲートを排他無しで呼び出す
		// Invokeでは大量受信後、Close でフリーズしてしまう。
		this->BeginInvoke( this->formatReceiveData_Obj );

宣言時は、

	array <String^,2/*次元数*/>^	recvFormatedData;	
	recvFormatedData = gcnew array<String^,2/*次元数*/>( 1/*行*/, 3/*列*/ );

参照時

recvFormatedData[ num, 0 ] =
		System::String::Format("{0:00}:{1:00}:{2:00}.{3:000} ",
			DateTime::Now.Hour, DateTime::Now.Minute,  DateTime::Now.Second, 
			DateTime::Now.Millisecond );
recvFormatedData[ num, 1 ] = data1;
recvFormatedData[ num, 2 ] = data2;

拡大はできないので、BACKコピーして、新しくnewして中身を転記する。

	// CLIでは多次元arrayの拡大はできないため、新しく作ってコピーする。
	array <String^,2/*次元数*/>^	backData;
	backData = (array <String^,2/*次元数*/>^)recvFormatedData->Clone();

	recvFormatedData = gcnew array<String^,2/*次元数*/>( num+1/*行*/, 3/*列*/ );

	for( int i = 0; i < num; i++ ) {
		recvFormatedData[ num, 0 ] = backData[ num, 0 ];
		recvFormatedData[ num, 1 ] = backData[ num, 1 ];
		recvFormatedData[ num, 2 ] = backData[ num, 2 ];
	}

配列数取得時は、

		// 未表示データ行数 ( [0]は配列保持用の空Record )
		int datanum = recvFormatedData->GetLength( 0 /*次元数*/ );

  • 意図せずUIダブルクリックしないように注意する。ダブルクリックでイベント自動追加され、要らないのでUndoするとFormが壊れる。イベント自動追加は無効に設定できるようにしてほしいものです。
  • UIを別frameに移動するとき、切取り -> 貼付け すると壊れる。ドラック移動だけで別frameの子供に移動できる。

以降、随時追加中...

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です