2011/12/27(火).NETのSerialPortでケーブルを抜くと例外

.NET(WinXP, .NET 3.5で発生)のSerialPortクラスで、USB-Serial変換ケーブルを使用している際に起こる問題。このケーブルに紐づくCOMポートをOpen()後、Close()前にケーブルを抜くと例外が発生する。

原因は.NET側のバグ。ケーブルの挿抜というかCOMポートが消えることを考慮していない。

発生する例外は把握している限りで2種(UnauthorizedAccessException, ObjectDisposedException)。前者は、Dispose()中に発生する。後者は、SerialPort内部で動いてるThreadが出してくる。

UnauthorizedAccessExceptionはラッパークラスを作って、Dispose()をオーバーライドしtry~catchで囲ってやることで対処可能。以下を参照。簡単なのでコードは書かない。

街角のリブロガー: C# SerialPortのエラー「UnauthorizedAccessException」対策

ObjectDisposedExceptionの方はちょっと難しい。発生箇所は.NET内部の別スレッドのようで、捕まえられる場所を思いつかない(存在しない?)しタイミングも不定。Thread.GetDomain().UnhandledExceptionで発生タイミングはとらえられるが、ここではキャンセルできない。古い.NET、おそらく1.1以下だとこの手の例外は処理継続となるのだが、.NET 2.0以降はアプリが落ちてしまう(WinXP/Win7, .NET 3.5で確認)。

ということで、どうもコードでの容易な対処方法がないようだ。アプリケーション構成ファイルでの回避策があるらしいので以下を参照。

SerialPort Crashes after disconnect of USB COM port | Microsoft Connect

こんな感じでよい。

program.cs
[STAThread]
static void Main()
{
	...

	//メインスレッド以外の非ハンドル例外処理
	Thread.GetDomain().UnhandledException +=
		new UnhandledExceptionEventHandler(Application_UnhandledException);

	...
}

public static void Application_UnhandledException(
	object sender,
	UnhandledExceptionEventArgs e
){
	if( e.ExceptionObject != null &&
		e.ExceptionObject.GetType() == typeof(ObjectDisposedException)) {
		
		//.NET3.5のSerialPortのバグで、ケーブル挿抜後にハンドル不能の
		//ObjectDisposedExceptionが返ることがある。このため、未ハンドル例外処理にて
		//ObjectDisposedExceptionについてのみ無視する。

		//何もしない
	} else {
		//何かメッセージを出力
		
		Application.Exit();
	}
}
以下のアプリケーション構成ファイルをアプリケーションと同じディレクトリに置く(hoge.exeが実行ファイルなら、hoge.exe.configとなる)。

hoge.exe.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<runtime>
		<legacyUnhandledExceptionPolicy enabled="1"/>
	</runtime>
</configuration>
構成ファイルを上記のように書くと、非ハンドル例外に関しては処理継続となる。釈然としないが、どうもこれが楽な方法らしい。

その他の非ハンドル例外を無視して良ければ、program.cs側の記述は不要。

.NETのバージョン変更、OSの変更で直る可能性有り。

2011/12/16(金)F904iの電話帳移行メモ

2011/12/16 15:00 PC(全般)
長らく使っていた、docomo F904iが故障してしまった。

故障状況はこの記事と直接関係ないのだが一応書いておこう。どうもアンテナ周りに問題が発生したらしく、屋外なら良いのだが屋内に入るとまず圏外になる。softbank,WiMAX,e-mobileが問題なく通信できる場所でもあっさり圏外になるのでdocomoと契約している意味がない。電池の消耗も異常に早い(電波を必死につかもうとするから?)。

同様の不具合は「F904i 圏外」で検索すればいくらでも出てくる。要は設計として部品耐久性に問題があったということのようだが、docomo側では使用3年超は無償での交換・修理は不可ということだった。

閑話休題。

今はdocomoガラケーとsoftbank iPhoneの2台持ち。状況を見極めつつ契約をスマートフォン1つに一本化したいのだが今は高速回線黎明期だったりしてタイミングが悪い。しゃーないので、確保してあった0円ガラケーことL-03Bに移行することにする。

前置きが長かったが、このとき電話帳を移行するのにちょっと手間だったのでメモ。

