CATEGORY:C#
何の因果か、諸事情により生まれて初めてDirectXなる物を触った。C#で。DirectShowLibってのを使ってDirectShowです。DirectShowって、何?っていうレベルなんだけどね。
しかし、.NETでもCOMが使えます!なんつってもデリケートなトコはそんな変わんないからあまりうれしくない。まあExcelを使ったときの隅から隅までReleaseComObject地獄よりはマシだったけど。
仕事でVBのコードを見てるのだけど、慣れない。何もかもダメだ。関数が戻り値を返すのを、関数名に値を入れることで実現できます。なんてのも慣れない。なんかスタックオーバーフローしそうなイメージが無い?いや、昔ね、新人さんがC#で
public int Foobar {
set {
// this.foobar = value; // こうじゃなくて
this.Foobar = value; // こう
}
}
みたいなコードを書いて見事に再帰してスタックを食いつぶしてた事があったので。まあ関数の戻り値とは違うけど、なんかそんなイメージがね。あるのよ。
あと、配列。
Dim foobar As Integer() = New Integer(10)
こんな感じで配列を定義した時、配列のサイズは11になります。なんて言われてもね、馴染めないんですよ。一瞬固まってしまうんですよ。VB難しいなぁ。
.NET Framework(1.1だけ?)のSplitterクラスが賢くない。Splitterって、何?っていうと、2ペインとかのウィンドウで使われてるヤツで、ウィンドウ内のコントロールの幅をね、ドラッグして変えられたりね、するアレですよ。アレ。アイツがね、スゲー賢くない。何が賢くないかと言うと、SplitterをドラッグしてるときにAlt+Tabでアプリを切り替えるとおかしな事になるのです。ほぼ全てのキーイベントをSplitterがガッチリ横取りしてしまうのですよ。いや分かんないけど。少なくとも他のコントロールがキーイベントを受け取れなくなります。例を挙げて説明します。
.NET FrameworkのControl.GetNextControlメソッドが無限ループするケースがあるという事を、声を大にして言いたい。のだけれど、コレを再現させるテストプログラムを作ろうとすると結局無限ループしなかったりという事になって、どうにももどかしい。
.NET FrameworkのControlは、Dispose時に自身を親のControlCollectionから削除させる。んで、この削除を行うControlCollection.Removeというメソッドは、コレクションからコントロールを削除した後、AfterControlRemovedというメソッドを呼び出すのだけれど、そこでアクティブなコントロールの設定が行われる。コントロールが削除されアクティブなコントロールが無くなるのを防ぐ、という事なのだろう。まあいいよ。それはいいよ。しかし、その処理、SelectNextControl(内で呼ばれるGetNextControl)で無限ループが起きるのよ。次のコントロールを延々探し続けるのよ。もの凄い勢いで探し続けるのよ。彼ってば全然諦めないのよ。それが若さか!ってなもんなのよ。そりゃないよ。どういったケースでそれが起きるかというと、前述の通りハッキリさせることが出来ていません。所謂八方塞がりというヤツです。困ったワタシはキーボードの掃除などを始めてみたものの、問題の解決には至りません。何故かというと、キーボードとは多分関係ないからです。マウスの掃除もしましたが結果は同じ。理由も同じです。これ参ったなぁ。
.NET FrameworkのXMLシリアライズ(SOAPも含む)を使用したアプリケーションが、最初のシリアライズ時にフリーズすることがあります。何で?というと、XPのバグらしい。XmlSerializer クラスから起動された csc.exe が一定時間応答を停止する場合がある
と。そのままです。さり気なく致命的です。どうしてそうなるのかというと、
XmlSerializer クラスは自クラスで使用するアセンブリの動的なコンパイル処理を行うために csc.exe を起動します。csc.exe はコンソールアプリケーションのため、Console IME が起動していない場合、コンソールでの日本語入力支援機能として Console IME を起動します。この時、Console IME 起動時の処理でデッドロックが発生しその結果 csc.exeが停止した状態になる場合があります。この現象発生時には、最終的に XmlSerializer を使用するプログラムが一定時間応答を停止した状態となります。
そういうことらしい。うん。やっぱり致命的ですね。さて。解決策は二つ。レジストリを弄ってコンソールでconime.exeが起動されないようにするか、XMLシリアライザをプリコンパイルし、動的にアセンブリを生成しないようにするか。レジストリを弄るわけにもいかないし、プリコンパイルしたアセンブリを使うにも、実行する全てのマシンにパッチをあてないと動かない気がした。以前に別件で試したけど、パッチ当たってないマシンだとXmlSerializerAssemblyAttributeが使えないから動かないんだよね。当たり前だけど。
じゃあどうすんのよ?
こまったさんのカレーライス、知ってるよね?大丈夫だよね?うん。じゃ。
.NET Frameworkには.NET Remotingという機能(?)がある。全体としての定義はぶっちゃけよく知らないのだけれど、異なるプロセス間で通常のメソッド呼び出しが出来ちゃうのですよ。呼び出す対象をipとポートで指定するので、異なるマシン間でもメソッドが呼び出せるという香ばしい機能です。
さて。今日、そいつを使ってちょっと困った問題にぶち当たりました。何が問題かというと、通信のサーバ側で特定のポートをlistenするのですが、そのサーバで別プロセスを実行し、その実行したプロセスを終了せずにサーバを終わらせると、ポートのlistenが終了しないと。だから、もう一度サーバを立ち上げて同じポートをlistenしようとするとbindに失敗して残念な結果に、というものです。えええ、何それ?第一誰がlistenしてんのよ?Frameworkが頑張ってんの?
取りあえず原因が分からないので放って置いたんだけど。で、今以下のようなテストコードを書きました。あ、クライアントコードは必要ないのでありません。
public class Test : MarshalByRefObject {
public static void Main() {
Test obj = new Test();
Process p = null;
try {
p = Process.Start(@"C:\WINDOWS\system32\notepad.exe");
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
finally {
if (p != null)
p.Close();
}
}
TcpChannel chan;
public Test() {
chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
}
}
かなりガムシャラですが、極限まで削るとこういう事で再現できるはずです。ポートを開いて別プロセスを立ち上げて何もせずに終了。作ったexeを一回実行して終了したらそのままもう一回実行。そうすればTestのコンストラクタで例外が発生するハズ。そのハズでした。が、何事もなく起動。アレ?netstat -aで見ても、8085をlistenしてる人はいません。ぬーーーーん。余りに簡単過ぎるかと思って、Formを使ってみたりと色々やったけど結果再現せず。なーんでーやねーーーーん。UnregisterChannelとかStopListeningとか、色々足掻いてダメだったのに。こういうの、困るよね。再現できるコードが出来ないと問い合わせも出来ない。かといって製品のコードをビローンと見せるわけにもいかないし。狼少年になった気分だよ。オッサンだけど。
.NET Frameworkのアプリで、キーバインドを設定可能にする。設定ファイルで読み書きするキーコードを、標準で用意されているKeysという列挙で行おうとするのは自然な考えであるハズだ。キーコードを数値で書き出すより分かり易いし、読み込んでそのまま使えるからね。