2008年1月20日日曜日

FONが我が家にやってきた

皆さんは"FON"をご存知でしょうか?

FONはWiFiを世界中に無料で広げようとするコミュニティです。

ユーザは自分の無線LANアクセスポイントを他のFONユーザと共有し、
代わりに他のFONアクセスポイントを無料で利用できる、というものです。

ちなみに、このFONユーザのことを"Fonero(フォネロ)"と呼びます。

Foneroには3種類あります。
Linus
自分の無線LANルータをAP(アクセスポイント)として無償で公開する代わりに、他のFoneroのAPも無償で利用することができる。
Bill
自分のAPにアクセスがあった場合に50%の使用料を得られるが、他のFoneroのAPを利用するには使用料を支払う必要がある。
Alien
APの公開は不要であるが、他のFoneroのAPを利用するには使用料が必要である。
ちなみに、"Linus"はLinuxの開発者リーナス・トーバルズから、
"Bill"はマイクロソフトの創始者ビル・ゲイツから来てるそうです。

無償利用が"Linus"で、有償利用が"Bill"ってあたりに、
何かのメッセージを感じるのは私だけでしょうか?

FONに参加するには、公式サイトより会員登録を行い、
FONが提供する無線LANルータ"LA FONERA"を購入・設置する必要があります。

"LA FONERA"にはプライベート用と公開用の2つのネットワークを持っているため、
自分のAPを安全に利用することができます。


なぜか、これまで我が家では有線ルータを使用していました。

しかし、最近になってノートパソコンを使う機会が増えたので、
無線LANルータを購入しようと思いました。

そこで、せっかくだから、FONに参加することにしました。


無線LANルータ"LA FONERA+"がこちら。



































なぜかステッカーが大小2つ入ってた。


説明書の通りに設置し、ノートPCからプライベート用ネットワーク"MyPlace"を探す。

接続して、ブラウザを開くと"http://192.168.10.1"に自動的にアクセスされる。

"LA FONERA+"の設定ページが開いたので、FONの登録ページへアクセスする。

・・・。

繋がらない。

よく見たら"No network access"と表示されている。













よく見たら、ノートPCの"ワイヤレスネットワークの選択"にも、
公開用ネットワーク"FON_AP"が見えない。


とりあえず、ルータを元の有線ルータに戻し、FONサイトのヘルプを見てみる。

どうやら、"PPPoE"や"PPTP"の場合は、設定を変えなければいけないらしい。

日本の多くのISPはPPPoEを採用していることを考えれば、
そのように説明を書いておいてくれてもよかったんじゃないかな?


さっきの"LA FONERA+"の設定ページをもう一度開いてみる。

"Advanced" > "Internet Connection Settings"を選択。

"Mode"を"PPPoE"に切り替え、ISPの契約通りユーザ名とパスワードを入力する。













もう一度、ネットワーク接続を開いてみる。















今度はちゃんと"FON_AP"も見えました。

再度、FONへ登録を行う。今度はちゃんとアクセスできた。

無事登録を完了し、晴れてFONメンバの一員となることができました。


もし、私のAPを発見した人は、ぜひ利用してみて下さい。


参照:
FON
Wikipedia : FON

2008年1月18日金曜日

C#でSingletonパターン

「結婚したら、2人の収入は1つの財布(家計)へ」

共働きの家庭ではよくある話ですが、今回はそんな話。(どんなだ?)


あるクラスのインスタンスが1つしか存在しないことが重要になることがあります。

上記の例だと、財布が1つしか無いことは重要です。

"へそくり"があったら大変ですよね(笑)

「財布は1つであって欲しい("財布"というクラスのインスタンスは1つであって欲しい)」

このように、インスタンスが唯一の存在であることを保証するために、
"Singleton"パターンが有効となります。

"Singleton"パターンを実現するには
  1. インスタンスが1つしか存在しない

  2. 別のクラスから直接インスタンスを生成することが不可

  3. インスタンスへは公開されたポイントからのみアクセス可能
