2007年12月27日木曜日

[他]GUIのデザイン

この前TVを見ていると、医療事故のニュースをやっていました。
何でもその事件は、患者の疾患に対してありえない薬を投与したため、起こった事件のようでした。
で、事故の原因を探っていくと、

  • 医師がシステムに誤ってありえない薬を入力した
  • その薬が看護婦によってそのまま患者に投与された

ということでした。で、専門家の人?がTVで「そんなありえないような組み合わせはシステムでチェックして事故を防ぐんだよ!!のようなことを言っていました。

確かに一理あります。以前あった株取引の単価と数量の入れ間違えによる事件も同じようなことが原因で起こっています。しかし、果たしてシステム対応だけでこういった問題は解決するといえるのでしょうか?この論理であれば、システムを導入するまでは事故が起こらなかったことになります。

仮にこのようなチェックを実施するようにすると、以下のような現象が起こります。

  • 本当に間違ったような数値が入れられなくなる
  • エラーメッセージ表示により操作性が悪くなる(と思われる)
  • エラーに慣れてしまい、無意識にOKボタンを押下するようになる
  • (少なからず)開発コストが上昇する

結局最後は、人間がチェックするそれしかないのだと私は考えます。もちろん人に優しいシステム構築というのはソフト開発する上では永遠のテーマだと思いますが。。。

2007年12月21日金曜日

[他]文字列の解析について(by @it)

時々以下のようなデータをMapに復元するという処理を作ることがあります。

「key=value,aaa=bbb,ccc=123」

コレをMapに復元し、キーに「aaa」を指定すると「bbb」が取得できる。まぁちょっとしたシリアライズですね。
そんな話題が@itの掲示板(java)に出ていました。
この手の処理を作るときに、方法としては以下の2種類を思いつきます。

  1. 正規表現でスマートに処理
  2. ガチでパーサを実装する
  3. 1と2のいいところどり

こういうときに私の場合「2.」で実装しちゃいます。理由は以下の2点。

  1. メンテできないと言い出す人がいる(正規表現を理解できない人結構いる)
  2. 仕様変更時に正規表現が複雑になる場合がある、または正規表現では困難

ありがちな問題として、「=」や「,」をデータに含めたい。その対策として、値を「"値"」のようにしたい。などと言れることがあるからです。

とはいえ、いまどきの.NETであればXMLSerializerでこの手の問題に発展することはないんですけどね。

2007年12月20日木曜日

[.NET]MailSlot(その2)

昨日のMailSlotの受信について書きましたが、今日は送信側。コード例は以下のとおりです。
昨日のとあわせてクラス化してみました。
(実は半分くらいMOFさんからのぱくり品)

public class MailSlot
{
    #region Win32 API宣言

    [DllImport("kernel32.dll"CharSet = CharSet.Auto)]
    private static extern SafeFileHandle CreateFile(
        string lpFileName// ファイル名 
        DesiredAccess dwDesiredAccess// アクセスモード 
        ShareMode dwShareMode// 共有モード 
        int lpSecurityAttributes// セキュリティ記述子 
        CreationDisposition dwCreationDisposition// 作成方法 
        FlagsAndAttributes dwFlagsAndAttributes// ファイル属性 
        IntPtr hTemplateFile // テンプレートファイルのハンドル 
        );

    [DllImport("kernel32.dll"CharSet = CharSet.Auto)]
    static extern SafeFileHandle CreateMailslot(
        string lpName,
        uint nMaxMessageSize,
        uint lReadTimeout,
        IntPtr lpSecurityAttributes);

    #endregion

    #region 列挙体

    private enum DesiredAccess : uint
    {
        GENERIC_READ = 0x80000000,
        GENERIC_WRITE = 0x40000000,
        GENERIC_EXECUTE = 0x20000000
    }

    private enum ShareMode : uint
    {
        FILE_SHARE_READ = 0x00000001,
        FILE_SHARE_WRITE = 0x00000002,
        FILE_SHARE_DELETE = 0x00000004
    }

    private enum CreationDisposition : uint
    {
        CREATE_NEW = 1,
        CREATE_ALWAYS = 2,
        OPEN_EXISTING = 3,
        OPEN_ALWAYS = 4,
        TRUNCATE_EXISTING = 5
    }