何が手間だったかというと、docomoの汎用Datalinkソフト(ドコモケータイdatalink(データリンク) | NTTドコモ)がF904iを認識してくれない。対応機種表にはあるのにだ。メーカのdatalinkソフト(携帯電話(データリンクソフト) ダウンロード - FMWORLD.NET(個人) : 富士通)からは認識できるし、こっちのページにはF905i以降は~docomoのでと書かれているから汎用datalink側の対応機種表が間違っているのだろう。L-03Bは汎用datalinkソフトで認識した。

ということで、メーカのdatalinkソフトで電話帳を抽出することにした。この時フォーマットの選択肢がcsvとvcf(vCard)とあるのだが、csvで吸うとグループが取得できない。グループが維持されないと大変困るのでvcfで吸ったのだが、今度は汎用datalinkでL-03Bに転送する際にこのvcfを取り込む方法がない。

ということで、自前スクリプトでvcf→csvに変換し、これを汎用datalinkソフトに取り込んでL-03Bに転送した。

iPhoneだとvcfをメールに添付→端末側で開く、でも移送可能(グループは消えるし、変なメモが作られるけど)なので、もっと簡単な方法もあるに違いない。まあ、とりあえずこれでできたということだけメモ。

素直にdocomoショップ行くのとどっちが早かったか…

2011/12/16(金)ICQのログ発掘

2011/12/16 14:24 PC(全般)
かなり今更だが、ICQのログを参照する機会があったのでちょっとメモ。

ICQは各バージョンデータの保持形式が違う上、少なくとも2003bくらいまではログの書き出し機能もなければプラグイン等での対応もできなかった(はず)。ログを参照するだけならICQをインストールし直せば見られるのだが、日付からの検索ができるだけで一括エクスポートなんかができない。

ということで何らかの方法で抽出しないといけない。サーベイするとだいたい候補が2つ。

ICQ バックアップ

Belkasoft ICQ Analyzer

上は無料、対応バージョンが限定(完全に確認してないが99~2001(2002も?)と思われる)。ICQがインストールされている必要がある。使い方が難しい。

下は有料、簡単、対応バージョンはたぶんすべて。チャットログ以外に、承認要求やファイル転送ログもとれそうだ(ただし一部文字化けするっぽい)。ICQのインストール不要。

他に、ABC Amber ICQ Converterというのもあったのだが、Belkasoftに機能面で負けてそうなので割愛。

今回取り出したいのは、99b~2001bとちょうどICQバックアップの対象範囲に合致したので、とりあえず上で試すことにした。

ICQバックアップはICQがインストールされている必要があるので、まずICQをインストールする。ただしバージョンに制約があるので注意。2003とかだとうまく抽出ができない。なんとかICQ2001b辺りを探して来てインストール。

次に、抽出したいログを含む
  • アカウント番号.dat (cf. 11111111.dat)
  • アカウント番号.idx (cf. 11111111.idx)
を含む2001aだとかDb99bとかのフォルダをインストールした場所に全部突っ込む。

これでICQバックアップを立ち上げると、自動認識して対応範囲のバージョンのログは抽出できる…はず。

やってみたところ、だいたい問題なくとれたようなのでこれで良しとする。

なんかめんどくさかったのでおとなしくあきらめるとか、Belkasoftに金払った方がよさそう(ICQ History Extractorという旧製品もあり、こちらはAnalyzerよりだいぶ安い。まだPurchaseできるならこちらか)。

2011/12/09(金)TVTestが終了時に落ちてその後の録画に失敗する

2011/12/09 16:54 PC(全般)
TVTestが原因と思われる録画失敗が多発しているため、前回の残件も含めてちょっと調査してみた。
[状況]
  • TvRock0.9u2 + TVTest0.7.23(Win7 x64) + PT2
  • 特定の入力デバイス(地デジ1なら地デジ1)に割り付けられた予約がある時刻から失敗している。
  • 確認してみると、TVTestが不応答状態で残っている(そのデバイスに割り付けられたもの)。
  • 録画ファイルが生成されていない(0bytesですら残っていない)
調査対象はTVTest0.7.15, 0.7.17, 0.7.18r3, 0.7.20, 0.7.21r2, 0.7.22r2, 0.7.23

まず、先日と同じく、コマンドラインは
>tvtest.exe /rec /recfile "test%test.ts"
で調査する。

