2012年6月13日水曜日

FTPでのファイル一覧取得の壁

FTPで指定したディレクトリ下のファイル一覧はFTPの"LIST"コマンドによって取得できる。しかし、このコマンドで取得できる結果のフォーマットについて標準的な規定は無く、FTPサーバーの実装に依存する。

つまり結果がどんなフォーマットで来るか分からないために、パース処理の実装が極めて難しい。FileZillaのような優秀なFTPクライアントがこの問題をどう解決しているかというと、メジャーな各FTPサーバーに依存した血反吐を吐きそうなパース処理を頑張って書いているのである(directorylistingparser.cppを見ると3000行弱あった)。

CDirectoryListingParser::ParseAsUnix()とCDirectoryListingParser::ParseAsDos()で大体のFTPサーバーをカバーできてるっぽい。
マネージコードだったら、このソースをまんま使いとこだけどC++だしGPLだし厄介だなあ。

更に厄介なことにFTPはディレクトリを丸ごと削除ができず、子階層のファイルを一件一件削除していって空のディレクトにしてからでないと"RMD"コマンドでディレクトリ削除ができない。従って"LIST"コマンドでファイル一覧取得→各ファイルを"DELE"コマンドで削除していく→空になったフォルダを"RMD"コマンドで消す、といった手順を踏まないといけない。これでは最初の"LIST"の時点でもう躓いてしまう。

今回はIISへの対応になりそうだからやっつけパーサーで誤魔化すしかないのか・・・。

2012年6月8日金曜日

WPFでスクロールバーを付ける

初歩的なことだけど使う機会がなく知らなかったので備忘録。

WPFではコントロールのサイズは動的に決定されるのが望ましい。例えば多言語対応を行った場合、日本語と英語とでは表記の長さが異なり、日本語用にサイズを固定化すると、英語化した際にはみ出たりする。あるいはファイル、DB、ネットワークに依存する値などは事前に固定化するのが難しい。

ということでSystem.Windows.WindowのSizeToContentプロパティをWidthAndHeightに設定することを自分はよくやる。Width/Heightは大体Auto。MinWidthやMaxWidthなども指定しない。こうするとウィンドウの内容は0x0まで小さくできる。

800x600を切るようなサイズまで小さくしたとき、全てのコントロールがウィンドウに収まるとは限らない。この場合、スクロールバーを実装していないと一部コントロールはウィンドウ外にはみ出てしまう。「最大化すりゃ最近の画面サイズなら収まるだろうからいいじゃん」て意見もあるかもしれないが小さいサイズにして画面端に待機させておきたいことはままある。

前置きが長くなったけどWPFには標準でScrollBarとScrollViewerというコントロールが用意されている。 ScrollViewerはScrollBarをラップ・拡張しており、上記のようなニーズにはScrollViewerを使うことになる。 使い方はスクロールバーを付けたいレイアウトコントロールを丸ごとScrollViewerの子要素にしてやる。

デフォルトでは縦方向のスクロールバーだけが表示される。 これはHorizontalScrollBarVisibilityプロパティの規定値がHiddenになっているからだ。この値はSystem.Windows.Controls.ScrollBarVisibilityを見れば分かるように4種類あるが、基本的にAutoでいいだろう。VerticalScrollBarVisibilityについても同様に設定する。

XAMLでいうとこんな感じ
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

            <!-- 分かりやすいようにMarginをでかくする -->
            <Label Content="設定値1" Margin="30" />
            <TextBox Width="300" Grid.Column="1" Margin="30" />
            <Label Content="設定値2" Grid.Row="1" Margin="30" />
            <TextBox Width="300" Grid.Row="1" Grid.Column="1" Margin="30" />
        </Grid>
    </ScrollViewer>
</Window>

Windows用インストーラ作成環境

無料で利用できる選択肢としては下記のものが有名。
  • WIX
  • Inno Setup
  • NSIS
  • Visual Studioセットアッププロジェクト(正式名称は何だろ?)
  • InstallShield Limited Edition
単一バイナリでの多言語対応を考えると
  • Inno Setup
  • NSIS
まで絞られる。メンテナンス頻度を見るとNSISは2009年から更新が無い。 両社は機能的に大差は無いと思われるが、Inno Setupを選択するのが無難かな。 いいトコどりすると製品版InstallShieldになるけど、あまりにも高い。

Innoも情報や参考になるコードが少なくて厳しいけど、それは他のも同じ。参考になるサイトを忘れないように貼っておく。
http://inno-setup.sidefeed.com/
http://bitassociates.jp/tools/innosetup/index.html 

2012年6月7日木曜日

LINQ to SQLでの接続テスト

LINQ to SQLで接続情報が間違っていたり SQLサーバーが死んでたりして接続できないとき どうやって判定すんのかな、ってのが分かんなかったのでメモ。 System.Data.Linq.DataContextは コンストラクタで接続文字列を指定できる。 その接続文字列でアクセスできたかどうかはDatabaseExists()で判定可能。 以下、WPF前提のコード例。
using (var db = new DataClasses1DataContext(builder.ConnectionString)) {
    if (!db.DatabaseExists()) {
        MessageBox.Show("Database open error.");
        this.Close();
    }
    else {
        var t_Customer = from a in db.T_Customer select a;
        this.ListView1.ItemsSource = t_Customer;
    }
}
LINQ to SQLが自動生成するクラスは、 Modelが実装済みなのでViewModelとしてそのままViewに突っ込める。 トランザクションは http://msdn.microsoft.com/ja-jp/library/bb387002.aspx にあるようにスコープで囲むだけでいいみたいだ。

2012年6月6日水曜日

SyntaxHighlighter導入

ソースコードを貼っつける際に 見やすく装飾してくれるらしいウィジェットのテスト
for (int i = 0; i < 10; ++i) {
    Console.WriteLine("i = {0}", i);
}
まーこんなもんかな? SyntaxHighlighterというらしい。

SqlConnection.BeginTransaction()でエラー

下記のようなコードを書いたら sqlConnection.BeginTransaction()でInvalidOperationExceptionが発生。
try {
    using (SqlConnection sqlConnection = new SqlConnection(this.connctionString))
    using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
    using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.Serializable)) {                
        sqlConnection.Open();
        sqlCommand.Transaction = sqlTransaction;
        int ret = -1;
        sqlCommand.CommandText = "DELETE FROM T_Hoge WHERE Piyo = '" + piyo + "';";
        ret = sqlCommand.ExecuteNonQuery();
        sqlTransaction.Commit();
    }
catch (Exception e) {
    throw;
}
"操作が無効です。接続は閉じています。"とか言われちゃったんで http://msdn.microsoft.com/ja-jp/library/2k2hy99x%28v=vs.80%29.aspx を見てみるとBeginTransaction()の前にSqlConnection.Open()しとかなきゃいけなかった。 うっかりうっかり。 トランザクション周りはロックやら結構気を付けないといけないね。 まあマルチスレッドでのアトミック性みたいな話と大体同じだとは思うけども。

2012年6月4日月曜日

久々や

プログラミング関係のことをメモ代わりに書いていきたいので
久しぶりにブログというものを再開することにした。
豆腐メンタルなのでいつモチベがポッキリいくかわからんけど
主にWPF+C#、たまにC++/MFC関係を書いていくつもり。