    private enum FlagsAndAttributes : uint
    {
        FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
        FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
        FILE_ATTRIBUTE_HIDDEN = 0x00000002,
        FILE_ATTRIBUTE_NORMAL = 0x00000080,
        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
        FILE_ATTRIBUTE_OFFLINE = 0x00001000,
        FILE_ATTRIBUTE_READONLY = 0x00000001,
        FILE_ATTRIBUTE_SYSTEM = 0x00000004,
        FILE_ATTRIBUTE_TEMPORARY = 0x00000100
    }

    #endregion

    private string slot = string.Empty;
    public string Slot
    {
        get { return slot; }
        set { slot = value; }
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MailSlot()
    {
    }

    /// <summary>
    /// メールスロット名付コンストラクタ
    /// </summary>
    /// <param name="strSlot">メールスロット</param>
    public MailSlot(string strSlot)
        : this()
    {
        Slot = strSlot;
    }

    /// <summary>
    /// メッセージの送信
    /// </summary>
    /// <param name="strMessage">送信する文字列</param>
    public void WriteMailSlot(string strMessage)
    {

        SafeFileHandle fileHandle = null;

        try
        {
            fileHandle = CreateFile(Slot,
                    DesiredAccess.GENERIC_READ | DesiredAccess.GENERIC_WRITE,
                    ShareMode.FILE_SHARE_READ | ShareMode.FILE_SHARE_WRITE,
                    0,
                    CreationDisposition.OPEN_EXISTING,
                    FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL,
                    (IntPtr)0);


            using (FileStream fs = new FileStream(fileHandleFileAccess.Write))
            {
                byte[] msg = Encoding.UTF8.GetBytes(strMessage);
                fs.Write(msg0msg.Length);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            System.Diagnostics.Debug.WriteLine(ex.StackTrace);
            throw ex;
        }
    }

    /// <summary>
    /// メールスロット受信待ち
    /// </summary>
    /// <param name="maxMessageSize">最大メッセージ数</param>
    /// <param name="readTimeout">タイムアウト</param>
    public string ReadMailSlot(uint maxMessageSizeuint readTimeout)
    {
        string result = string.Empty;
        SafeFileHandle mailslotHandle = null;
        try
        {
            mailslotHandle = CreateMailslot(SlotmaxMessageSizereadTimeout, (IntPtr)0);

            using (FileStream fs = new FileStream(mailslotHandleFileAccess.Read))
            {
                byte[] buf = new byte[maxMessageSize];
                int len = fs.Read(buf0buf.Length);
                result = Encoding.UTF8.GetString(buf0len);

                fs.Close();
           }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            System.Diagnostics.Debug.WriteLine(ex.StackTrace);
            throw ex;
        }
        return result;
    }
}

[.NET]MailSlot

最近、過去の資産(今となってはレガシーな物)のリプレースをやっています。
で、そのシステムのなでMailSlotを使った通信をしていました。そこで送信・受信のドライバを作ろうと思って.NET Frameworkのライブラリを探したのですが、それらしいものが見つからず、仕方なく自作しました。
とりあえず、受信まちの場合、以下のようなコードで実現可能です。送信側はまた後日。

通常、CreateMailslotしたハンドルに対してCloseHandleする必要があるのですが、FileStream.close()がそれらしく処理してくれるっぽい。

[DllImport("kernel32.dll"CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateMailslot(
    string lpName,
    uint nMaxMessageSize,
    uint lReadTimeout,
    IntPtr lpSecurityAttributes);

/// <summary>
/// メールスロット受信待ち
/// </summary>
/// <param name="maxMessageSize">最大メッセージ数</param>
/// <param name="readTimeout">タイムアウト</param>
public string ReadMailSlot(string slotuint maxMessageSizeuint readTimeout)
{
    string result = string.Empty;
    SafeFileHandle mailslotHandle = null;
    try
    {
        mailslotHandle = CreateMailslot(slotmaxMessageSizereadTimeout, (IntPtr)0);

        using (FileStream fs = new FileStream(mailslotHandleFileAccess.Read))
        {
            byte[] buf = new byte[maxMessageSize];
            int len = fs.Read(buf0buf.Length);
            result = Encoding.UTF8.GetString(buf0len);

            fs.Close();
        }

    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.Message);
        System.Diagnostics.Debug.WriteLine(ex.StackTrace);
        throw ex;
    }
    return result;
}


検索で結構ヒットしているようなので、その後のblogへリンクを追加

GDD Blog: [.NET]MailSlot(その2)
GDD Blog: [.NET]MailSlot(その3)

2007年12月10日月曜日

[他]HTTPのダウンロード(HTML)

ASP.NETでexcelファイルをダウンロードする場合、対象がIEの場合、何もしなければexcelがoleでIEの中に表示されているため、以下のようにContentTypeを指定して強制的にファイルをダウンロードします。

Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition""attachment; filename =target.xls");
Response.WriteFile("c:/path/target.xls");
Response.Flush();
Response.End();


しかし、@ITを見ていると、HTMLで解決できる方法が紹介されていました。以下のようになります。

<a href="/path/target.xls" type="application/octet-stream>ダウンロード</a>


・・・目からうろこでした。

2007年12月8日土曜日

[.NET]SVNクライアント(SubCommander)

Windowsの場合、SVNクライアントといえばTortoiseSVNが有名です。その他にもいくつかありますが、最近SubcommanderというSVNクライアントを発見しました。

TortoiseSVNはExplorerに統合されます。この点が賛否両論で分かれるところではあります。私は別に気になりませんが、気にする人は気にするみたいです。
で、Subcommanderは日本語化は出来ませんが、単独のアプリになります。何がよいかというと、複数のリポジトリをまとめて管理できるところです。ただ、まだ荒削りですが、私は期待しています。

現在携わっているプロジェクトでは、アセンブリ単位でリポジトリを分ける必要があり、ちょっとメンドクサイのでちょうどいいのです。ただ、VS.NETを使っている分ではAnkhSVNがあるので通常の操作では苦にはなりませんが、VS.NETを使わずにメンテしたい場合には最適ですね。

2007年12月6日木曜日

[他]SVNのホスティングサービス(無料)

時々何かを調べるために、簡単なテストプログラムを書くことがあります。その資産は以外に有用なものになります。
この小さなテストプログラムの数がある程度たまってくると、それを元に小さなテストプログラムを作ることができるので、作業効率が上がります。

しかし、家や会社あるいはマシン単位でばらばらに管理しているとどれが最新版かわからなくなったり、マシンによっては無かったりと、管理が面倒なことになります。で、それを解決するのがWeb上にあるファイル共有サービスだったりします。

しかし、ファイル単位で版数の管理をしたりとなるとCVSやSVNのような履歴を管理するシステムが必要となり、そういったサービスを提供してくくれる業者はなかなかありません。さらに無料ともなるとその数は激減します。

なかなか私の希望にかなうもの(無料・容量>50MB・オープン)はみつからず、数年の日々がたちました。

で、最近Google codeにSVNのホスティングサービスがあることを知りちょっと調べてみました。
・・・私の希望を希望を完全に満たしているうえに管理ツールまであります。登録も意外と簡単にすみ、
今はSVNを使って快適に過ごしています。
しかし、ある程度オープンなライセンスになってしまいます。なので仕事の情報が混じっていないかなど、ちょっと気をつけないといけませんね。

.NETのテストプログラムは数が少ないので完全に移行できましたが、Javaは整理にちょっと時間がかかりそうです。

2007年12月2日日曜日

[.NET]型付DataSet

VS.NET2005+.NET Framework2.0の環境で、データモデルを構築する場合、型付DataSetを使うのがもっとも手っ取り早く、障害も少なくシステムを構築することが出来ます。

しかし、
  • 型付DataSetをつくるまでもない
  • 流動的にDataTableの構成が変わる

のような場合、DBを使うシステムを前提としますが、DbDataAdapter.FillSchemaを使う方法があります。
(オーバーロードがいくつかあるのでこの限りではないかもしれませんが。。。)

このFillSchemaは、SELECT文にあわせてDataTableまたはDataSetを作ってくれます。
型付DataSetのようにコンパイルで障害の検出をすることは出来ませんし、都度DB接続するため、Webシステムでアクセス頻度の高いページには不向きかも知れませんが、動的に構成が変わる場合などにDataTable.Colmns.Addをちまちまやらなくても済みます。