2006年05月20日

Raw Input(直訳:生入力)

本日は、Visual Studio® User Group より、複数のキーボードを別処理するには というスレッドを話題にしようと思います。飽くまで簡単に。

同じタイプの複数の入力デバイスからの入力を受けたい、と言うのは、例えばゲームパッドでは良くある話でしょう。ですが、これがキーボードやマウスとなるとなかなか要望も稀で解決策も難しい物があります。ついでに、使いこなすのも大変そうです。

基本的なところでは、Windows はそれらを扱いません。今使っているノートパソコンにはタッチパッドが付いていて、かつ USB でマウスも接続していますが、この両者はごくナチュラルに同居しており、どちらでもカーソルやボタンを操作できます。プログラム的にも、この両者からの入力は分け隔て無く .NET で言えば MouseDown とか MouseMove とかが発生します。そして、どちらからの入力なのかというのは考慮されず、透過的に扱うことを強制されます。

.NET のイベントや、その下層レベルのメッセージである WM_LBUTTONDOWN とか WM_MOUSEMOVE などで扱えないのならばどうするか。その解決になるかも知れない一つが、Raw Input に関連する API 群です。その端っこをちょっとだけ踏んでみます。

続きを読む
posted by Hongliang at 05:07| Comment(6) | TrackBack(0) | C# | このブログの読者になる | 更新情報をチェックする

2006年04月19日

いかに固定するか

日々拝読している akiramei さんの 匣の向こう側 - あまりに.NETな の記事で、Genericsとunsafe というのがあったのですが。

Marshal.UnsafeAddrOfPinnedArrayElement

これが勝利の鍵だ。

と言う結論ですが、これ間違いです。

MSDN の解説には次のように書かれています。

配列は、GCHandle を使用して pin を実行してあります。パフォーマンスを高めるため、このメソッドは渡された配列に対して一切の検証を行いません。この結果、予期しない動作につながることがあります。

分かりづらい文章ですね。原文だと極めて明快です。

The array must be pinned using a GCHandle before it is passed to this method.

このメソッドによって配列が自動的に固定されることはありません。配列を固定していない場合、このメソッドを呼び出した後に GC が起動して配列のアドレスが移動し、メソッドで取得したアドレスが無効になる可能性があります。

英文通り、事前に GCHandle.Alloc(object, GCHandleType.Pinned) で固定しなければいけません。で、GCHandle.Alloc したなら GCHandle.AddrOfPinnedObject メソッドを使うことで先頭アドレスが取得できます。

Alloc の第一引数はプリミティブなメンバしか含まない構造体でなければなりませんが、これの評価は実行時にされるので Generics でも問題なく使えます。

posted by Hongliang at 01:58| Comment(0) | TrackBack(0) | .NET | このブログの読者になる | 更新情報をチェックする

2006年04月15日

C# と NTFS ストリームの甘くなくもない関係

今回は、C#とNTFSストリームの甘くない関係 のアップデートを行います。ただし、IPropertSetStorage を使ったアクセスは自由度が低いと感じたため、ITaskScheduler みたいなきっちりしたコードではなく、飽くまで「こうすれば動く」程度にとどめたいと思います。

以前の記事では COM オブジェクトが例外出しまくりで何でさ、で終了でしたが、ちょっと COM の知識が増えた今では何が問題だったのかは明らかです。大雑把に言って、COM オブジェクトは IUnknown または IDispatch から派生しますが、IDispatch は名前でメソッドを検索するのに対し、IUnknown は 先頭からの宣言順で検索します。つまり、以前書いたコードの IPropertySetStorage は宣言が Enum メソッドしかないため、IUnknown 的にはこれは Create メソッド相当なんですね。当然引数が全く違うのでメソッドは失敗していたってわけです。

メソッドの宣言順が大事なのであって宣言の正当性はわりとどうでも良いので、以前書いた IPropertySetStorage インターフェイスは以下のように書き換えれば Enum が成功するようになります。