を満たさなければなりません。

実際に"Singleton"パターンを用いてみます。
(コンソールアプリケーションプロジェクトを作成して下さい。)

クラスを1つ追加します。名前は例にならって"Wallet.cs"にします。

/*** Wallet.cs ***/
// 1. 唯一のインスタンスを格納する変数
private static Wallet instance = null;

// 2. 直接インスタンス生成できないように、コンストラクタを不可視に
private Wallet() { }

// 3. インスタンスへのアクセスが可能なアクセスポイント
public static Wallet GetInstance()
{
if (instance == null)
instance = new Wallet();
return instance;
}

ソースコードを見ればわかる通り、他のクラスからは③へのみ、
アクセスすることが許されています。

そして"GetInstance"メソッドでは、変数"instance"が初期値の"null"である場合、
コンストラクタを呼び出すことでインスタンスを生成しています。

コンストラクタは"private"修飾子により、他のクラスからは呼び出せません。

これにより、インスタンスが1つしか生成されないことを保証しています。

全てのクラスは、"GetInstance"メソッドを通じて共通の"instance"変数へ
アクセスすることになります。

これが"Singleton"パターンです。


実際に、"instance"変数へアクセスする様子を見てみましょう。

/*** Wallet.cs ***/
// 財布に入ってるお金
private int money = 0;

// 財布にお金を入れる
public void Income(int value)
{
this.money += value;
}

// 財布の中を見る
public int Look()
{
return money;
}

/*** Program.cs ***/
static void Main(string[] args)
{
new Thread(new ThreadStart(Man)).Start();

new Thread(new ThreadStart(Woman)).Start();
}

private static void Man()
{
Wallet.GetInstance().Income(5000);

Console.WriteLine("僕たちの財布には" + Wallet.GetInstance().Look() + "円入ってるよ。");

Thread.Sleep(5000);
}

private static void Woman()
{
Wallet.GetInstance().Income(2000);

Console.WriteLine("私たちの財布には" + Wallet.GetInstance().Look() + "円入ってるわ。");

Thread.Sleep(5000);
}

実行します。




出力のための遅延や、"Income"メソッドと"Look"メソッドのタイミングによって、
表示している画像の結果と多少違うかも知れませんが、
同じインスタンスへアクセスしていることがわかると思います。

このように、"Singleton"パターンを用いることで、
インスタンスが唯一であることを保証することができました。

モジュール結合度と共通結合
モジュールを分割する基準の1つとして、モジュールの独立性が用いられます。
独立性が高いほど、他のモジュールの影響を受けにくく、拡張性などが高くなります。

モジュールの独立性を評価する指標の1つに、モジュール結合度があります。
モジュール結合度が高いほど、そのモジュール同士の独立性は低くなります。
逆に、結合度が低いほど、独立性は高くなります。

例で挙げたメソッド"Man"と"Woman"は、唯一の"instance"インスタンスが持つ
共通の"money"を参照しています。
このように、共通領域のデータを参照するモジュール同士の場合、
その結合方法は"共通結合"と呼ばれます。
共通結合は、モジュール結合度は2番目に高く、独立性は低めです。
例では、互いに"Income"メソッドを呼び出すことで、共通のデータ"money"へ
影響を与えています。

"Singleton"パターンは共有結合の代表的な実装方法です。



今後も、有用なデザインパターンは随時、記事にしていきたいと思います。

C#でのユーザー定義Exception(例外)の作り方

「プログラムにエラーはつきもの」

と言うのは、言い過ぎかも知れませんが、
エラーが起こる可能性を0(ゼロ)にすることはなかなか難しい。

エラーが発生した場合、そのエラーがなぜ発生したのかがわかると、
対処がしやすいものです。

そのため、何のエラーかを識別するために、.NET Frameworkには多くのExceptionが存在します。

しかし、これらのExceptionだけでは足らず(適切なものがなく)、
自ら定義をして例外の種類を追加したいことがあります。

