2012年1月22日日曜日

ブログ移設

ブログを移設しました。どうぞ宜しくお願い致します。
   ↓
 Lassy silly talk

2008年2月2日土曜日

アプリケーション構成ファイル(C#)

前回の記事で、設定ファイルにはXMLが有効であると述べましたが、
単純に設定ファイルだけならアプリケーション構成ファイルの方が簡単で便利です。
(と、知り合いの人につっこまれました^^;)

アプリケーション構成ファイルは、文字通りアプリケーションの設定内容を
記述した、.Net Framework固有の設定ファイルです。

アプリケーションの設定をこのファイルに記述しておき、必要な時に読み取りにいく
処理を記述することで、容易に値を設定することが可能になります。

アプリケーション構成ファイルは、作成時には"App.config"という名前で
作成されますが、デバッグ / ビルドをした際にプロジェクト名に則した
ファイル名に変更されます。

実際に使いながら見ていきましょう。


まずは、"コンソール アプリケーション"プロジェクトを選び、
"AppConfigTest"と名前をつけます。

次に、メニューの"プロジェクト" > "新しい項目の追加"を選択します。

出てきた中から"アプリケーション構成ファイル"を選択します。
(名前を変更する必要はありません。)











作成されたばかりの"App.config"にはほとんど何も記述されていません。

このファイルがXMLファイルであること、文字エンコードが"utf-8"であること、
"configuration"という要素名のタグがあること、だけです。

ここに、以下のような記述をします。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Title" value="Localhost Web Pages" />
<add key="IPAddress" value="192.168.0.1" />
<add key="port" value="80" />
</appSettings>
</configuration>

注意しなければならないことは、"appSettings"の先頭が小文字であることです。

大文字にすると読み取れないので注意して下さい。

また、"<appSettings>"は読み取り専用であることも注意が必要です。

構成ファイルに追記をすることはあまりありませんが、
万が一そのような状況になった場合は別の方法を利用して下さい。
(個人的には別途XMLファイルを作成する方がいいと思います。)


次に、設定要素を読み出すプログラムを記述します。

そのまえに、実は"App.config"を解析するためのライブラリは、
デフォルトでは参照されていません。

よって、"プロジェクト" > "参照の追加"を選択し、".NET"の中から、
"System.Configuration"を選択します。














では、ソースを書いてみます。今回は非常に簡単です。

static void Main(string[] args)
{
Console.WriteLine(System.Configuration.ConfigurationManager.AppSettings["Title"]);
Console.WriteLine(System.Configuration.ConfigurationManager.AppSettings["IPAddress"]);
Console.WriteLine(System.Configuration.ConfigurationManager.AppSettings["port"]);
System.Threading.Thread.Sleep(5000);
}

これだけです。

本来は、"App.config"自体をもっと細かく記述することで、
読み出すプログラムも複雑になるのですが、それはまた別の機会にします。


実行してみます。















インデクサに"App.config"の"add"タグ内で指定した"key"を記述すれば、
"value"の値が返ってきます。


また、プロジェクトの "bin" > "Debug"(または"Release")フォルダを開くと、
"AppConfigTest.exe.config"というファイル名で保存されていることが
確認できます。
















参照:
アプリケーション構成ファイル : msdn
「アプリケーション構成ファイル」を使用して設定ファイルを読み込む : DOBON.Net

2008年1月29日火曜日

XMLファイルを読み込む(C#)

プログラムを開発する上で、設定ファイルを作成することは非常に重要なことです。

ソースコードに直接埋め込む場合と違って、設定ファイルからパラメータを
読み込むことで、実行環境の違いを吸収したりできます。
(逆に言えば、設定ファイルを変更することで、プログラムが動かなくなったり
 します。)

Windowsでは古くから設定ファイルとして"INIファイル"が用いられてきました。
拡張子が".ini"となってるファイルです。

ところが、ネットワークの発展と共に、XMLが普及しだすと、
設定ファイルとしてもXMLファイルが広く用いられるようになりました。

そこで、今回はXMLファイルを読み込む方法を紹介します。


"コンソール アプリケーション"プロジェクトを作成します。
今回は"XMLParse"とします。

メニューの"プロジェクト"から"新しい項目の追加"を選択します。

"XML ファイル"を選択して、適当にファイル名をつけます。
例では、"server.xml"としておきます。

/*** server.xml ***/
<?xml version="1.0" encoding="utf-8" ?>
<server>
<address>
192.168.10.1
</address>
<port>
80
</port>
</server>

ネットワーク上に配置されたサーバの設定を保持していると想定して下さい。

次に、XMLファイルを解読するクラスを作成します。

先ほどと同様の手順で、クラスファイルを選択して下さい。
名前は"XmlParser.cs"としておきます。

"XmlParser"クラスは、最初にXMLファイルを読み込み、
その値を保持するユーティリティクラスとして作成したいと思います。

/*** XmlParser ***/
class XmlParser
{
private static IPAddress ip;
private static int port;

public static IPAddress Ip
{
get { return ip; }
}

public static int Port
{
get { return port; }
}
}

このクラスには静的フィールド変数として、IPアドレスを表す"ip"と、
ポート番号を表す"port"があります。

さらに、それらの値を返す静的プロパティが2つあります。

ユーティリティクラスのため、全てのメンバが静的です。

このクラスにXMLファイルを解析する処理を持たせるわけですが、
読み込むためのメソッドを作成して、それを呼び出せば簡単に済みます。

しかし、今回のようにファイルを一度だけ読み込む場合、
どこで一番最初にXMLファイルを解析させるかを考える必要があります。

もし、その順番が間違ってしまえば、不正な値を持つかもしれません。

そこで、今回のような場合は"静的コンストラクタ"を用います。

静的コンストラクタ
静的コンストラクタとは、クラスを初期化したい場合に利用します。
静的コンストラクタは以下の場合に呼び出されます。
  • 最初のインスタンスが生成される前

  • 静的メンバが参照される前
静的コンストラクタを用いれば、静的プロパティにアクセスするだけで、
何らかの処理を行わせることができます。


今回のケースにはぴったりですね。

静的コンストラクタは通常のコンストラクタと違い、
修飾子として"static"のみを付与します。

なお、"System.Xml"を"using"を持ちいて省略しておいて下さい。

/*** XmlParser.cs ***/
static XmlParser()
{
// 空白を無視するように設定する
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;

// リーダー
XmlReader reader = XmlReader.Create("../../server.xml", settings);

while (reader.Read())
{
// ノードの種類が要素かどうかを判定
if(reader.NodeType == XmlNodeType.Element)
{
if(reader.LocalName.Equals("address"))
{
Console.WriteLine(reader.ReadString() + "を読み込みました。");
// TryParseを利用してIPアドレスを読み込む
bool flag = IPAddress.TryParse(reader.ReadString(), out ip);
if (!flag)
ip = IPAddress.None;
}
else if (reader.LocalName.Equals("port"))
{
Console.WriteLine(reader.ReadString() + "を読み込みました。");
// TryParseを利用してポート番号を読み込む
bool flag = int.TryParse(reader.ReadString(), out port);
if (!flag)
port = int.MaxValue;
}
}
}
reader.Close();
}

"reader.LocalName"にはタグの要素名が入るため、
文字列比較を行うことで判定を行うことができます。


また、"TryParase"メソッドは、第一引数に与えられた文字列を、
指定したクラスに合うよう変換しつつ、それが正しいかを判定してくれます。
変換後の値は、第二引数で与えられたフィールドに格納されます。

out
"out"キーワードを用いると、"ref"と同様、参照渡しを行うことができます。

"ref"との違いは、"out"に渡すフィールドを明示的に初期化する必要が無い点です。
ただし、そのメソッド内で必ず"out"で参照されたフィールドに対して、
何らかの値を代入してやる必要があります。

"ref"が参照するために参照渡しを実現するのに対し、
"out"は出力するために参照渡しを実現しています。


"XmlReader"を作成する箇所で、XMLファイルへは相対パスで指定しています。

プロジェクト内の"server.xml"ですが、指定には"../"を2つ必要です。

これはプロジェクトの先頭を表すフォルダと、
デバッグ / ビルドを行う実行ファイルを持つフォルダが違うためです。

実際、プロジェクトが収まっているフォルダを開いてみて下さい。
(プロジェクトフォルダが無い場合は、メニューの"ファイル"から
 "全てを保存"を選択して下さい。)

"マイ ドキュメント" > "Visual Stuidio 2008" > "Project" > "XMLParse" >
"XMLParse"と選んだ先がプロジェクトのトップ階層です。

さらに、"bin" > "Debug"と選んだ先に、デバッグモードでの実行ファイルがあります。

ここが基点として、相対パスは認識されます。

よって、その2階層上にある"sever.xml"を指定する場合には、
"../../server.xml"と記述する必要があります。


出力用の処理を"Main"メソッド内に記述します。

/*** Program.cs ***/
static void Main(string[] args)
{
Console.WriteLine("ip = " + XmlParser.Ip + "を取得しました。");
Console.WriteLine("port = " + XmlParser.Port + "を取得しました。");
System.Threading.Thread.Sleep(5000);
}

実行します。















あれ?IPアドレスやポートがエラー値を示している。

と言うよりは、なぜか正しく設定してあるはずなのに、空白も読み込んでる?

・・・なぜ?


"XmlReader"の方で対処できないのであれば、読み込んだ後に対処しましょう。

全ての"reader.ReadString()"を以下のようにして下さい。

/*** XmlParser.cs ***/
string tmp = reader.ReadString().Trim();
reader.ReadString -> tmp
(宣言せずにメソッドに直接メソッドの結果を渡すと
 うまくいかない場合があります。)

"Trim"メソッドは、指定の文字列の先頭、及び末尾にある全ての空白を
削除してくれる素晴らしいメソッドです。

また、"TrimStart"、"TrimEnd"を用いれば、先頭、末尾のどちらかを
指定して削除することもできます。


再び、実行します。















今度は正しく動きました。
(原因が未だにわからないんですが、良しとして下さい^^;)


XMLは、今後インターネットの世界だけではなく、
様々なアプリケーションに使われていきます。

それも、今回のような設定ファイルだけではなく、
互換性の向上のために、メタ情報を記述した保存用ファイルや、
データ転送のための共通規格として利用されます。

XMLを用いることでプログラムに柔軟性と拡張性を持たせることができます。


参照:
out (C#) : msdn

2008年1月28日月曜日

IPアドレスやMACアドレスを取得する(C#) その2

前回の記事ではWMIインスタンスである"ManagementObject"を
利用しました。

しかし、クエリの発行やインデクサに対して名前を指定するなど、
全てのデバイス情報にアクセスできる分、プログラムが容易ではありませんでした。

そこで、今回はネットワークのみを扱うクラスを用いたいと思います。

と言っても、難しいものではないので早速ソースを見てみましょう。
(プロジェクトは前回同様"コンソール アプリケーション"で結構です。)


まず、名前空間の修飾省略定義を記述します。

ソースコード中に多く出てくる名前空間はここで省略して書いてしまいます。

/*** Program.cs ***/
using System.Net;
using System.Net.NetworkInformation;

名前空間の省略は、記述した空間内の、クラスにのみ適用されます。

つまり、"System.Net"名前空間の省略を宣言しても、"NetworkInformation"
名前空間の前が省略されるようになるわけではありません。

"using System.Net"のみの宣言を行った場合は、
例えば"System.Net.NetworkInformation.GetIsNetworkAvailable()"
のように、頭から全て記述しなければなりません。

/*** Program.cs ***/
static void Main(string[] args)
{
// ネットワークが利用できない場合は終了する
if (!NetworkInterface.GetIsNetworkAvailable())
return;

// 各ネットワークインタフェース毎に処理を行っていく
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
{
if (adapter.OperationalStatus.Equals(OperationalStatus.Up))
{
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (IPAddressInformation ipInfo in properties.UnicastAddresses)
{
IPAddress ip = ipInfo.Address;
if (!IPAddress.IsLoopback(ip))
{
Console.WriteLine("IP = " + ip);
Console.WriteLine("MAC = " + adapter.GetPhysicalAddress());
}
}
}
}
System.Threading.Thread.Sleep(5000);
}

実行します。















ネットワークの状態は前回と同じです。

今回は、"OpenVPN"ネットワークに関する情報が表示されていません。

これは、ソース中の"adapter.OperationalStatus"で
"OperationalStatus.Up"と「ネットワークインタフェースが
正常に稼働している」ものに限定したためです。

この方が、より正確に現在利用しているネットワークインタフェースの情報を
取得できます。

2008年1月25日金曜日

IPアドレスやMACアドレスを取得する(C#)

ネットワークを介したシステムを作る上で、マシンのIPアドレスやMACアドレスは
重要になってくる。

C#ではIPアドレスをはじめとして、様々なデバイス情報を取得することができます。

今回は、この取得をWindows Management Instrumentation (WMI)を
用いて行います。

WMIはWindows管理技術の中核を担っています。
また、ローカルのマシンだけでなく、リモートのマシンも管理することが可能です。

C#には、このWMIを表すクラス"ManagementObject"があります。

実際に使ってみてから説明していきます。


"コンソール アプリケーション"プロジェクトを作成して下さい。

"ManagementObject"は"System.ManagementObject"ライブラリ下に
あります。

メニューの[プロジェクト] → [.NET]タブ → "System.Management"を
クリックして、[OK]ボタンを押して下さい。

"using"ディレクティブを用いて、名前空間の修飾省略定義を行います。

/*** Program.cs ***/
using System.Management;
IPアドレスとMACアドレスを標準出力するソースを記述します。

/*** Program.cs ***/
static void Main(string[] args)
{
string query = "SELECT * FROM Win32_NetworkAdapterConfiguration";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

ManagementObjectCollection queryCollection = searcher.Get();

foreach (ManagementObject mo in queryCollection)
{
if ((bool)mo["IPEnabled"])
{
Console.WriteLine(mo["Caption"]);
foreach (string ip in (string[])mo["IPAddress"])
{
Console.WriteLine("IP = " + ip);
}
Console.WriteLine("MAC = " + mo["MacAddress"].ToString() + "\n");
}
}

System.Threading.Thread.Sleep(5000);
}

実行します。















実際に"ネットワーク接続"画面を開いて確認してみます。














接続状態が"無効"もしくは"切断"の接続は、コンソールに表示されていません。

これは、"IPEnabled"がfalseになっているためです。

また、「ネットワークケーブルが接続されていません」と表示されている接続は、
IPアドレスが"0.0.0.0"と表示されています。

ちなみに、このネットワークは"OpenVPN"を使った仮想ネットワークです。

他のネットワークアダプタの状態や振る舞いを取得する場合は、
msdn : Win32_NetworkAdapterConfiguration Class」を
参照して下さい。


このようにWMIを用いることで、マシンの情報を容易に取得することができます。


参照:
Windows Management Instrumentation の秘密 : Microsoft TechNet
ManagementObject クラス (System.Management) : msdn
how do i get the MAC address in c# ? : C# Friends

画面をキャプチャする(C#) その2

前回の最後で、
「何らかの方法でアクティブなウィンドウの左上隅の座標とウィンドウの大きさを
取得すれば、"CopyFromScreen"メソッドが利用できる。」
と話しましたが、もちろんできます。


前回のソースをそのまま改変していきます。
(新たにプロジェクトを作成する場合は、"button1"をダブルクリックする所まで
 進めて下さい。)

まずは、長方形を表す構造体を宣言します。
("using"ディレクティブを用いて、"System.Runtime.InteropServices"名前
 空間の修飾省略定義を行って下さい。)

/*** Form1.cs ***/
public partial class Form1 : Form
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
(省略)
}

構造体"RECT"は長方形の左上隅と右下隅とで、長方形の形を定義します。

左上隅に (x, y) = (RECT.left, RECT.top)
右上隅に (x, y) = (RECT.right, RECT.bottom)
が入ります。

続いて、「アクティブなウィンドウを取得する」メソッドと、
「ウィンドウの長方形座標を取得する」メソッドを"user32.dll"より呼び出します。

// 現在ユーザーが作業しているウィンドのハンドルを返します。
[DllImport("user32")]
private static extern IntPtr GetForegroundWindow();

// 指定されたウィンドウ左上端と右下端の座標をスクリーン座標で取得します。
[DllImport("user32")]
private static extern int GetWindowRect(IntPtr hWnd, ref RECT lpRect);

"GetWindowRect"の第一引数には、もちろん座標を取得したいウィンドウのハンドルが入ります。

第二引数に渡した変数に対して、取得された座標が代入されます。

このとき、"ref"がついてることがポイントです。

値渡しと参照渡し
引数の渡し方は2種類あります。1つは"値渡し"、もう1つは"参照渡し"です。

値渡しはメソッドを呼び出すときに、値のコピーを渡す方式です。
メソッド内で、引数の値に対して操作を行った場合でも、元の値には影響しません

参照渡しはメソッドを呼び出すときに、値の参照を渡す方式です。
メソッド内で、引数の値に対して操作を行うと、元の値にも影響します

C#ではメソッドの引数は基本的には値渡しです。
参照渡しにする時は、開発者が"ref"を用いて明示的に示さなければなりません。
この"ref"は引数の宣言の時にも、渡す時にも必要になります。


実装は以下の通りです。

/*** Form1.cs ***/
private void button1_Click(object sender, EventArgs e)
{
// アクティブなウィンドウのデバイスコンテキストを取得
IntPtr hWnd = GetForegroundWindow();

// ウィンドウの大きさを取得
RECT wRect = new RECT();
GetWindowRect(hWnd, ref wRect);

int width = wRect.right - wRect.left;
int height = wRect.bottom - wRect.top;

Bitmap bmp = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(new Point(wRect.left, wRect.top), new Point(0, 0), bmp.Size);
}

this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
this.pictureBox1.Image = bmp;
}

実行します。




















参照:
Capture a Screen Shot - Developer Fusion

2008年1月24日木曜日

画面をキャプチャする(C#)

デスクトップ画面をキャプチャする。

キーボードなら[PrtSc]キーを押せば画面全体はキャプチャできる。
[Alt]キーと一緒に押せば、アクティブなウィンドウのみをキャプチャできる。

もっと細かくキャプチャしたいとなると・・・プログラムするしかない。
(フリーソフトを使えば?ってツッコミは無しで^^;)

.NET Frameworkには画面のキャプチャを容易にしてくれる、
"CopyFromScreen"というメソッドがある。


早速、使ってみる。

まずは、新規で"Windows フォーム アプリケーション"プロジェクトを作成する。

表示されたフォームに、"Button"と"PictureBox"を追加する。
この時、"pictureBox1"の"Size"を「400, 400」、"BorderStyle"を"FixedSingle"にしておく。












"button1"をダブルクリックして、メソッドを作成。

/*** Form1.cs ***/
private void button1_Click(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(200, 200);
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(new Point(100, 100), new Point(0, 0), bmp.Size);
}

this.pictureBox1.SizeMode = PictureBoxSizeMode.Normal;
this.pictureBox1.Image = bmp;
}

実行する。

"button1"をクリックすると、画面の左上の方がキャプチャされ、
"pictureBox1"に表示される。
















"pictureBox1"の左上1/4の所に表示された。

CopyFromScreen
通常、第一引数には、コピーの画像の左上隅の座標を与える。
第二引数には、コピーの画像の左上隅の座標を与える。
第三引数には、コピーするサイズを与える。
第一、二引数に与えた座標を開始点として、第三引数に与えたサイズだけコピー
される。


既にお分かりの人も多いと思うが、ソース内の

this.pictureBox1.SizeMode = PictureBoxSizeMode.Normal;
で、"pictureBox1"内での表示方法を設定している。

表示方法は全部で5つある。
AutoSize
PictureBoxのサイズが、格納しているイメージと同じ大きさになる。

CenterImage
PictureBoxがイメージより大きい場合は、イメージは中央に表示される。
イメージがPictureBoxより大きい場合は、イメージはPictureBoxの中央に
配置され、外にはみ出した分はクリップされる。

Normal
PictureBoxの左上隅に配置される。イメージがPictureBoxより大きい場合は、イメージはクリップされる。

StretchImage
イメージのサイズが、PictureBoxのサイズに合うように調整される。

Zoom
イメージのサイズは、サイズ比率を維持したままで拡大または縮小される。
と、言うわけで、全部試してみる。

AutoSize


















CenterImage


















StretchImage


















Zoom


















残念ながら、上の画像からでは"StretchImage"と"Zoom"の違いがわからない
が、"pictureBox1"の"Size"を「300, 400」などにしてみるとわかると思う。
(作った後に気づいてしまった、申し訳ないです。)


C#を使って、簡単に画面をキャプチャすることができた。

この方法だと、座標指定を指定するため、ほとんどの場合に利用できそうだ。

例えば、前述の「アクティブなウィンドウのみをキャプチャする」の場合、
何らかの方法でアクティブなウィンドウの左上隅の座標とウィンドウの大きさを
取得すれば、"CopyFromScreen"メソッドが利用できる。


参照:
画面をキャプチャする: .NET Tips (VB.NET, C#, Visual Studio...)