プログラム

125件の投稿

シリアル番号の検査 in MSI セッアップ、 WIX

パッケージソフトを作って、インストールの途中でシリアル番号のチェックをしたい。[InstallShield]等の高価なソフトを買うほどのことではない。このようなケースは結構あるのではないでしょうか。

[Visual Studio]でSetupProjectを作成できますが、添付されているシリアル番号の検査は、非常に単純で内実を知っているユーザからすれば全くの無防備です。

恐らく政治的な(あるいは商売上の)方針だと思いますが、マイクロソフトはこのあたりの実装について公開していません。公開されているカスタムアクションは、製品のインストール終了後直ちにインストールしたプログラムを開くとか、データベースを作成するとかの方法の情報だけで、インストールの途中で独自のシリアル番号のチェックするルーティンを走らす情報はどこを探しても出てきません。

私自身は[InstallAware]というソフトを購入したのである意味緊急という訳ではないのですが、めったに使わないのにOSや[.Net Framework]の更新があるたびに、買い替えなければいけないのは抵抗があります。

手持ちのツールでなんとかしたいと長く考えていました。

なんとかなるのではないかと思いますのでご報告します。
道具立ては[Visual Studio 2010]と[Wix](WixEdit:Wix用無料IDE)です。

[Visual Studio 2010]には、マネージコード用のカスタムアクションDll作成ツールが付いていて、C++でもVBでもC#でもカスタムアクションが作成できます。手馴れた言語が使えるのはありがたいことです。

次はWixの情報です。これがなかなかありません。
多分唯一といっていい解説書は、[Wix : A Developer’s Guide to Windows Installer XML](PACKT)です。
ドンぴしゃりの解説はありませんが、Wix一般を勉強するにはいい教科書だと思います。紙が不必要に厚いのが気に入りませんが、英文は平易ですので読み進むのに抵抗はありません。

一番ほしいシリアル番号ルーティンの組み込みの方法は、インターネットで入手可能な[Wixチュートリアル](日本語訳)で十分です。

これだけあれば、[Visual Studio]のセットアッププロジェクトで可能な限りキチンとしたセッアッププロジェクトを作成し、インストールパッケージ(msi)を出力。出来上がったmsiを[WixEdit]で逆アセンブルし[wxs](XMLソースファイル)に変換して、これにカスタムアクションを組み込めばよい。

と思っていたのですが、そうは問屋が卸さないようです。
[Visual Studio]の[msi]を逆アセンブルした[wxs]は、教科書に書いてあることといささか異なります。出来上がった[wxs]はすんなり使えません。

これを色々調べながら使うくらいなら、はじめから[WixEdit]でwxsを作った方がいいようです。[Visual Studio]はDllの依存関係と所在を教えてくれますので、[Visual Studio]ではその辺りの情報だけ使うとよいと思います。

完全ではないのですが、模擬的にこれでよさそうだという[wxs](msi)を作りました。WixEditでターゲットマシンのディレクトリを作り、[Visual Studio Setup Project]からDllの情報を参考にしながら、ディレクトリの中にComponentを作成。[Wixチュートリアル]に従って[UI]を作成。この中で[Visual Studio]で作っておいたカスタムアクション(.CA.dll)をコールするようにしました。

まだ色々詰めなければいけませんが、なんとか物にできそうです。

C# 嫌いなところ

VB to C#の作業は、必要最小限のFormを書き換え、コンパイルエラーがなくなるところまで来ました。まだ沢山の警告が出ますので、これもつぶしていかなければいけません。

C#のいいところは、VBに比べて「オブジェクト指向」らしいというところです。オブジェクト指向の基本理念を守っていますので、使っていてある種安心感があります。オブジェクト指向の本筋を勉強できるかなと思います。

しかし、嫌いなところもあります。これはオブジェクト指向とは関係ない話だと思います。仕様を改定してほしいものです。

先ず、ブロックから抜ける方法です。
VBの[Exit ]構文は、

Exit { DO | For | Function | Property | Select | Sub | Try | While }