[1]録画ファイル化けの問題。
  • 0.7.15-0.7.20は"test%test.ts%"になり化ける。
  • 0.7.21r2-0.7.23は"test%test.ts"になり正常。
[2]TVTest落ちの問題。
ドライバも何もない状態で起動し、数秒後に録画停止ボタンを押して録画停止する。
  • 0.7.15-0.7.17は正常に停止する。
  • 0.7.18r3-0.7.23は異常終了(ハンドルされていないWin32例外)する。
ここで、異常終了するケースで色々試してみると、
  • "/recexit"オプションを付けると異常終了しない。
  • ファイル名に"%"を含まない場合は異常終了しない。
"%"については残件調査のつもりで始めたのだが、ここで原因が確定してしまった。"%"を含むファイル名で録画すると、TvRockからTVTestの終了に失敗する。そうすると、手動でこのプロセスを落とすまで以降このTVTest側に割り付けられた予約がすべて失敗するということのようだ。具体的には前回と同じく、"うたの☆プリンスさまっ♪マジLOVE1000%"が原因で、その後に来た"UN-GO"と"ギルティ クラウン"が失敗したということになる。この話どうでもいいな。

"/recexit"を付けるとなんか改善するのは現行TVTest/RecTestの常っぽい(RecTestでも類似バグがあった)。

運用に関してはとりあえず/recexit付けて様子を見ることにする。改善要望出して、ソース読める時間とれたらもうちょっと解析してみよう。DirectX SDKとDirectShowのBaseClasses入れないといけない……

2011/12/06(火)HSV解析メモ

2011/12/06 9:32 PC(全般)
画像をHSV値で眺めたり分析したりしたいときがある。
その際使用するツールとかのメモ。
  • 各画素値を取るなら
    • 自前で書く。
  • ヒストグラムを見たいなら
    • PaintStar(DPExの方が見やすいけどシェアウェア)
  • チャンネル毎に分解したいなら
    • GIMPで色→色要素→チャンネル分解
  • 色のピックアップ
    • Photoshopでも何でも。
なお、自前で書く際に.NETを使用する場合、GetPixel()が天地を貫くほど遅いので、
Mr.Exception 画像処理にGetPixel/SetPixelを使っていませんか?
などを参考とすること。

OpenCV周りもそろそろ触らないとなあ。

チャンネル分解でHレイヤーに色つけてくれるツール募集中(ImageNosでできそう?)。

2011/12/01(木)awstatsの更新がおかしい

2011/12/01 22:13 PC(Linux)
メールをよみがえらせてみたら、こんなメッセージが山ほど来ていた。
Create/Update database for config "/etc/awstats/awstats.conf" by AWStats version 6.95 (build 1.943)
From data in log file "/var/log/apache/access.log"...
Error: Couldn't open server log file "/var/log/apache/access.log" : Permission denied
Setup ('/etc/awstats/awstats.conf' file, web server or permissions) may be wrong.
Check config file, permissions and AWStats documentation (in 'docs' directory).
うへえ、awstatsの更新が死んでる。……あれでもおかしい、awstats自体はちゃんと最新のデータを取ってきてる。しかしメールは続々来る。なんだこれはと調べてみたら、/etc/cron.dと/etc/cron.hourlyにそれぞれawstatsのスクリプトがあった…… orz。

片方が成功、片方が失敗して正常な更新とエラーメール(受け取れてなかった)を延々続けていたのか。なんだそれ!

awstatsは確か当初ソースから入れたので残っていたのかもしれない。パッケージに切り替えたときに旧cron設定を消し忘れたのだろう。

ということで、
$ dpkg -L awstats
で調べつつ、
cron.hourlyの古いスクリプトを消し、パッケージから入っていると思われるcron.d側を残した。

この状態でまだエラーメールが来るので、/etc/cron.d/awstats中のユーザ指定をaccess.logのユーザに書き換え。

更新成功し、エラーメールもなくなった。

2011/12/01(木)メールがローカルに配信されない

2011/12/01 14:01 PC(Linux)
しばらくほったらかしにしてたメールを久々に触ってみようと思ったら、何かとんでもないことになっていた。
# qmail-qstat
messages in queue: 54682
messages in queue but not yet preprocessed: 54680
最初はすわ不正中継爆弾? とか思ったのだが、
# cat /var/log/mail.log
1218742:Dec  1 10:19:04 debian qmail: 1322702344.535144 warning: unable to create local/8/1656100
...
おおう、これはローカルへの配信ができずに延々ループしてるっぽい。そしてmail.logのサイズがどんどんふくらんでいる。そこで、/var/spool/qmail/local/をのぞいてみたら、