このような場合、ユーザー自身がExceptionを定義することで、
より細かくエラーを識別する手助けをすることができます。


今回は例外を試すだけなので、コンソールアプリケーションで行いましょう。
("ファイル" > "新しいプロジェクト" > "コンソール アプリケーション")

例外のためのクラスを追加します。

例外を作成する場合は、クラスの最後に"Exception"と付けると、
コーディング時に便利です。

クラス名は"OriginalException"としておきましょう。

/*** OriginalException.cs ***/
public class OriginalException : Exception
{
public OriginalException() { }

public OriginalException(string message) : base(message) { }

public OriginalException(string message, Exception inner) : base(message) { }
}

このように、例外を作成する場合は必ず"Exception"クラスを継承しなくてはなりません。

また、推奨される3つのコンストラクタは実装しておくと便利です。

2つ目と3つ目のコンストラクタには、コンストラクタ宣言の後に

: base(...)
が付いています。

これは継承元クラス(baseクラス,この場合"Exception"クラス)のコンストラクタを
呼び出すことを意味しています。
通常、クラスに継承関係がある場合、継承元のコンストラクタが実行された後、
継承先のコンストラクタが実行されます。
しかし、その場合、コンストラクタの引数は継承先のコンストラクタに渡され、
継承元のコンストラクタには渡されません。
そこで、": base"を用いることで、継承元のコンストラクタへ引数を渡すことができます。

実際に作成した"OriginalException"を試してみます。

/*** Program.cs ***/
private static void ThrowTest()
{
Console.WriteLine("Enter ThrowTest method.");
throw new OriginalException("This is an OriginalException.");
Console.WriteLine("Exit ThrowTest method.");
}

[STAThread]
public static void Main()
{
ThrowTest();
}

実行しようとすると、以下のように警告されます。

到達できないコードが検出されました。

これは、例外を投げると、それ以降のコードが処理されず、
"ThrowTest"メソッドの3行目が実行されないことを意味しています。

警告を無視して実行します。


コンソールに「Enter ThrowTest method.」と表示された後、


とエラーが表示されました。

次に、Mainメソッドを変更して、例外処理を行います。

/*** Program.cs ***/
public static void Main()
{
try
{
ThrowTest();
}
catch (OriginalException e)
{
Console.WriteLine("Exception occured : " + e.Message);
}
finally
{
Thread.Sleep(5000);
}
}

実行します。
("using System.Threading;"を追加して、名前空間の修飾省略定義を
 行って下さい。)


正しく例外をキャッチすることができました。


.Net Frameworkでもユーザー独自の例外を作成することができます。

ユーザー独自の例外を用いることで、よい効率的なコーディングができるように
なります。


参照:
MSDN : 方法:ユーザー定義の例外を作成する

2008年1月16日水曜日

