ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2012年12月12日水曜日

[C#] WinAPI を呼んでみる

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


以前の投稿内では普通に使っているのですが、特に記載していないようだったので別途投稿してみようと思います。
C# は便利ですが、細かいことをやろうとするとどうしても API に頼ることになります。
では API ってどうやって呼びだすのな部分を今回はネタにしてみました。

using System.Runtime.InteropServices;   // DllImportなど、API呼び出し
namespace CSWinFormTest2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // API を利用するのに必要な宣言(インポートライブラリの宣言)
        [DllImport("user32.dll")]
        // API を利用するのに必要な宣言(インポートライブラリの宣言、オプション指定はこんな感じ)
//        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        // API を利用するのに必要な宣言(関数定義の宣言)
        // static extern は必須。アクセス演算子は任意
        private static extern UInt32 MessageBox(IntPtr hWnd, String lpText, String lpCaption, UInt32 uType);

        // MessageBox() Flags  winuser.h より(一部抜粋)
        private const UInt32 MB_OK                   = 0x00000000;
        private const UInt32 MB_ICONASTERISK         = 0x00000040;
        private const UInt32 MB_ICONINFORMATION      = MB_ICONASTERISK;

        private void button1_Click(object sender, EventArgs e)
        {
            // API の利用方法を確認
            MessageBox(this.Handle, "メッセージ本文", "タイトル部", MB_OK | MB_ICONINFORMATION);

            // 余談:C# では MessageBox.Show があるので、本来上記のような手間は必要はない
            System.Windows.Forms.MessageBox.Show("メッセージ本文", "タイトル部",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}
短いので全部まとめて書いています。
まぁコメント見ていただければわかるかと思います。
DllImport で使用するライブラリを指定して関数定義を宣言。
あとは普通に利用するだけです。

ちなみに複数種類の API を利用したい場合は、その関数毎に定義を各必要があります。
オーバーロードするような場合でも DllImport から書く必要があります。
これがめんどくさいので結局 C++/CLI で作るかぁ~?ってなっちゃいます。
まぁ DllImport から関数定義までが一つの宣言だと思ってあきらめるしかありません。

全体的に見れば C# の方が生産性いいはずなので、できるだけ C# でやっていこうかな、っと。

--- 2012/12/27追記 ----------
よく見に行くサイトをいちいち検索するのがめんどくさいので、こちらに転記させていただいた。
毎度お世話になっております(@IT .NET TIPS Win32 APIやDLL関数を呼び出すには?)
APIでの型名
(括弧内は対応するC言語の型)
対応するC#の型
(括弧内は.NET Frameworkでの型名)
HANDLE (void *)System.IntPtr
BYTE (unsigned char)byte (System.Byte)
SHORT (short)short (System.Int16)
WORD (unsigned short)ushort (System.UInt16)
INT (int)
LONG (long)
int (System.Int32)
UINT (unsigned int)
DWORD, ULONG (unsigned long)
uint (System.UInt32)
BOOL (long)bool (System.Boolean)
CHAR (char)char (System.Char)
LPSTR (char *)
LPWSTR (wchar_t *)
System.Text.StringBuilder
LPCSTR (const char *)
LPCWSTR (const wchar_t *)
string (System.String)
FLOAT (float)float (System.Single)
DOUBLE (double)double (System.Double)
Win32 APIでの型名と対応するC#の型 WindowsのDLL(Win32 API)と.NET Frameworkとでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。なお、BOOL型の実体はLONG型と同じなので、boolの代わりにintを指定することも可能である。

2012年11月29日木曜日

[C#] 画像描画と背景透過

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


画像描画と銘打っていますが、実のところ先行して投稿した下記2記事の続きと言ってもいいかもしれません。

[C#] ListView のカラム部分に画像表示

[C#] ListView のカラム部分に画像表示(其の2:HDITEM版)

其の2の中で、実は背景が透過していないため色の指定次第で画像背景が目立ってしまうと問題定義しておいたことに対する対策方法となります。
タイトルを引き続き利用しようかとも考えたのですが、基本的に内容が異なるために別タイトルとしました。

前置きはこのくらいにして本題に入りましょう。
今回は前提無しです。
でいきなりコードとなります(説明はのちほど)
// OdrIdx:表示する数字
// Odr   :三角形の向き
private Bitmap CreateSortOrderBmp(int OdrIdx, int Odr)
{
    // やっていること
    // 背景塗りつぶし → ソート順により三角形作成 → フォント描画(上下中央右寄せ)
    Bitmap img = new Bitmap(16, 16);
    Graphics g = Graphics.FromImage(img);

    // 背景を塗りつぶし(透過色)
    g.FillRectangle(new SolidBrush(this.TransparencyKey), g.VisibleClipBounds); // ①

    // 三角描画
    Point[] pnt = new Point[4];
    switch (Odr)
    {
        case 1: // 上三角
            pnt = new Point[4] { new Point(4, 2), new Point(0, 10), new Point(8, 10), new Point(4, 2) };
            break;
        case 2: // 下三角
            pnt = new Point[4] { new Point(4, 13), new Point(0, 5), new Point(8, 5), new Point(4, 13) };
            break;
        case 0: // 非表示化
        default:
            // 無印化するので描画処理をやらずに終了
            g.Dispose();
            return img;
    }
    g.FillPolygon(Brushes.Gray, pnt);   // 先に塗りつぶし
    g.DrawPolygon(Pens.Black, pnt);     // 次に、線描画

    // 文字を描画
    Font fnt = new Font("MS ゴシック", 8, FontStyle.Bold);
    StringFormat sfmt = new StringFormat();

    sfmt.Alignment = StringAlignment.Far;           // 横の表示位置
    sfmt.LineAlignment = StringAlignment.Center;    // 縦の表示位置

    g.DrawString(OdrIdx.ToString(), fnt, Brushes.Red, new RectangleF(new PointF(0, 0), new SizeF(16, 16)), sfmt);

    // 描画作業が完了したのでグラフィッククラスを解放
    g.Dispose();

    // 作成した画像を戻り値に
    return img;
}
御覧の通り、今回はメソッドとなります。
画像描画を書いたことある人には、なんてことない内容となっています。
やりたかったことは、リストのカラムをクリックすることでソートするので、それの昇順と降順、選択順番を明記するために画像を表示しようと考えていました。
そんなこんなの描画部分についてはソースを見ていただくとして、今回メインとなる背景透過の部分について。
ズバリ①の部分です。
コメントにも書いてあるのでわかるかと思いますが対象画像の背景をフォームで透過色と指定されている色で塗りつぶしています。
こうすることでカラム部分に表示しても昇降を示す三角と数字以外は表示されないことになります。
あとはそうして作成した Bitmap を ImageList の該当する部分に登録してあげれば対策完了です。
当然ですが ImageList の項目数 = カラム数と言うことになるので、他にもアイコンやらを使う予定がある場合はあらかじめ ImageList の配置仕様を検討しておく必要があると思います。

あと当たり前の話ですが、事前に ImageList に画像を登録して、それを利用する場合は登録する画像の背景を this.TransparencyKey で指定する色と同じにしておく必要があるので注意です。

[C#] ListView のカラム部分に画像表示(其の2:HDITEM版)

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


先日、ほぼ同様のタイトルでカラムに画像を表示する方法を投稿しました。
[C#] ListView のカラム部分に画像表示
この時は ImageList を利用して表示する方法となっていましたが、今回は別の方法で画像を表示してみたいと思います。

表示パターンが確定していて動作中に変更が必要無い場合は、画像ファイルを準備した状態で前回の方法を利用してもらうのが一番だと思います。
しかし状況により表示内容を変更したい(動的に描画する)場合は、少々不便です。
確かに該当する ImageList の内容を書き換えてしまえば前回の方法で表示することは可能ですが、それだと ImageList を利用して準備している意味がありません。

そこで今回は動的に描画した画像をカラムに表示させてみたいと思います。
(妙に説明調になっているのは、自分が忘れそうだからです)
Header Control (Windows)


まずは宣言。
// 前回も宣言したもの
using System.Runtime.InteropServices;

public const UInt32 LVM_GETHEADER = 0x101F;
public const int HDF_BITMAP_ON_RIGHT = 0x1000;

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, UInt32 lParam);

// 以下前回より追加宣言したもの
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct HDITEM
{
 public uint mask;
 public int cxy;
 public IntPtr pszText;
 public IntPtr hbm;
 public int cchTextMax;
 public int fmt;
 public int lParam;
 public int iImage;
 public int iOrder;
 public uint type;
 public IntPtr pvFilter;
 public uint state;
}

public const int HDF_BITMAP = 0x2000;
public const uint HDI_FORMAT = 0x0004;
public const uint HDI_BITMAP = 0x0010;
public const uint HDM_FIRST = 0x1200;
public const uint HDM_GETITEM = HDM_FIRST + 3;
public const uint HDM_SETITEM = HDM_FIRST + 4;

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, ref HDITEM lParam);
前回と似たような感じですが、新規宣言したものがあります。
相変わらず情報源は CommCtrl.h となっています。

次はコード本体です。
今回もボタン押下イベントに書いています。
IntPtr colHeader;

// リストビューのカラム部分のハンドルを取得
colHeader = SendMessage(listView1.Handle, LVM_GETHEADER, (UInt32)0, (UInt32)0);

// 設定対象のカラム情報を取得
HDITEM hdItem = new HDITEM();
hdItem.mask = HDI_FORMAT;
SendMessage(colHeader, HDM_GETITEM, 0, ref hdItem);

// 設定対象のカラム情報を変更、反映
hdItem.mask = HDI_FORMAT | HDI_BITMAP;
hdItem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
Bitmap bmp = CreateSortOrderBmp(1, 1);
hdItem.hbm = bmp.GetHbitmap(SystemColors.Window);
SendMessage(colHeader, HDM_SETITEM, 0, ref hdItem);

/* 参考:Header_GetItem と Header_SetItem の定義内容
#define Header_GetItem(hwndHD, i, phdi) \
    (BOOL)SNDMSG((hwndHD), HDM_GETITEM, (WPARAM)(int)(i), (LPARAM)(HD_ITEM *)(phdi))
            
#define Header_SetItem(hwndHD, i, phdi) \
    (BOOL)SNDMSG((hwndHD), HDM_SETITEM, (WPARAM)(int)(i), (LPARAM)(const HD_ITEM *)(phdi))
*/
CreateSortOrderBmp(1, 1)の中でBMP画像を作成しています。
利用される場合は、その辺を適当に変更していただければいいかと。

このコードでリストのカラムテキストの右側に画像を表示することはできるのですが、少し後に問題があることに気付きました。
ほとんど保護色になっていて気付かなかったのですが、背景色が透過していないため、画面の色指定次第で画像部分が目立って表示されることがあります。
それなりに考えた背景色にしておけば気づかれない可能性はありますが、完璧を求めるならばこの方法では駄目だということになります。
その辺の対策は ImageList を利用した前回のパターンで解消できます。

じゃ何でこの記事投稿したんだ?って話になるのですが、そこはそれメモです。
せっかく調べたのに消すのはもったいないですからね。

上記理由から、特に理由がないようでしたら前回か次回(予定)の投稿を参照してください。

2012年11月22日木曜日

[C#] 文字列まわりの基本

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


2016/03/18 ⇒ 追記

今更ながらに文字列まわりの基本をいくつか確認したのでメモしておく。

●シングルクォーテーション、ダブルクォーテーション
シングルが文字でダブルが文字列。
・・・ホント今更。
これだけだと、ちょっとおバカさんなので他にも。

文字コード指定は\uXXXXで指定。
文字列の場合「逐語的文字列リテラル」というのがあってエスケープシーケンスを無視させる書式がある。
シングルやダブルの前に @ をつければいい。
string str = @"aaa\nbbb\nccc";
Console.Write(str);
@ を使わない場合
aaa
bbb
ccc
と表示されるが @ があると
aaa\nbbb\nccc
と表示される。
ちなみに複数行にわたっての記述に対応していて
string str = @"この下の行も文字列扱いになる。
string str = ""ここも文字列扱い"";
ここまで文字列";
Console.WriteLine(str);
これを実行すると 3 行にわたり文書が表示される。
対象になる行の改行、スペースもそのまま表示されるのでコード上ではインデントで空白を入れたくてもいれられないので注意(入れちゃうとその分しっかりと空白が表示されます)


●文字列が null かどうか(情報参照元)
いろいろ手段がある。
string str = "ほげほげ";
if(str == "") Console.write("空白です");
if(str == string.Enpry) Console.write("空白です");
if(str.Equals("")) Console.write("空白です");
if(str.Equals(string.Enpry)) Console.write("空白です");
if(string.IsNullOrEmpty(str)) Console.write("空白です");
if(str.Length == 0 ) Console.write("空白です");
どれで判断しても結果として空白かどうかの判断ができる。
対象が空白の場合に、ただ空白か否かの判定をする場合は str.Length == 0, String.Equals, == "" の順が早いようです(詳しくは情報参照元やMSDNにて)
ただ普段使いとしては IsNullOrEmpty がいいようです(MS的に推奨されているとのこと)

余談だが調べている時にnull合体演算子というものがあるのを知った。
string str = null;
str = str ?? "nullです";
?? の左辺が null でないなら左辺値を null であるなら右辺値を返すらしい(詳しくはMSDNを参照のこと)
使い方次第ではすっきりしたコードを書くことができそう。


●デバッグ時のログ出力
デバッグ中とかにブレークするほどではないけど値を確認したい場合に出力画面に結果を表示する方法。
System.Diagnostics.Trace.WriteLine("");


●デバッグ時の時間計測
動作時間を計測したい場合はStopwatchを利用すると便利。
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
sw.Stop();
sw.Restart();
sw.Reset();


以上、確認事項メモでした。

2012年11月20日火曜日

[C#] ListView のカラム部分に画像表示

更新履歴:
2012/11/29:宣言部分で DLL の宣言が抜けていたので追記。

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


タイトル通り ListView のカラム部分に画像を表示する方法です。
ただしカラム文字列の左に表示するだけなら SmallImageList にImageList を登録して Columns.ImageIndex を設定するだけで表示できるので、取り立てて記事にするほどでもありません。
←これは簡単

今回記事にした理由は、文字列の右側に画像を表示しようとしているためです。
たとえば列でソートする機能を作成した時や、何らかの印を表示したい時などに便利かと。
ではさっそく。

環境は Form 上に Button, ListView, ImageList をそれぞれ一つずつ配置しています。
ImageList には適当に画像を登録しておいてください。
カラムに登録するサイズなのでフォントサイズにもよりますが、縦16 x 横8 程度が見栄えいいかと。
とりあえず、この投稿では以下のような感じで登録しています。

参照したサイトは以下の通り。
●PRB: Cannot Assign Images to a ColumnHeader Control in a Windows Forms ListView Control in Visual C# .NET
●Common Control Versions (Windows)
●LVCOLUMN structure (Windows)

おまけの類似項目
●HDITEM structure (Windows)

とりあえず宣言部分です
using System.Runtime.InteropServices;

public const UInt32 LVM_GETHEADER = 4127;
public const UInt32 HDM_SETIMAGELIST = 4616;
public const UInt32 LVM_SETCOLUMN = 4122;
public const uint LVCF_FMT = 1;
public const uint LVCF_IMAGE = 16;
public const int LVCFMT_IMAGE = 2048;

// ListView のカラム情報設定用構造体
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
public struct LVCOLUMN
{
    public uint mask;
    public int fmt;
    public int cx;
    public IntPtr pszText;
    public int cchTextMax;
    public int iSubItem;
    public int iImage;
    public int iOrder;
}

// 以下 fmt で利用する定義で参照元に無かったやつ( CommCtrl.h より定義参照)
// ComCtl32.dll ver4.70 以降でのみ利用可能らしい
// ただし 4.70 というのは Internet Explorer 3.0 って事なので今時では制約にならない
public const int HDF_IMAGE = 0x0800;      // Same as LVCFMT_IMAGE
public const int HDF_BITMAP_ON_RIGHT = 0x1000;      // Same as LVCFMT_BITMAP_ON_RIGHT
// おまけ:参考サイトのコメント欄にて発見。隠し?項目らしいです(CommCtrl.h には定義あり)
public const int HDF_SORTUP = 0x0400;      // Windows7 Explorer のソート順表示(上)
public const int HDF_SORTDOWN = 0x0200;      // Windows7 Explorer のソート順表示(下)

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, UInt32 lParam);

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, UInt32 wParam, ref LVCOLUMN lParam);
覚書みたいなものはコメントにゴチャゴチャ書いているので省略します。

次はボタン押下イベントです。
IntPtr colHeader;
int i;

// リストビューのカラム部分のハンドルを取得
colHeader = SendMessage(listView1.Handle, LVM_GETHEADER, (UInt32)0, (UInt32)0);

// カラム部のハンドルにイメージリストを割り当て
SendMessage(colHeader, HDM_SETIMAGELIST, (UInt32)0, (UInt32)imageList1.Handle);

// カラムの設定
for (i = 0; i < listView1.Columns.Count; i++)
{
    // Use the LVM_SETCOLUMN message to set the column's image index. 
    LVCOLUMN col;
    //  col.mask: include LVCF_FMT | LVCF_IMAGE 
    col.mask = LVCF_FMT | LVCF_IMAGE;

    // LVCFMT_IMAGE 
    col.fmt = LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT;
//    col.fmt = LVCFMT_IMAGE | HDF_SORTUP;

    // 表示するイメージの番号(仮に固定値で ImageList の 1 番目をしていしている)
    col.iImage = 1;

    //  Initialize the rest to zero.
    col.pszText = (IntPtr)0;
    col.cchTextMax = 0;
    col.cx = 0;
    col.iSubItem = 0;
    col.iOrder = 0;

    // Send the LVM_SETCOLUMN message.
    // The column that we are assigning the image to is defined in the third parameter.
    SendMessage(listView1.Handle, LVM_SETCOLUMN, (UInt32)i, ref col);
}
まぁ何というか、ほぼ MS サイトのコードまる写しです、はい。
col.fmt 部分の設定がキモになっています。
上記通りにやればリンクしてある ImageList の 1 番目の画像が表示されます。
コメントになっているように記述すると Windows7 の explorer の詳細表示部分のソート表記と同じにできます。
左:LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT
右:LVCFMT_IMAGE | HDF_SORTUP

何にしろ画像は表示できたので目的達成です。

2012年10月15日月曜日

[C#] 非同期処理

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


ファイルの比較処理をテストで作ったのだが、処理が長くなると UI が固まるため、対策として非同期処理を導入することにしました。
今回は web 上で公開されているサンプルなども充実していたので苦労しませんでした。
っと言いたいのですが VisualStudio (っと言うか .NET のバージョンですかね)の違いなどにより使える機能が限定されているため最新の解説ページの情報が役に立たず。
何だかんだと調べてみた結果 VS2008 + C# の組み合わせでは最適と思われる方法で作ってみました。
環境制限などで必要に迫られているようでしたら参考にどうぞ。

今回のテストフォーム上は少しコントロール多めです。
TextBox x 2
Label x 2
Button x 1
ListBox x 1
TextBox に比較対象となるファイルのフルパスを入力しておきます(実行時に入力してもいいですがめんどくさい)
そしてボタンを押すと処理開始となります。
処理結果は ListBox に出力、途中経過は Label に出力します。

とりあえず using 宣言。
using System.IO;            // ファイル操作で使用
using System.Threading;     // スレッド系で使用

次にデリゲートの宣言。
スレッド対象のメソッド(って言う言い方が正しいのか?)を宣言します。
ちなみに宣言位置はクラス内です。
// 非同期処理に利用するデリゲートの定義宣言
private delegate int checkDlgt(string file1, string file2);

// 非同期側スレッド内からフォーム上のコントロールを操作するために利用するデリゲートの定義宣言
private delegate void addStrLog3dlgt(int tgt, string str);

そして非同期側からフォームコントロールを操作するためのメソッド
フォームコントロールは別スレッドからは操作できないため、デリゲート経由で操作することになります。
今回は途中経過を表示したいので、作成しました。
// 非同期側(子プロセス)から listBox3 に追記するためのメソッド
private void addStrLog3(int tgt, string str)
{
    switch (tgt)
    {
        case 1:
            label1.Text = str;      // 処理位置
            break;
        case 2:
            label2.Text = str;      // ファイルサイズ
            break;
        case 3:
        default:
            listBox3.Items.Add(str);
            break;
    }
}

やっとサブスレッドの本体です。
この中でファイルの比較を行います。
// 非同期処理の本体
private int binaryChecking(string file1, string file2)
{
    // もろもろメッセージ出力のための処理
    if (!this.InvokeRequired)
        return 0;

    addStrLog3dlgt asl3 = new addStrLog3dlgt(addStrLog3);

    if (File.Exists(file1) && File.Exists(file2))
    {
        // 指定された2ファイルをバイナリ形式で開く
        byte[] f1 = File.ReadAllBytes(file1);
        byte[] f2 = File.ReadAllBytes(file2);
        // 進捗率表示用
        int iProgress = f1.Count() / 10;

        // 比較元のファイルサイズを表示
        this.Invoke(asl3, 2, f1.Count().ToString());
        this.Invoke(asl3, 3, "start   :" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
        for (int i = 0; f1.Count() > i; i++)
        {
            if (!(f1[i] == f2[i]))
                break;

            // 進捗状況表示
            if (i % iProgress == 0)
                this.Invoke(asl3, 3, i.ToString("D8") + ":" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());

            // 処理状況表示(非常に処理速度に影響を与えるので下記値を参考に変更)
            //   1=トイレ休憩可能、10=遅い、1000=まぁ待てる、10000=実用範囲
            if( i % 1000 == 0)
                this.Invoke(asl3, 1, i.ToString());
        }
    }
    else
    {
        return 0;
    }

    return 1;
}

そしてコールバックメソッドです。
呼び出したのはいいですが、非同期なのでスレッドいつ終わるかわかりません。
メインスレッドでサブスレッドの終了監視を行うことも可能ですが、それでは結局 UI が固まり本末転倒。
そんなわけで、終わったら自分から申告してもらう必要があります。
そのために利用します。
// コールバックメソッド
private void CallBackMethod(IAsyncResult iAR)
{
    addStrLog3dlgt asl3 = new addStrLog3dlgt(addStrLog3);
    checkDlgt cDlgt = (checkDlgt)iAR.AsyncState;

    string strWork;
    int ret = cDlgt.EndInvoke(iAR);

    if (ret == 1)
        strWork = "finish  :";
    else
        strWork = "abort   :";
    
    this.Invoke(asl3, 3, strWork + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
}

最後にイベント起動部分。
ボタン押下イベントです。
// バイナリファイルの比較
private void button4_Click(object sender, EventArgs e)
{
    // 非同期で動作させるメソッドを指定
    checkDlgt cDlgt = new checkDlgt(binaryChecking);

    // 非同期処理の起動(先方で利用する引数値もここで指定)
    IAsyncResult iAR = cDlgt.BeginInvoke(textBox1.Text, textBox2.Text, new AsyncCallback(CallBackMethod), cDlgt);
}

本当は別途テストプログラムを作成するつもりだったのですが、なんとなく勢いで考えていた処理をそのまま作ってしまいました。
サブスレッドの処理状況表示 if 文の数値を小さくすると格段に処理が遅くなるため、メインスレッド(画面側)の UI が死んでいる様、もしくは死なない様の比較がしやすいかと思います。

余談ですが VS2012 など投稿時点での最新バージョンでは atwait などの処理を利用することで、もっと簡単に高機能な処理を実現できるようです。
バージョン制約が無い方は、その辺も調べられてみてはどうでしょうか。

今回はこれまで。

2012年10月10日水曜日

[C#] App.config の利用方法

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


C#で開発を行っていてアプリの固有情報を保存するにあたり、何の制約もないのに ini ファイルを使うことも無かろう・・・って事で C# スタンダードらしい「アプリケーション構成ファイル( App.config )」を利用することにした。
しかし探し方が下手なのか、もっとも単純な使用例(書いて読む)が見当たらなかったので、自分で書いておく。

コードを書く前に
事前準備として system.configuration.dll への参照を追加する必要がある。
ソリューション エクスプローラで右クリック →「参照の追加(R)...」で表示される「参照の追加」ウィンドウが開く。
.NET タブの中から「System.Configuration」を選択して「 OK 」を押下。

お試し環境は、いつもの Button x 2 と ListBox があるフォーム画面です。

// 下記宣言を追加
using System.Configuration;

// app.config の appSettings に情報を設定
private void button1_Click(object sender, EventArgs e)
{
    // System.Configuration.ConfigurationManager を利用するにあたり
    // System.Configuration.dll への参照を追加する必要がある
    // (ソリューション エクスプローラで右クリック → 参照の追加)

    // Configuration オブジェクトを開く(対象ユーザーは「全てのユーザー」を指定)
    Configuration conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

    // 登録情報の作成
    // キー名:KeyName の後ろに通番を付与した書式
    // データ:日付と時間を登録する
    string keyStr = "KeyName" + ConfigurationManager.AppSettings.Count.ToString();
    string valueStr = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();

    // 情報の登録 → 保存 → 情報更新(次回アクセス時)
    conf.AppSettings.Settings.Add(keyStr, valueStr);
    conf.Save(ConfigurationSaveMode.Modified);
    ConfigurationManager.RefreshSection("appSettings");
}

// app.config の appSettings から情報を取得
private void button2_Click(object sender, EventArgs e)
{
    listBox1.Items.Add("--- start reading ----------");

    // App.Config からの読み込み
    // AllKeys は全てのキー名を取得してくれる
    foreach (string keyStr in ConfigurationManager.AppSettings.AllKeys)
        // ピンポイントで取得するだけなら下記1行だけで ok (取得先のキー名がわかっている時など)
        listBox1.Items.Add(ConfigurationManager.AppSettings[keyStr]);

    listBox1.Items.Add("--- end reading ----------");
    listBox1.SelectedIndex = listBox1.Items.Count - 1;
}

Button1 を押すことでデータを登録します。
Button2 を押すことで appSettings よりデータを取得します。
取得したデータは ListBox に追加されます。

データが膨大になった場合のセクション分けもできるらしいのですが、時間なので今回はこれまで。