C#実装事例?

delegateについて

ググレカス→delegate C#

注意:Delegateクラスもある.delegate型とは別ぽい.

しかし,"デリゲートの使用 (C# プログラミング ガイド)"では,以下の記述がある.

デリゲート型は System.Delegate から派生するため、
このクラスで定義されているメソッドとプロパティをデリゲートで呼び出すことができます。

見た目・UI操作

強制クローズを禁止する.(システムメニューの削除,ALT+F4の終了排除.)
→参照 http://www.atmarkit.co.jp/fdotnet/dotnettips/142closebtn/closebtn.html



非同期プログラミング

宇宙仮面のC#プログラミング-非同期プログラミングと, 例のサンプルから推測交じりで消化.delegate,Thread class等の比較による紹介.

リード処理をdlegate宣言したクラス(変数?)にセットする.読み出し要求が発生したときに,BeginInvokeする.このとき,非同期callbackを行うため,AsyncCallbackオブジェクトも渡す.処理終了時に,AsyncCallbackオブジェクトを作るときのmethodが呼ばれる.引数として,(IAsyncResult)arを受け取る.キャストして,自分の定義したdeletgate型へ修正する.

ReadInputReportDelegate deleg = ( ( ReadInputReportDelegate )( ar.AsyncState ) ); 

ar.EndInvoke()でスレッド終了.この引数には,delegate宣言したmethodのうち,参照型'ref'をつけたものが順に渡せる.+ar自身.

deleg.EndInvoke( ref myDeviceDetected, ref inputReportBuffer, ref success, ar ); 

System.Object System.Delegate System.MulticastDelegate

この型のすべてのパブリック staticメンバは、スレッド セーフです。



Threadの寿命について

class methodをThreadStartクラスでインスタンス化したとき,そのクラスから半ば独立したthreadで起動する模様. 親windowが死んでも走り続けていたので,なぜか不思議であった.上記終了処理で子スレッド終了を待たせてみたが,スマートではないと思えたので検索↓.
k.kinukawa(momo_dev)の日記より,デフォルトはフォアグランドスレッド?として起動していた模様.
ハンドル類はcloseされることなく,そのスレッドが従属するクラスのデストラクタも呼び出されなかったため,裏でHID Read fileを無限に繰り返す状態になっていたようだ.


(Rxthread).IsBackground = true;

とセットしてから,

(RxThread).Start();

とすることで,バックグランドで走るようになった.親スレッド(Form)をcloseした際に,デストラクタが呼び出された.Threadは放置すると良くないと思ったので,thread.abort()で殺した.

この処理を実装しなくても,Thread objectのデストラクタかどこかで閉じられた模様.ファイルハンドルがどうなったのか….safefilehandle objectで触っていたから,これもデストラクタが綺麗にしてくれたのだろうか.不安が募るが,何という適当な処理言語だろう….runtimeでデバッグコードが走るようなものか?安全といえば安全だが.とりあえず明示的に殺すことにする.

デストラクタの宣言

C++と同じ.クラス名の先頭にチルダ'~'をつける.引数はナシ(空白).objectが破棄されるときに呼ばれるハズ.Formのクラスですが,ALT+F4では呼ばれなかった@デバッガ.breakがうまく効かなかっただけなのかなぁ...