マウスの位置を取得し続ける(C#) その4

前回前々回の記事を見ていない人は先にそちらを。


前回までで、ローカルフックを用いてマウスの位置を取得し続けることはできました。

しかし、ローカルフックでは、そのプログラムが動いているスレッド上でしかマウスの
位置が取得できません。
(前回の場合、スレッド上で動いているのは"Form1"のみなので、
 "Form1"上の位置は取得できます。)

そこで、前回のソースを少し改変して、グローバルフックで取得できるようにします。

グローバル(Low-Level)フックを用いて、マウスメッセージを取得するためには、
フックタイプとして以下の値が必要になります。

/*** Form1.cs ***/
private const int WH_MOUSE_LL = 14;
"GetModuleHandle"メソッドをDLLインポートします。

/*** HookMethods.cs ***/
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(String lpModuleName);

"SetMouseHook"メソッドを以下のように変更します。

/*** Form1.cs ***/
private void SetMouseHook(HookMethods.HookProcedureDelegate proc)
{
using(Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
{
hHook = HookMethods.SetWindowsHookEx(WH_MOUSE_LL, proc, HookMethods.GetModuleHandle(module.ModuleName), 0);
}

if (hHook == IntPtr.Zero)
{
MessageBox.Show("SetWindowsHookEx Failed.");
}
}

実行します。



"Form1"以外でも、マウスの位置を取得できました。

上記のソースコードについて解説します。

"GetModuleHandle"メソッドは、呼び出し側プロセスのアドレス空間に
マップされているモジュールのハンドルを返します。
モジュール名の指定は、1つ目の引数"lpModuleName"に指定します。
"SetWindowsHookEx"メソッドの3つ目の引数"hInstance"は、
フックプロシージャを保持しているモジュールのハンドルを指定する必要が
あります。
"GetModuleHandle"が返すハンドルを"SetWindowsHookEx"の
3つ目の引数として与えることで、モジュールを指定します。

usingステートメント
using( オブジェクトの生成 )
{
処理
}

usingステートメントを用いると、using{ }ブロックから制御が離れた時に、
( )内で生成されたオブジェクトのDispose呼び出しが自動的に行われます。

usingステートメントはアンマネージリソースに対して効果を発揮します。

アンマネージリソース
C#を使っていると、オブジェクトに対してメモリ管理を意識することはありません。
これは、自動メモリ管理機能「ガベージコレクタ」が管理してくれているからです。
しかし、一部のオブジェクトはメモリを解放するタイミングを意識して
プログラムしなければなりません。
このオブジェクトをアンマネージリソースと言います。
ファイルやウィンドウ、データベース接続などがその対象です。


今回の場合、"ProcessModule"がアンマネージリソースであるため、
usingステートメントが使用されています。
(モジュールは、".dll"や".exe"などの実行ファイルだから。)


これで、どの位置にあるマウスでも、その位置を取得し続けることができました。

ただ、本当は位置だけではなく、他の情報も取得できます。
(例えば、マウスクリックとか)

マウスの位置を取得し続ける(C#) その3

前回の記事を見ていない方は「マウスの位置を取得し続ける(C#) その2」から
先にどうぞ。

前回は、フックを行うための準備まで実装しました。

今回はいよいよ、フック処理を実装します。

の前に、フック処理の開始 / 終了を切り替えるボタンを設置しましょう。
"Form1.cs"のデザイナを開き、"Button"を"Form1"上に設置します。
作成された"button1"をダブルクリックし"button1_Click"メソッドを作成します。

では、マウスフック処理を行うための準備をします。

まずは、"Form1.cs"に名前空間の修飾省略定義を行います。

/*** Form1.cs ***/
using System.Runtime.Interopesrvices;
次に、位置と、マウスフックを表す構造体を宣言します。

/*** Form1.cs ***/
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}

[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}

構造体"POINT"は画面内の座標を表します。
同じく,"MouseHookStruct"はマウスフックを表します。

フックプロシージャのハンドルを保存しておくための、静的フィールドを用意します。

/*** Form1.cs ***/
private static IntPtr hHook = IntPtr.Zero;
次に,マウスに対するフックタイプを宣言します。

/*** Form1.cs ***/
private const int WH_MOUSE = 7;
フックタイプには,様々なものがあります。詳しくはMSDNを参照して下さい。

マウスフックを設定 / 削除するためのメソッドを作成します。

/*** Form1.cs ***/
private void SetMouseHook(HookMethods.HookProcedureDelegate proc)
{
// マウスフックを設定
hHook = HookMethods.SetWindowsHookEx(WH_MOUSE, proc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
if (hHook == IntPtr.Zero)
{
MessageBox.Show("SetWindowsHookEx Failed.");
}
}

private void RemoveMouseHook()
{
// フックを削除
if (HookMethods.UnhookWindowsHookEx(hHook) == false)
{
MessageBox.Show("UnhookWindowsHookEx Failed.");
}
}


マウスフックプロシージャの実装を行います。

/*** Form1.cs ***/
public IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if(nCode >= 0)
{
// コールバックからのデータを整理する.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));

String strCaption = "x = " + MyMouseHookStruct.pt.x.ToString("d") + " : y = " + MyMouseHookStruct.pt.y.ToString("d");
this.Text = strCaption;
}
return HookMethods.CallNextHookEx(hHook, nCode, wParam, lParam);
}


これで、マウスフックの処理を行うための準備ができました。

では、実際にマウスフック処理を行ってみましょう。
"button1"が押されたらフックを開始し、再度押されたらフックを終了する処理を記述します。

/*** Form1.cs ***/
private void button1_Click(object sender, EventArgs e)
{
if (hHook == IntPtr.Zero)
{
SetMouseHook(MouseHookProc);
this.button1.Text = "Unhook Windows Hook";
}
else
{
RemoveMouseHook();
this.button1.Text = "Set Windwos Hook";
}
}


実行します。

以下のような警告がでますが、後ほど削除する部分なので、無視します。
'System.AppDomain.GetCurrentThreadId()' は古い形式です: 'AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202'


"button1"を押すと、フックが開始されます。

"Form1"内でマウスを動かすと、その座標がタイトルに表示されます。


"Form1"外では、タイトルが変化しません。

もう一度"button1"を押すと、フックが終了します。


ローカルフックを用いて、マウスの位置を取得し続けることはできました。

.NETのみで実装した場合に比べて、動きがスムーズです。


次は、グローバルフックを用いた場合について説明しますが、
今回も長くなってしまったので、次回にまわしたいと思います。

マウスの位置を取得し続ける(C#) その2

前回の最後に、「マウスの位置が○○に来たら」のようにイベントを設定することができない、と書きましたが、これは誤りです。

正確には、「.NET Frameworkだけではできない」です。

このようなイベントを設定するためには、"フック"を用いるそうです。

フックについては「参照:KAB-studio フックのしくみ」が非常にわかりやすいので、知らない人は参照してみて下さい。

まとめると、
"フック"

システム内のメッセージを条件に合わせて拾い上げること

"ローカルフック"

自スレッドのみに対して働くフック

"グローバルフック"

全てのスレッドに対して働くフック

です。

前述のように「(範囲の内外を問わず)マウスの位置に応じて処理を行う」場合は、
グローバルフックを用いなければなりません。

しかし、「グローバルフックは .NET Framework ではサポートされていない」そうです。

・・・「.NET Framework では」?

つまり、.NET Framework 以外でグローバルフックを取得すればいいわけです。


まずはローカルフックのみを試してみます。
前回のプログラムと内容的には類似していますが、精度が違います。)

前回と同様に、新しいプロジェクトを開始します。
("ファイル" > "新しいプロジェクト" > "Windows フォームアプリケーション")

ソースコードを見やすくするために、フックを扱うクラス"HookMethods"を別に定義します。
("プロジェクト" > "クラスの追加" > "クラス" で名前を"HookMethods.cs"と入力)

名前空間の修飾省略定義を行う。

/*** HookMethods.cs ***/
using System.Runtime.InteropServices;
フックを扱うためのメソッドを3つ、"user32.dll"より呼び出します。

/*** HookMethods.cs ***/
// フックプロシージャのためのデリゲート
public delegate IntPtr HookProcedureDelegate(int nCode, IntPtr wParam, IntPtr lParam);

// フックプロシージャ"lpfn"をフックチェーン内にインストールする
// 返り値はフックプロシージャのハンドル
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProcedureDelegate lpfn, IntPtr hInstance, int threadId);

// "SetWindowHookEx"でインポートされたフックプロシージャを削除する
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(IntPtr idHook);

// 次のフックプロシージャにフック情報を渡す
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

3つのメソッドについて解説します。

"SetWindowsHookEx"と"UnhookWindowsEx"はセットで使われます。

"SetWindowsHookEx"は、フックタイプに関係するフックチェーンに対して、
フックプロシージャをインストールするためのメソッドです。

監視する対象はフックタイプで指定します。
フックタイプには,マウスを監視する"WH_MOUSE"やキーボードを監視する
"WH_KEYBOARD"があります。
1つ目の引数"iHook"に指定します。

フックした時の処理は、フックプロシージャの実装に委ねられます。
フックプロシージャへのポインタを2つ目の引数"lpfn"に指定します。
そのため、C#ではデリゲートを用います。
フックプロシージャの実装は、デリゲートの実装で行います。

"SetWindowsHookEx"メソッドの返り値は、
メソッドが成功した場合は、フックプロシージャのハンドルが、
メソッドが失敗した場合は、NULLが、
返ります。

"SetWindowsHookEx"で返されたフックプロシージャのハンドルを、
"UnhookWindowsHookEx"の引数として指定することで、フックが削除されます。

"CallNextHookEx"は、現在のフックチェーン内の次のフックプロシージャに、
フック情報を渡します。
他のプログラムが同じフックチェーンに対してフックしている場合、
他のプログラムのフックプロシージャに対してフック情報を渡さなければ、
そのフックプロシージャは実行されません。つまり、フックされません。
"CallNextHookEx"を実行しなければ、他のプログラムに影響を及ぼします。
そのため、フックプロシージャの実装コード内で、"CallNextHookEx"を呼び出す
必要があります。

これらのメソッドは、必ず3つセットで利用する必要があります。

これで、フックを行う準備はできました。


今回は長くなってしまったので、続きは次回にします。


参照:
MSDNフォーラム クリックしただけで別アプリケーション上の TextBox に文字列をペーストしたい
Stephen Toub : Low-Level Mouse Hook in C#
MSDN フック

マウスの位置を取得し続ける(C#)

マウスの位置を取得し続けるプログラムを考えてみる。

.Net Frameworkには"System.Windows.Forms.Cursor"というクラスがあります。

このクラスは「マウスポインタの描画に使用されるイメージを表します。」だそうです。

簡単に言うと、マウスポインタだと。

この"Cursor"クラスを使って,マウスの位置(X座標,Y座標)を取得するには,

Cursor.Position.X
Cursor.Position.Y

を使用します。

これを使って、100ms毎にマウスの位置を取得し,
"Form1"のタイトルに表示し続けるプログラムを作ってみましょう。
("ファイル" > "新しいプロジェクト" > "Windows フォームアプリケーション")

/*** Form1.cs ***/
public Form1()
{
InitializeComponent();

new Thread(new ThreadStart(GetMousePosition)).Start();
}

public void GetMousePosition()
{
while (true)
{
SetText();
Thread.Sleep(100);
}
}

public delegate void SetTextDelegate();

public void SetText()
{
if (InvokeRequired)
{
Invoke(new SetTextDelegate(SetText));
return;
}
this.Text = "x = " + Cursor.Position.X + " : y = " + Cursor.Position.Y;
}

"Thread"クラスを扱うために、"System.Threading"名前空間の修飾省略定義を
入れることをお忘れなく。

using System.Threading;
実行してみる。


どうやらマウスの位置を取得し続けているようだ。

意外なことに"Form1"の範囲外でも、マウスの位置を取得することができた。

ただ、これだと「100ms毎」という制限があり、雑な感じがする。
(だからと言って、1ms毎にすれば良いというわけでもない。)

また、「マウスの位置が○○に来たら」のようにイベントを設定することができない。("Form1"の範囲内でなら可能。)


簡単なものならこれでいいが、複雑なことを行いたい場合は別の方法で取得する必要がありそうだ。

注意:上記のプログラムをそのまま実行し、"Form1"の終了ボタンを押すと、
   "ObjectDisposedException"が投げられます。
   これは、終了ボタンを押したことで、オブジェクト"Form1"が解放(メモリ上から
   消える)されたにも関わらず、"SetText"内でアクセスしようとしたためです。
   必ず、終了ボタンが押された時に、スレッドを抜けるように作成して下さい。