です。
ブロックがネストしている場合にも、指定のブロックから抜けることができます。例えば次のようなものです(一部、[Visual Basic .NET ランゲージリファレンス](マイクロソフトプレス)から)。

Sub ExitStatementDemo()
    Try
        Do
            For I As Integer = 1 To 1000
                Dim MyNum As = Int(Rnd * 1000)
                Select Case MyNum
                    Case 7: Exit For
                    Case 29: Exit Do
                    Case 54: Exit Try
                    Case 65: Exit Sub
                End Select
            Next I
        Loop
    End Try
End Sub

C#には[break]がありますが、これは一番内側の繰り返しブロックから抜けるだけです。tryブロックには使えません。

Javaでは、ブロックにラベルを付すことができ[break label]でそのブロックから抜けることができます。
(以下[Java クイックリファレンス](O’Reilly)から)

testfornull: if (data != null) {
    for (int row = 0; row < numrows; row++) {
        for (int col = 0; col < numcols; col++) {
            if (data[row][col] == null) break testfornull;
        }
    }
}

C#ではこれらの仕組みがありませんので、ネストした内側のブロックから抜けるためには、ラベルを用意しFortran流の[goto]を使うしかありません。エレガントではありません。

次はswitch文です。
VBではCase文に[式]等が使え柔軟です。

Dim Number As Integer = 8
Select Number
    Case 1 To 5
        ...
    Case 6, 7, 8
        ...
    Case Is > 8
        ...
    Case Else
        ...
End Select

しかし、C#([switch])ではCase文のラベルには定数またはリテラルしか使えません。ラベルが列挙子や文字列だけならいいですが、でなければif文を書いた方がいいくらいです。

ついでに嫌な部分を書けば、関数コールで参照渡しを使う場合、オブジェクトのプロパティを渡せないことです。オブジェクトそのものを渡すか、関数コールの前後に作業用の変数を用意しなければならない、ことも大変面倒です。

例えば、沢山のテーブル(クラス)が[更新日]列を持っていて、レコードに変更があれば更新日を書き換えるものとします。クラスの[更新日]プロパティでなく、クラスそのものを参照渡しにすると、クラスの数だけ関数を用意しなければいけません。これではあまりにひどいので汎用化したいのです。ジェネリック(多分だめ)かインターフェースかAbstractを使うことになると思いますが、これがなかなかうまくいきません。

まだ十分研究していませんが、これはこれでコツを掴まなければいけないのだろうと思っています。

VB to C# 3

VBプロジェクトからC#プロジェクトへの書き換えは、
次のように三つのプロジェクトを開いて作業しています。
一つ目はオリジナルのVB2010プロジェクト(以下[VBPro]といいます)、
二つ目はSharpDevelopで変換したC#プロジェクト(以下[SDevPro]といいます)、
三つ目は新規のC#プロジェクト(以下[新規Pro]といいます)です。

最初は二つ目のプロジェクト[SDevPro]ですべて作業していたのですが、
SharpDevelopの変換は不十分で、
それに沢山の修正を加えてゴチャゴチャにするよりも、
新たにプロジェクトを構築していく方がすっきりしてよいと気づき、
新規のプロジェクトを一から構築することにしました。

まず、Formを継承していないクラスファイルは問題が少ないので、
ほぼそっくり[SDevPro]から[新規Pro]にコピーします。

VBはすべてがいい加減で(言葉は適切ではありません)、
C#はすべてが厳密だという問題が出てきます。

すぐ問題になるのが名前空間[namespace]です。
プロジェクトが大きくなるとサブフォルダを作ることは至極当然です。
VBでは、特に[namespace]を気にしなければ、
フォルダに関係なく一つの名前空間の下に管理されます。
C#ではフォルダをつくり、その中にクラスなりフォームなりを作成すると、
そのクラスの[namespace]は、
[ルート名前空間 + ドット + フォルダ名]になります(Javaと同じです)。

プロジェクトのなかでも別のフォルダを使うときには、
[using]で必要なサブフォルダ=[namespace]を呼び込んでおかなければなりません。