空っぽ!

え、分割ディレクトリはどこへ行ったの?

分割ディレクトリを自動的に作る機能はなさそう…… しかし、分割ディレクトリがないのが原因っぽい。

ということで、分割ディレクトリを標準の23作成する。
# mkdir 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
して、chmod,chown,chgrpしたところ…… 動き出した。
ログをチェックして、warningが消えたことを確認

えー、なんだこれ。手で消した記憶はないので、update時に何かが消してしまったのかqmailの仕様が変わったのか。

うーん原因までは追えなかった。

一応解決。

2011/12/01(木)/dev/sdaが無い!

2011/12/01 11:37 PC(Linux)
起動時にデバイスノード/dev/sda, /dev/sda1, /dev/sda2が作成されない問題。

環境を整理すると、
kernel: 2.6.34.1(自分でコンパイル)
udev: 164-3
HDD: SATA HDD(内蔵)

/sys/block/sda/, /sys/block/sda/sda1/, /sys/block/sda/sda2/はできており、fstabsでのマウントも成功している。

これなら起動時にデバイスノードが自動的に作成されることを期待するのだが、なぜか作成されない。デバイスノードがないとfdiskとかできないので大変にまずい。

どうも1月にsqueezeに入れ替えたときから発生していたような気配だ。fstabsはUUIDで書いていたので、今まで問題が起こらなかった(もしくは気づかなかった)。

さて、squeezeからデバイス割り当ての仕組みがudevに変わっている。この移行に関しては注意書きが手順書に書かれるくらいなので大きな変更なのだが、一応これは気をつけてカーネルを作り直したつもり。起動はするし、警告も出てる気配がないのでこれはudevとsysfsの連携が単にうまくいっていない。

で、調べてみると、
[Sorcerer-admins] udev does not create sda, sdb and md0
完全に追えてないのだが、以下自分の解釈。udevの元々の仕組みはデバイスのモジュール組み込みを想定しているためモノリシックカーネルの対応が不十分な場合があるとのこと。あちこち回ってみると、udevのバージョン変更で直った例が多いようなのだが、debianは相変わらずtesting=unstableだし、依存関係がもうすンごかったので断念。うーん、推奨環境(カーネルもdebianのものを使用)じゃないのでバグ報告がためらわれる。しょうがないので、まずはコマンドラインで、
# cat  /sys/block/sda/dev /sys/block/sda/sda1/dev /sys/block/sda/sda2/dev
8:0
8:1
8:2
としてメジャー・マイナー番号を調べ
# mknod /dev/sda b 8 0
# mknod /dev/sda1 b 8 1
# mknod /dev/sda2 b 8 2
とした。

で、これを起動スクリプトに追加。
$ cat /etc/init.d/mk_sda_node
#!/bin/sh

if ! [ -e /dev/sda ]; then
        mknod /dev/sda b 8 0
        mknod /dev/sda1 b 8 1
        mknod /dev/sda2 b 8 2
fi

exit 0
雑にこんなんでいいや。すでにデバイスノードが存在してればmknodは特に問題は起こさないようだし。ずれることもないだろう(udevを頭から否定)。
update-rc.d mk_sda_node start 15 1 2 3 4 5 .
debian流で登録。single user modeでもあった方がいいでしょ。
update-rc.d: warning: /etc/init.d/mk_sda_node missing LSB information
update-rc.d: see <http://wiki.debian.org/LSBInitScripts>
 Adding system startup for /etc/init.d/mk_sda_node ...
   /etc/rc1.d/S15mk_sda_node -> ../init.d/mk_sda_node
   /etc/rc2.d/S15mk_sda_node -> ../init.d/mk_sda_node
   /etc/rc3.d/S15mk_sda_node -> ../init.d/mk_sda_node
   /etc/rc4.d/S15mk_sda_node -> ../init.d/mk_sda_node
   /etc/rc5.d/S15mk_sda_node -> ../init.d/mk_sda_node
LSBがらみは今度調べよう。

再起動してデバイスノードがあることを確認。うーん、次期wheezyまでこれでしのぐか…
OK キャンセル 確認 その他