[ComImport, Guid("0000013A-0000-0000-C000-000000000046"),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPropertySetStorage {
    int Create();
    int Open();
    int Delete();
    [PreserveSig] int Enum(
        [MarshalAs(UnmanagedType.Interface)]
            out IEnumSTATPROPSETSTG propSetStg);
    }
}
続きを読む
posted by Hongliang at 14:37| Comment(16) | TrackBack(1) | C# | このブログの読者になる | 更新情報をチェックする

2006年04月14日

ListView とアイテムとフォーカスと

以前書いた C#とNTFSストリームの甘くない関係 ですが、今のところ五本の指に入るぐらいの人気(笑)にも関わらず内容はひどく寂しいものです。寂しいだけなら良いのですが、なにせ COM のことをほぼ全く理解していなかったときに書いた記事。思いっきり勘違いした上での愚痴ですので今は見るに堪えません。IDispatch と IUnknown をごっちゃにしてるんですよね。そのうち改めて記事にしようと思っています。正直実用は微妙であるという認識が強くなってるんですが。Microsoft が配布してる Dsofile.dll 使えば済む話っぽいんですよね……。

さて、今回は某掲示板から、ListView のフォーカスが移動したときのイベントって存在するのか、という問題です。フォーカスの移動ってのは、Control キー押しながらカーソルキーを押したときに動くアレです。選択状態とは無関係に移動できます。

さて、問題のイベントですが、率直に言って存在しません。

が、もちろん一切存在していないのでは深いレベルでは色々困るわけで、Win32API レベルでは存在します。それが LVN_ITEMCHANGED です。この通知メッセージは、リストビューのアイテムが変更されたときに送られてきます。アイテムの変更にはもちろんフォーカスが当たっているアイテムの変更も含まれます。

続きを読む
posted by Hongliang at 01:29| Comment(6) | TrackBack(0) | .NET | このブログの読者になる | 更新情報をチェックする

2006年04月08日

Screen クラスにおける細かいこと。

掲示板で答えるときは大抵事前に動作確認するわけですが、「画面プロパティ」の「画面の色」のビット数を取得・設定するには? というスレッドで「API 使えばできるさ」と答えた関係上、当然ながら最小限のコードで試しています。

で、そのついでに System.Windows.Forms.Screen クラスをいじっていたのですが、ここでちょっとした問題に気付きました。

Screen.DeviceName でディスプレイのデバイス名を取得できますが、これで取得できる文字列は実は本来の文字列よりも長くなってます。ちなみに取得する用の関数で使用する構造体のデバイス名格納用メンバは TCHAR[32] と定義されています。で、これを .NET の char[] で表現して、そのまま String のコンストラクタに突っ込んだだけみたいな形なんですよね。

つまりわかりやすく言うと、NULL 文字以降のまで保持しちゃってるってことです。

実用上はほぼ問題ないでしょうけど(表示するにもアンマネージドに渡すにも NULL 文字で終端されるでしょうからね)、ちょっとだけ気になった点でした。

posted by Hongliang at 21:16| Comment(0) | TrackBack(0) | C# | このブログの読者になる | 更新情報をチェックする

2006年04月02日

マルチメディアタイマ・改訂版

さて、前回アップしたマルチメディアタイマですが、速攻で問題点を見つけました。このままでは実行中に参照が無くなったとき、タイマが実行中でも止まってしまいます。

それはいけない。手本である System.Timers.Timer はタイマが有効な間は参照が無くなってもタイマを止めるまでは動き続けるようにデザインされています。

今回初めて知ったんですが、標準ライブラリの三つのタイマクラスのうち System.Threading.Timer クラスだけは実行中でも GC に回収されます(当然タイマは強制中断)。

続きを読む
posted by Hongliang at 21:35| Comment(2) | TrackBack(0) | .NET | このブログの読者になる | 更新情報をチェックする

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。