後でフォルダ名を変えたり、ファイルの移動をしたりするとどうなるか。
ソリューション エクスプローラでファイルを[切取]、[貼り付け]でフォルダを移動しても、
[Visual SourceSafe 2005]は問題ありませんが、
当該クラスで記述した[namespace]は自動では変更してくれませんので、
手動で変更しなければいけません。

100%ではありませんが、
この辺りはVS2010の[リファクター]やDecXpressの[RefactorPro!]が十分働いてくれます。

フォームを継承したクラス(要するにForm)のデザイン画面は、
[VBPro]の画面をコピーし貼り付けても問題ないようです。
コードは[SharpDevelop]で変換した[SDevPro]のコードをコピーします。

どちらにしても自動変換の後はたくさんのエラーが表示されます。

VBメソッドの[Optioal]はC#でサポートしていない」ので、
一部書き換えを始めていましたが、C#2010からC#でもこれをサポート。
C#ではもっと簡単に、
引数定義の後ろに[=]に続いてデフォルト値を書くだけでいいと分かり(たとえば[= true])、
「よかった!!」という感じです。
実はこの変更もどのように統一的な形にするか悩んでいました。

C#への書き換えで気づいたのは、
VBのプログラミングムでは私自身「ずいぶんいい加減に書いてきたな」ということです。

その一つが、[ByRef]の使い方です。
メソッド側で変更がある場合は、碌に考えもしないで[ByRef]を使っていました。
VBでは害にもならないのですが、
C#ではコールする側にも[ByRef]に相当する[ref]を書かなければいけません。
しかもコールする側でこの変数に値を代入しておかなければエラーになります(VBでは警告)。

反対にメソッドを呼ぶ側でなく、呼ばれるメソッド側で値を作る場合は、
呼ぶ側も呼ばれる側も[out]キーワードを付けなければいけませんし、
呼ぶ側は変数定義だけでにしておかなければいけません。
呼ぶ側で変数に値を設定するとエラーになります。

もう一つ面倒なのは、C#で参照渡し([ref]あるいは[out])でメソッドを呼ぶとき、
オブジェクトの[プロパティ]を引数に渡すことはできません。
例えば、商品台帳(クラス)の価格プロパティをメソッドで変更しようとして、
価格プロパティだけをメソッドに渡すことができません。
プログラムを改変する必要があります。
この辺りは「C#は融通が利かない」とみるか、「厳密でいい」とみるか意見の分かれるところです。

現在DBでは[Ado.Net]、[Linq to SQL]、[Linq to EF]を節操もなく使っていますので全面見直しです。
まだ先は長そうです。