class Foo { ...
  ~Foo()
  {
  }


process間データ通信について(C#→Win32native)

C# .Net SendMessageで他のアプリに文言を送信したいを参照.

キーワード:C# Alloc message lparam wparamで,探索.

C#/Win32API覚書

2008/09/08WinPGMimport

Windows Programming

主にC#ベースで作成します.速度を求めるならnativeコードで実装すべきでしょう.とりあえずC#で作って,速度を稼ぎたいところでunsafe codeを書いていきます.
その時点でWindows APIの使用となり,Import宣言等を自前で書く必要が出てきます.

それで我慢できなくなったら,VC++等への移行も検討しましょう.

HID Interrupt transferの動作確認

2008/09/08工作::USBimport

HID Report DescriptorとHostとの通信について(Interrupt Transfer)

デバイス設定:断りの無い限り,下記のとおりとする.

itemvalueremark
Poling cycle10 mSecInterface Descriptorで指定
EndPointBuffer size64 bytesInterrupt transfer

Report Descriptorで定義したサイズは固定長である.


転送サイズについて

Report Descriptorでは,Variable....という記載があったので,デバイス→ホスト間の転送サイズは任意であると考えていた.実際に動かして見ると調子が悪かったので,HIDは 原則固定長であると認識した.

実際にDevice/Host間で送受信データサイズを変えて確認した.ただし,いずれもデバイスのReport Descriptorでは 8bit 64count(64octet)とし,Interface Descriptorでは,EP sizeを64octetとしている.

Transfer directionfromtoRemark
IN transactionDEV: 64byte送信HOST: 64Bytes受信OK
OUT transactionHOST: 64byte送信DEV:64byte受信OK
IN transactionDEV: 1byte送信HOST: 64Bytes受信NG
IN transactionDEV: 1byte送信HOST: 1Bytes受信NG
IN transactionDEV: 64byte送信HOST: 1Bytes受信NG

Full Speed時は64byte(max)のユーザデータを送受信できるが,固定長となる.UARTの代替として使用する場合,もしくは64octetを超えるパケット通信の土管として使用する場合には,さらに一段ラッパーをかます必要がある.Low Speedもサポートするのであれば,パケットサイズが8octetになる.

 struct {
	Byte SizeOfData ;
	BYte Data[63] ;		// Low=63, High/Full=63
	}

PC側は,Report Descriptorを受け取って,capabilityとしてサイズを認識するので,データサイズは固定としない.デバイス側で,最大長となるように設定する.デバイスのメモリの都合・データ流量の都合により,削減する場合は,descriptor・interrupt transferの転送サイズをReport通りに修正するだけでよい.


取りこぼしについて(後方参照のこと!)

Interrupt転送は,デバイス-ホスト間でのデータ取りこぼしが起こりえないという話をどこかで見かけた.本当にそうなのかを検証してみた.*1

  • デバイスは複合デバイスとして実装した.同一USB portに複数のデバイスがぶら下がっているため,Interrupt転送を占有していない.← このため,最大負荷テストではない
  • PSoC側で1mSecの分周クロックを生成して*2,INパケットに積む
  • 毎パケット,+1したシーケンス番号を積んで,取りこぼしを検出する.
  • Host側は受信スレッドを作成し,受信開始からスレッドをまわす.取得したデータはQueueに積んでいく. 表示するときにQueueから吸い上げてListに突っ込む.
Poling Timing(Report)PO cycle実測結果Remark
10mSec8mSec取りこぼしなし周期が早いのは..?
1mSec2mSecかなりこぼす複合デバイスにしたので,最速では回らないか? スレッドで0秒寝かせているのが悪いのかもしれない.回りっぱなしでもトライする?.
2mSec2mSecかなりこぼします10/Sep.の追試実験用.条件よければOK.

※試験結果を随時追加していく.★疑問:GetInputReportBufferSize は なに?EPサイズでもreportサイズでもない.. reportの記憶回数でもない..



*1 : ホスト→デバイス方向は未確認

*2 : Sysclk=24MHz, VC1-VC2-VC3接続し,Timer8bitでカウント.VC1=15, VC2=16, VC3=100 → 1mSec

追記(10/Sep./2008)

同時期に,C++ Builderでホストプログラムを作成されている方より,取りこぼしはおきない旨,伺いました.Visual C#が癌なのかという疑いが出てきます.

ホスト側実装

前回の測定時のフローを示す.
1. 受信を Threadクラスを使ってぶん回す.ひたすらRead()を呼び出し続ける.
2. 受信したデータは,下記のクラスを用いてデータを積み上げる.

System.Collection.Generic.Queue<Byte>

このとき,スレッド内では,追加時にlock(Que)する.
3. Formスレッドで,上記クラスからenqueしてデータを取り出し,表示用のListControlに追加する.
このとき,lockなし,連続して吸い出している.

チェキ1

Formスレッドで,Queから読み出すたびに,Thread.sleep(0)を入れた.体感もっさりした気がする*3.取りこぼしは消えない.

追加情報

MSVSのmanualを見ると,動的に記憶領域を延ばすと書かれていた.追加時のオーバヘッドでもこぼしている可能性があるのだろう.
また,ロックの仕方に問題がありそう.本質的にスレッドセーフではないとあるので,記載どおりのlockを行う.
・・・ために,generic Queueではなく,Collection.Queueクラスを用いる.

チェキ2

ヘルプに記載のとおり,下記のlock()を適用する.

lock(myCollection.SyncRoot)

System.Collection.Generic.Queue<>ではなく,System.Collection.Queueを使ってみた.読み書き両方でlock()を使ったが,やはり駄目だ.
Byte[]の動的確保にも負荷がかかっているのだろう.開放処理もGCが走るだろうし.

チェキ3

表示させずにデータ取得させてから,表示してみる.
取りこぼしナシ.CPU負荷がそれなりにかかる模様*4

アプリケーション走行速度・データの扱い方による問題と思われる.

また,受信バッファは512byteまで指定可能のようだ.でかい値を放り込んでも拒絶される(32byte:report値に戻る?).
ただし,これによる救済措置が図れるのかは疑問である・・・.


結論

実は,チェキ1が終わった段階で,USB Snoopy Proを使用してPC側にデータが来ているかどうかを確認していた.
案の定,データが来ていることは見て取れたので,取りこぼしているのはドライバ~アプリのアタリであるとアタリがついていたのです.

そもそもHIDと言っている以上,ヒトの応答速度程度の情報を送受信するのが目的であるし,仕様なのかもしれない.
また,Report Descriptorを見ても判るように,固定長・ビット単位で意味を持たせたデータを送るのが目的であり,今回のようにストリームデータを送るのは想定外使用なのだろう.もし,確実に双方向のデータ通信を行いたいのであれば,ホスト~デバイス間でハンドシェイクを行う必要があるでしょう.

そこまでするならBulk転送で~とも思うのだけれど,それはそれでホスト側のドライバが面倒.汎用USBドライバやWinUSBといったものがあるようなので,それらの使用も視野に入れたい.
ひとまずHIDポート制御部分を切り出してコンポーネント化しておきたいところですね.

*3 : ListControlへの追加が遅くなっただけだろう

*4 : Core2Duo 2.4GHz(E6600)で30%超. VisualC# デバッグモードにて.Debug Writeも使用.

[C#][USB] Host apllication on WindowsXP with C#

2008/08/30工作::USBimport

★本気時は書きかけのものです.頓挫する前に公開★

PC側のソフトを準備する

氏のsample"generichid_cs"から,必要なコードを流用する.C#では,Win32APIの呼び出しを直接行うことはできない.unsafe codeとして,DLLからAPIをimport宣言してやる必要がある.VBでWin32APIを用いるために宣言していたのと同様である.*1

*1 : .NET環境ではC#もVBも同じILに落ちると思いますけど

HostTest_01

  • device enumeration
  • detection of device attached/detattached
  • IN/OUT transaction check by using ReadFile/WriteFile

console applicationでもよさそうだが,今後のことを考えてGUIで作っていく.Formに配置するリソースを列挙する.

  • TextWindow or List Window
    • Status Window
    • Rx Data window(StatusWindowに書いておけばいいか?)
  • EditBox
    • Device ID
      • VID
      • PID
      • IF-ID
    • TxData
  • コマンドボタン
    • device detect
    • INPUT
    • OUTPUT

パクリ

下記のファイルをコピーする.ただし,VBのnamespaceも使ってるようなので,少し綺麗にしたいと思た.ちうことで,VBのlength/CRLFだけを使っていたので,これを排除しただけなんですがね.

  • Debugging.cs
  • DebuggingDeclarations.cs
  • DeviceManagement.cs
  • DeviceManagementDeclarations.cs
  • FileIODeclarations.cs
  • Hid.cs
  • HidDeclarations.cs


device enumeration

detection of device attached/detattached

IN/OUT transaction check by using ReadFile/WriteFile



HostTest_02

Include all functions of HostTest_01.


メモ

device change

WM_DEVICECHANGEメッセージが通知される模様.標準でフックできないようなので,WindowProcedureをoverrideしてsniffする.

class DeviceManagement(In file DeviceManagement.cs)

attached/removed検出
登録: RegisterForDeviceNotifications()
停止:StopReceivingDeviceNotifications()
DeviceManagement::Boolean DeviceNameMatch( Message m, String mydevicePathName )

WM_DEVICECHANGEメッセージを受けて,pathnameとマッチするかチェックする.マッチする場合はtrueを返す.

DeviceManagement::Boolean FindDeviceFromGuid( System.Guid myGuid, ref String[] devicePathName )

Uses SetupDi API functions to retrieve the device path name of an attached device that belongs to an interface class.


SetupDi APIを使って,interface classに属する接続されたdevice path nameを検索する.


参照資料

★MSDNより,C#で Num Text boxの作成 - 派生させてHex Edit boxでも作れ..~ 入力: hexのみ, 出力:':'区切りで表示, nibbleでfocus失うなら捨てる. 入力中は文字色変えるとか.

http://msdn.microsoft.com/ja-jp/library/ms229644(VS.80).aspx http://msdn.microsoft.com/en-us/library/ms790920.aspx http://msdn.microsoft.com/ja-jp/library/cc429201.aspx