どちらにしても、
[VB to C#]の変換作業はVBプログラマにとって最も効率のいいC#の勉強法だと思います。

C# 参考書

昔-Windows以前の世界では-FortranやCを使っていて、
オモチャっぽいBasicは好きではありませんでした。

WindowsでVBが発売されたとき、
統合開発環境=IDEの充実した言語としては他にVC++しかありません。
VC++のフレームワークはあまりにゴチャゴチャしていて、
予想どおり大多数の人はVC++を使って来ませんでした。

私もVC++はあまり使わず、結局長い間VBを使ってきました。
VBの教科書としてはBalenaの翻訳本がとてもいいと思います。
BalenaがVB.NETに書き換えたとき躊躇せず購入しましたし、
VB2005の解説書も購入しました。
私は完全にBalenaを信頼しています。

今回本格的にC#の勉強をしようと参考書を物色しましたが、
日本語の「いい教科書がない」と痛感しました。
書店には「・・・入門」はたくさんありますが、中級以上の本がない。

私はよく思います。
「なぜ日本語のいい教科書がないのだろう」と。
理由は色々あると思いますが、一つは投資対効果でしょう。
一冊の本をしっかり書くことは大変な労力を必要としますが、
数年でバージョンが変わるソフトを追いかけて、
大して売れもしないうちに次々教科書を出版するのは採算に合わないのでしょう。

英語圏であればそれなりの購買層があるが、
日本語に特化すると商売にならないのでしょう。
結局私たちは中級以上の本は賞味期限を過ぎた翻訳本か、
それが嫌なら原書を読むしかありません。

ついでですが、「調べる」にはオンラインヘルプはいいが、
「勉強」には本の方が数段勝ると思います。

今回C#のプログラムを書くにあたって、いい教科書に出会えません。
[プログラミング VIsual C# 2005]はもはや話題が古くLINQ等の記述がありません。
[Pro C# 2010 and the .NET 4 Platfrom](Apress)は1600ページ以上あって、
英語ですから拾読みするには適当ではありません。通読する時間などありません。
[C# 4.0 In a nutshell](O’Reilly)がリファレンスとしては一番いいかもしれません。

出版社に提案があります(出版社がこのブログを読んでいるとは思いません。独り言です)。
日本語の[・・・入門]はやめて、英語の定評のある参考書の翻訳本を素早く出版していただきたい。
これは出版社に対してのお願いというよりマイクロソフトへのお願いです。
マイクロソフトは[Microsoft Press]だけでなく、
他社の書籍に対してもいい翻訳本を迅速に出す工夫をしていただきたいと思います。

いい教科書があるかどうかは、その言語が広く使われるかどうに大きく影響します。

このような状態では、英語に弱い日本のソフト技術者は、最前線からどんどん追いやられてしまいます。

インドや中国の技術者の進出を見れば、彼らの勢いを肌で感じるでしょう。
ソフトにかかわる人は、「彼らを使えば安く開発できる」とのんきなことをいっている場合ではないでしょう。
ソフトウェア技術者も英語の技術書を「読む」くらいは身につけなければいけません。

大学の教養科目の英語で「文学書」の一節を勉強するなど間違っています(私の学生時代)。
大学生になればそれぞれに教養として専門書が読めるようにすべきです。
専門書はそもそも文学書と違って「凝った」表現はないのです。
自分の専門分野に限れば単語も数多くあるわけではありません。
ある種「慣れ」で、母国語程度とはいきませんが、慣れればいくらでも読めるのです。

今の大学の教養授業は変わったのでしょうか。

VB to C# 2

VB.NETからC#.NETへの変換では、
SharpDevelopは大いに助かるのですが、
結局ややこしいところは自動処理してくれなくて手作業で書き直さなければいけません。

全面書き換えには次のようなものがあります。

画面
LINQ
インデクサ

そのほかVBでよく使うメソッドでC#ではどうかけばいいのか分からないケースがあります。
たとえば、二つの日付の差の日数を計算する[DateDiff]や変数に数字が入っているかを調べる[isNumeric] 等をC#ではどう書けばいいか結構調べまわることになります。

LINQでは明示的に型指定のない変数(VBではDim、C#ではvarで宣言)をよく使います。
VBではつぎのように条件文の外で変数を宣言し、条件によるLINQ文の結果をその後で使っていくことができます。

Dim query
If true then
    query= From rec In 商品tbl Where rec.CD = “1234”
Else
    query= From rec In 商品tbl Where rec.CD = “5678”
End If

それどころが明示的に型指定のない変数をクラス変数(フィールド)にさえ使えます。

一方C#では、型指定のない変数は厳密にローカルにしか使えませんし。
上のコードのように初期化なしの宣言だけも許されません(nullでの初期化も不可)。

C#では型指定するか、プログラムの構造を変えるしかありません。

また、[LINQ toSQL]と[LINQ to Entiry Framework](以下LINQ to EFと書きます)との使い分けがまだよく分かりません。
おそらく、[LINQ to EF]に統一すべきだと思いますが、我がプログラムはまた統一していません。
[LINQ to SQL]は身軽な感じがしているので、
簡単なSQLはこちらが便利かなと思っています。

VBではLINQやデリゲートやジェネリックを使ってきましたが、
VBの緩い約束事にしたがって、「いい加減に使ってきたかな」という感想を持っています。

この際、「デリゲート、ジェネリック、LINQ、ラムダ式をキッチリ理解しなければいけない」
と考えています。
正味1、2ヶ月は必要かもしれません。