2008年12月24日水曜日

.NET RadioButton CheckedChangedイベントを処理する

ラジオボタンやチェックボックスでCheckedChangedイベントを処理する場合、
Private Sub RadioButton_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged

If Me.RadioButton1.Checked Then
Console.WriteLine("ラジオボタン1がチェックOn時の処理です。")
ElseIf Me.RadioButton2.Checked Then
Console.WriteLine("ラジオボタン2がチェックOn時の処理です。")
End If

End Sub 

このようなコードだと
ラジオボタン2をチェックONすると
まずラジオボタン2がチェックOnになったのでCheckedChangedイベントが走り、「ラジオボタン2がチェックOn時の処理です。」が出力されます。
そしてラジオボタン1がチェックOffになったのでCheckedChangedイベントが走り、再度「ラジオボタン2がチェックOn時の処理です。」が出力されます。




以下のコードのようにすれば処理が2回実行されることはないですね。
Private Sub RadioButton_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged
If DirectCast(sender, RadioButton).Checked Then
If Me.RadioButton1.Checked Then
Console.WriteLine("ラジオボタン1がチェックOn時の処理です。")
ElseIf RadioButton2.Checked Then
Console.WriteLine("ラジオボタン2がチェックOn時の処理です。")
End If
End If
End Sub  

Private Sub RadioButton_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged

If Sender.Equals(Me.RadioButton1) AndAlso Me.RadioButton1.Checked Then
Console.WriteLine("ラジオボタン1がチェックOn時の処理です。")
ElseIf Sender.Equals(Me.RadioButton2) AndAlso RadioButton2.Checked Then
Console.WriteLine("ラジオボタン2がチェックOn時の処理です。")
End If

End Sub

.NET Option Infer ステートメント

Option Infer ステートメント
変数の宣言に対するローカル型の推論の使用を有効にします。
VS2008で新たに追加されたオプションです。

Option Infe On
変数の宣言に対するローカル型の推論の使用を有効にします。

Option Infer Off
変数の宣言に対するローカル型の推論の使用を無効にします。

デフォルトはOnです。


Option Infer Onでは以下のようにAs句を書かなくてもコンパイルエラーになりません。
Public Shared Sub Main()

Dim str = "123"
str.Substring(0, str.Length)

Dim lst = New List(Of Integer)
lst.Add(1)

End Sub


またOption Strict Offでも上記のコードはコンパイルエラーになりません。
Option Strict OffではAs句を省略できるためです。

では何が違うかというと
Option Strict Off (Option Infer Off)では変数strも変数lstもObject型になります。
また「str.」と入力してもインテリセンスにはObject型のメンバしか表示されません。
strのSubstringメソッドや、lstのAddメソッドは遅延バインディングにより、実行時に変数の中身がチェックされ、メソッドが存在すれば実行されます。

これに対し
Option Infer On (Option Strict On)では、変数strはコンパイル時にString型と推論されます。変数lstはList(Of Integer)型と推論されます。
Option Infer Onでは、変数宣言の右辺の初期化式からコンパイラが変数の型を推論して決定されるので、「str.」と入力すればインテリセンスにはString型のメンバが表示されます。
右辺の初期化式を書かないとコンパイルエラーになります。


Option Strict Off、Option Infer Onでは
右辺の初期化式があれば型推論が行われstr変数はString型になります。
Public Shared Sub Main()
Dim str = "123"
str.Substring(0, str.Length)
End Sub

右辺の初期化式ががなければ型推論が行わずstr変数はObject型になります。
Option Strict Offなのでコンパイルエラーにはなりません。
Public Shared Sub Main()
Dim str 
str = "123"
str.Substring(0, str.Length)
End Sub

2008年12月23日火曜日

.NET Option Strict ステートメント

Option Strict ステートメント

Option Strict On
  • 変数の宣言でAs句を省略できません。
  • 遅延バインディングは禁止されます。
  • 明示的なキャスト演算子を使用しない縮小変換は禁止されます。



Option Strict Off
  • 変数の宣言でAs句を省略できす。
  • 遅延バインディングを使用できます。
  • 明示的なキャスト演算子を使用しない縮小変換が使用できます。




デフォルトはOption Strict Off です。
Microsoftのホームページに
「Option Strict On によって 厳密な型指定が可能になり、データの損失を伴う意図しない型変換を回避でき、
遅延バインディングが禁止されて、パフォーマンスが改善されるので、必ず使用することをお勧めします。」
とあります。

必ず使用することをお勧めします
Σ(゚д゚;) ヌオォ!? デフォルトでOption Strict Onにしたらいいじゃないの!?


Option StrictをデフォルトでOnにするには


「ツール」→「オプション」→「プロジェクトおよびソリューション」→「VisualBasicの規定値」で
Option Strictのデフォルト値をOnに設定できます。



変数の宣言でAs句を省略


Option Strict Offでは変数の宣言でAs句での型指定を省略することができ変数はObject型になります。
Option Strinct Onではコンパイルエラーとなります。
Public Shared Sub Main()
Dim a
a = 123

a = 123.4567

a = "123"
End Sub




遅延バインディング


遅延バインディングとは、Object型の変数にインスタンを代入し、そのインスタンスのメソッドなどを呼び出すことです。
Option Strict Offでは下記のコードは正常に実行されます。Option Strict Onではコンパイルエラーとなります。
Public Shared Sub Main()
Dim obj As Object
obj = New System.Windows.Forms.Form
obj.Text = "CreateForm"
Console.WriteLine(obj.Text)
End Sub

このコードではObject型の変数にフォームのインスタンスを代入しそのTextプロパティを参照しています。
コンパイル時は変数objがTextプロパティを利用可能か判断できないので、エラーにならず、
実行時に変数objに代入されているインスタンスをチェックし、Textプロパティが存在するか判断します。

これに対し、インスタンスが特定の型として宣言された変数に代入される場合を事前バインディングといいます。
Option Strict Onでは遅延バインディングは禁止され、事前バイディングを使用することになります。
Public Shared Sub Main()
Dim frm As System.Windows.Forms.Form
frm = New System.Windows.Forms.Form
frm.Text = "CreateForm"
Console.WriteLine(frm.Text)
End Sub




明示的なキャスト演算子を使用しない縮小変換


精度の小さな型を精度の大きな型に変換することを拡大変換といいます。
値型の拡大変換
Public Shared Sub Main()
Dim int As Integer
Dim lng As Long
frm.Text = "CreateForm"
Console.WriteLine(frm.Text)
End Sub


参照型の拡大変換
子クラスは親クラスのすべてのメンバをもっているため、拡大変換をおこなっても何も問題が発生しません。
Public Shared Sub Main()
Dim frm As System.Windows.Forms.Form
frm = New System.Windows.Forms.Form
frm.Text = "CreateForm"
Console.WriteLine(frm.Text)
End Sub


拡大変換は、実行時には常に正常に行われ、データ消失が発生することはありません。
Option Strict OnでもOffでも、拡大変換は暗黙的に実行できます。

拡大変換に対し精度の大きな型を精度の小さな型に変換することを縮小変換といいます。
縮小変換は正常に行われるとは限りません。

たとえばOption Strict Offで以下のコードをを実行すると、
Long型の値がInteger型の範囲に収まらないため、OverflowExceptionが発生します。
Public Shared Sub Main()
Dim lng As Long = 12345678901
Dim int As Integer
int = lng
End Sub


またOption Strict Offで以下のコードを実行すると、
Double型の値の小数点以下部分が欠落します。
Public Shared Sub Main()
Dim dbl As Double = 123.45
Dim int As Integer
int = dbl
End Sub


Option Strict Onでは上記のコードはコンパイルエラーととなります。
縮小変換を行う場合、明示的にキャスト演算子を使用しなければなりません。
Public Shared Sub Main()
Dim lng As Long = 123
Dim int As Integer
int = Cint(lng)
End Sub

.NET Option Explicit ステートメント

Option Explicit ステートメント
ファイル内のすべての変数を明示的に宣言するよう強制します。


Option Explicit On
すべての変数を明示的に宣言する必要があります。
宣言されていない変数名を使用すると、コンパイル時にエラーが発生します。

Option Explicit Off
宣言されていない変数を使用できます。
宣言されていない変数はすべて Object 型になります。

デフォルトはOnです。

以下のコードはOption Explicit Offでは正常に実行され「a:1」が出力されます。
変数aは宣言されていませんが、暗黙的にObject型の変数になります。
またOption Explicit Onではコンパイルエラーになります。
Public Sub ExplicitTest()
a = 1
Console.WriteLine("a:{0}", a)
End Sub

.NET Option Compare ステートメント

特に意識することなくデフォルトのBinaryにしています。
改めて「Option Compareって何ですか?」と聞かれて即答できませんでした・・・。


Option Compare ステートメント
文字列のデータを比較するときに使用する、文字列の比較方法を指定します。

Option Compare Binary
文字コード番号順で文字列が比較されます。

Option Compare Text
システムロケール(言語や国に固有の情報)で決められたテキストの並べ替え順序に基づいて、文字列が比較されます。

デフォルトはBinaryです。


以下のコードをBinaryとTextでそれぞれ実行してみます。
文字コードは「A」は65、「B」は66、「a」は97です。
Public Sub ComparTest()
Console.WriteLine("B > a:{0}", "B" > "a")
Console.WriteLine("A = a:{0}", "A" = "a")
End Sub



Option Compare Binaryでの実行結果は
B > a:False
A = a:False
となります。


Option Compare Textでの実行結果は
B > a:True
A = a:True
となります。

2008年12月18日木曜日

Bloggerに目次を表示させるには

以下のサイトを参考にしてブログに目次をつけてみました。
clmemo@aka :@ Blogger に目次を設置する

これはたいへん便利です。

まずHTMLの編集でテンプレートを表示します。
このとき「ウィジットのテンプレートを展開」にチェックをつけること。
あとはリンク先の通りですが、Contents Includableのコードにコメントを入れるとエラーになりました。

BloggerでSyntaxHighliterを使うには?

この記事は以前に投稿したのですが、なぜか消えてしまいましたので再投稿です。

FaziBear Blogger Syntax Highlighter にアクセスします。

「To add it check out this page」をクリックします。


「Add To Blogger」をクリックします。


「ウィジットを追加」をクリックします。


以上で設定は終了です。


使い方は表示させたいコードをPreタグで囲みます。赤字部分は言語により変更します。
<pre name="code" class="java">

(コード)

</pre>




サポートしている言語は以下の通りです。

  • c

  • c++

  • c#

  • css

  • delphi

  • html

  • java

  • js

  • pascal

  • php

  • python

  • ruby

  • sql

  • vb

  • xml




2008年12月17日水曜日

.NET DataTableをグループ化し集計するには

DataTableから重複を除くには

DataTableの集約計算を行う(Compute)
を利用して、DataTableをグループ化し集計を行います。

以下のようなデータが入ったDataTableから、Field1とField2で重複を取り除き集計をおこないます。

Field1Field2Field3
りんご青森100
りんご青森100
みかん愛媛100
みかん和歌山100
みかん和歌山100
メロン北海道100

重複を取り除いて集計した結果は以下のデータになります。
Field1Field2Expr
りんご青森200
みかん愛媛100
みかん和歌山200
メロン北海道100


    Dim dtOriginal As New DataTable
dtOriginal.Columns.Add("Field1", GetType(String))
dtOriginal.Columns.Add("Field2", GetType(String))
dtOriginal.Columns.Add("Field3", GetType(Integer))

dtOriginal.Rows.Add(New Object() {"りんご", "青森", 100})
dtOriginal.Rows.Add(New Object() {"りんご", "青森", 100})
dtOriginal.Rows.Add(New Object() {"みかん", "愛媛", 100})
dtOriginal.Rows.Add(New Object() {"みかん", "和歌山", 100})
dtOriginal.Rows.Add(New Object() {"みかん", "和歌山", 100})
dtOriginal.Rows.Add(New Object() {"メロン", "北海道", 100})

Dim viw As New DataView(dtOriginal)
Dim isDistinct As Boolean = True
Dim cols() As String = {"Field1", "Field2"}
Dim dtFilter As DataTable = viw.ToTable(isDistinct, cols)
dtFilter.Columns.Add("Expr", GetType(Integer))
For Each row As DataRow In dtFilter.Rows
    Dim expr As String = String.Format("Field1 = '{0}' AND Field2 = '{1}'", row("Field1"), row("Field2"))
    row("Expr") = dtOriginal.Compute("SUM(Field3)", expr)
Next


For Each row As DataRow In dtFilter.Rows
    Console.WriteLine("Field1={0}, Field2={1}, Expr={2}", row.Item("Field1").ToString, row.Item("Field2").ToString, row.Item("Expr").ToString)
Next

.NET DataTableから重複を取り除くには

以下のようなデータが入ったDataTableから、Field1とField2で重複を取り除きます。
Field1Field2
りんご青森
りんご青森
みかん愛媛
みかん和歌山
みかん和歌山
メロン北海道


重複を取り除いた結果は以下のデータになります。
Field1Field2
りんご青森
みかん愛媛
みかん和歌山
メロン北海道


Dim dtOriginal As New DataTable
dtOriginal.Columns.Add("Field1", GetType(String))
dtOriginal.Columns.Add("Field2", GetType(String))
dtOriginal.Rows.Add(New Object() {"りんご", "青森"})
dtOriginal.Rows.Add(New Object() {"りんご", "青森"})
dtOriginal.Rows.Add(New Object() {"みかん", "愛媛"})
dtOriginal.Rows.Add(New Object() {"みかん", "和歌山"})
dtOriginal.Rows.Add(New Object() {"みかん", "和歌山"})
dtOriginal.Rows.Add(New Object() {"メロン", "北海道"})

Dim viw As New DataView(dtOriginal)
Dim isDistinct As Boolean = True
Dim cols() As String = {"Field1", "Field2"}
Dim dtFilter As DataTable = viw.ToTable(isDistinct, cols)

For Each row As DataRow In dtFilter.Rows
Console.WriteLine("Field1={0}, Field2={1}", row.Item("Field1").ToString, row.Item("Field2").ToString)
Next


【関連項目】
DataTableをグループ化し集計するには

2008年12月11日木曜日

GrapeCityTool MultiRow5.0J 数値型ユーザ定義セルを改造してハマった

以前に数値型セルを作成しました。
MultiRow 5.0J ユーザ定義セル
この数値セルは最小値~最大値の範囲外の値が入力または設定されると
セルクラスのOnCellFormattingメソッドでNothingを設定するようにしていました。

今回は、最小値~最大値の範囲外の値が「設定」された場合はNothingを設定し
範囲外の値が「入力」されたときは、入力前の値を表示するよう変更しようとしたのですが
大いにハマりました。

入力前の値はセルクラスのInitializeEditingControlメソッドの引数formattedValueでセルクラスのインスタンス変数に退避したのですが、
セルクラスのOnCellFormattingメソッドでこの退避した値を参照すると常にNothingが返ります。

結論
退避したインスタンス変数をCloneメソッドで作成したオブジェクトに設定しなければならない。


MultiRow5.0J…もう嫌だ。

GrapeCityTool MultiRow5.0J セルの値が変更された場合のみCellValidatingイベントを発生させるには

標準ではセルの移動を行っただけでCellValidatingイベントが発生します。
ユーザによりセルの値が変更された場合のみ、CellValidatingイベントを発生させるようにします。

CellValidatingイベント内で変更後の値は e.FormattedValueで取得できます。
問題は変更前の値の取得方法です。
GetValueメソッドで取得すればいいのですが
下記の1では変更後の値が返ります。2では変更前の値が返ります。

1.以下の操作で発生するイベント
Enterキーで編集状態にする
セルに入力
Enterキーで編集状態を終了する
Tabキーでセル移動

CellEnter
CellBeginEdit
CellEditingControlShowing
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellEndEdit
CellLeave
CellValidating
CellValidated

2.以下の操作で発生するイベント
マウスダブルクリックで編集状態にする
セルに入力
マウスクリックでセル移動
CellEnter
CellBeginEdit
CellEditingControlShowing
CellLeave
CellValidating
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellValidated
CellEndEdit



CellValidatingイベント内でGetValueメソッドで取得する値はあてにならないので
CellEnterイベントでインスタンス変数に変更前の値を退避しておき、
CellValidatingで退避した変更前の値と変更後の値を比較し、ユーザによる変更があったかどうかを判定します。

Public Class CustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _OriginalValue As Object

Protected Overrides Sub OnCellEnter(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
'変更前の値を記憶します。
Me._OriginalValue = Me.GetValue(e.RowIndex, e.CellIndex)
MyBase.OnCellEnter(e)
End Sub

Protected Overrides Sub OnCellValidating(ByVal e As GrapeCity.Win.MultiRow.CellValidatingEventArgs)
'OnCellEnterイベントで記憶した値と比較します。
Dim isCellModified As Boolean
Dim currentValue As Object = e.FormattedValue
If Me._originalValue Is Nothing Xor currentValue Is Nothing Then
isCellModified = True
ElseIf Me._originalValue IsNot Nothing And currentValue IsNot Nothing Then
If Me._originalValue.ToString <> currentValue.ToString Then
isCellModified = True
End If
End If

If isCellModified Then
MyBase.OnCellValidating(e)
End If
End Sub

End Class

GrapeCityTool MultiRow5.0J ユーザによるセルの変更を判定するには

Ver4.0にはユーザによるセルの変更を判定するModifiedプロパティがありましたが
Ver5.0にはありません。

実現するにはCellValueChanged イベント を監視し、IsCurrentCellDirtyで変更がコミットされた場合にフラグを立てます。
CellValueChangedイベントはユーザによりセルの値が変更された場合に発生します。
Public ClassCustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _IsModified As Boolean

Public ReadOnly Property Modified() As Boolean
Get
Return Me._IsModified
End Get
End Property

Protected Overrides Sub OnCellValueChanged(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
If Me.IsCurrentCellDirty Then
Me._IsModified = True
End If
MyBase.OnCellValueChanged(e)
End Sub

End Class



しかし、ユーザー定義セルでOnCellFormattingメソッドで書式設定などを行うとCellValueChangedイベントが発生するため、
上記の方法ではユーザによるセルの値の変更を判定できません。
そのため、OnCellEnterメソッドで変更前の値を記憶しておき、OnCellEndEditで変更後の値と比較すると
ユーザによるセルの変更を判定できます。
Public ClassCustomMultiRowSheet
Inherits GrapeCity.Win.MultiRow.GcMultiRow

Private _OriginalValue As Object
Private _IsModified As Boolean

Public ReadOnly Property Modified() As Boolean
Get
Return Me._IsModified
End Get
End Property

Protected Overrides Sub OnCellEnter(ByVal e As GrapeCity.Win.MultiRow.CellEventArgs)
'変更前の値を記憶します。
Me._OriginalValue = Me.GetValue(e.RowIndex, e.CellIndex)
MyBase.OnCellEnter(e)
End Sub

Protected Overrides Sub OnCellEndEdit(ByVal e As GrapeCity.Win.MultiRow.CellEndEditEventArgs)
'OnCellEnterイベントで記憶した値と比較し、セルの値が変更されていれば
'ユーザがセルの値を変更したかどうかのフラグを立てます。
'すでにフラグがたっている場合は比較を行いません。
If Me._isModified = False Then
Dim currentValue As Object = Me.GetValue(e.RowIndex, e.CellIndex)
If Me._originalValue Is Nothing Xor currentValue Is Nothing Then
Me._isModified = True
ElseIf Me._originalValue IsNot Nothing And currentValue IsNot Nothing Then
If Me._originalValue.ToString <> currentValue.ToString Then
Me._isModified = True
End If
End If
End If

MyBase.OnCellEndEdit(e)

End Sub

End Class

2008年12月10日水曜日

GrapeCityTool MultiRow5.0J ユーザ定義セル

MultiRow5.0j 数値型セルぐらいは用意して欲しかったなぁ
それともInputManが抱き合わせで売れるようにワザと用意しないのかい?

InputManを買うお金もないので、数値型セルを自分で作りました。


数値型セルの要件


・マイナス値は赤字で表示。
・入力は数値のみ。貼り付けも数値のみ。
・最小値と最大値の範囲でのみ入力可。
・編集状態が終了したタイミングで3桁ごとにカンマ区切りにする。
・小数点以下桁数の設定。


準備


・ユーザー定義セルをツールボックスに登録するには、MultiRow for Windows Forms 5.0J Service Pack 1 (5.0.2008.0930)が必要です。
Sp1を適用していない場合は適用します。

・ヘルプの以下の箇所を読みましょう。
[製品の概要]
-[Service Pack 1 の変更点]
-[デザイナのツールボックスのカスタマイズ]
-[ユーザー定義セル]
-[デザイナのツールボックスに登録する]



ユーザ定義セルを作成するクラスライブラリの作成


以下に記述したカスタム編集エディタクラスとカスタムセルクラスは、クラスライブラリとして作成しますので
まずは新規プロジェクトでクラスライブラリを作成します。


数値型編集エディタクラスの作成


エディタクラスはカスタムコントロールの作成要領とほとんど同じです。
数値型編集エディタはGrapecity.Win.MultiRow.TextBoxEditingControlを継承して作りました。
「数値型セルの要件」で必要なプロパティなどを実装します。
KeyPressなどで数字以外の文字を弾く処理を記述します。
ペーストで数字以外を貼り付けできないよう処理を記述します。


数値型セルクラスの作成


数値型編集エディタクラスを編集エディタにもつ、数値型セルクラスです。

・Grapecity.Win.MultiRow.TextBoxCellを継承します。

・EditTypeプロパティで数値型編集エディタクラスのTypeオブジェクトを返します。
Public Overrides ReadOnly Property EditType() As System.Type
Get
Return GetType(CustomNemericTextBoxEditingControl)
End Get
End Property


・数値型編集エディタクラスに作成したカスタムプロパティは、数値型セルにも同じプロパティを用意します。

・セルのCloneメソッドでカスタムプロパティの値を複製します。
よくある質問:ID11562より
実行時に GcMultiRow コントロールに追加されるセルは、GcMultiRow.Template.Row.Cells プロパティのセルのインスタンスのクローンとなります。
このため、ユーザー定義型セルを作成してカスタムプロパティを実装している場合、Clone メソッドでプロパティの値を複製する必要があります。
Public Overrides Function Clone() As Object
Dim cell As CustomNumericTextBoxCell 
cell = DirectCast(MyBase.Clone, CustomNumericTextBoxCell )
cell.CustomProperty1 = Me._CustomProperty1 
Return cell
End Function


・InitializeEditingControlメソッドでエディタに設定する値やプロパティを設定します。
InitializeEditingControlメソッドはセルが編集状態になる場合に発生します。
このメソッドの引数ByVal formattedValue As Objectがエディタの値になります。
この値はセルに表示されている書式変換後の値なので、この値よりカンマ区切りなどの書式を除去したり、エディタに表示する値を加工したりします。
またここでセルに設定されているカスタムプロパティの値を、エディタの同プロパティに設定します。

・OnCellFormattingメソッドでセルに表示する値の書式を設定します。
OnCellFormattingメソッドは表示用の文字列に変換される際に発生します。
このメソッドの引数ByVal e As GrapeCity.Win.MultiRow.CellFormattingEventArgsのValueプロパティの値が、セルに表示される値になります。
この値に対して、カンマ区切りなどの書式設定やマイナス値の前景色の設定を行います。


数値型セルをツールボックスに登録する


・数値型セルを含むdllを「マイ ドキュメント\GrapeCity MultiRow\MultiRow 5.0 CellTypes」に配置します。
Windows Vista/2008 の場合、
C:\Users\<ユーザー名>\Documents\GrapeCity MultiRow\MultiRow 5.0 CellTypes
Windows 2000/XP/2003 の場合、
C:\Documents and Settings\<ユーザー名>\My Documents\GrapeCity MultiRow\MultiRow 5.0 CellTypes

・Visual Studio でプロジェクトに「MultiRow 5.0 テンプレート」を追加するか、または既存のテンプレートを開いてデザイナを表示する。
デザイナのツールボックスを右クリックし、「ユーザー定義セルを再読み込み」を実行する。
ツールボックスの「ユーザー定義セル」グループにユーザー定義セルが表示されます。


ユーザ定義セルのdll参照


ツールボックスに表示されたユーザ定義セルをドラッグ&ドロップでテンプレートを作成しビルドすると、
bin/debuまたはbin/releaseにユーザ定期セルdllがコピーされます。
もしdllに変更があったら、プロジェクトのbin配下のdllを手動で変更しなければなりません。
それは面倒なので、参照設定でユーザ定義セルdllの参照先を変更しておくことをお勧めします。


以上 間違っていたらご指摘ください。


MultiRow5.0Jの調査はまだまだ山積みです。
使用感としてはVer4.0より確実に面倒が増えたと感じます。
慣れとかではなく、あきらかにコード量が増えました。
Ver4.0はカスタムセルの作成など複雑なことはできなかったけれど、その分簡単でした。
Ver5.0では複雑なことができるようになったけれど、簡単だったことまでが複雑になった感じがします。

ですがその前に…
VerUpするなら互換性をキープしてほしい!!
まじで勘弁して欲しい。
あとヘルプ。
ユーザ定義セルをサポートしてるんだからInitializeEditingControlメソッドとかちゃんとヘルプに載せて下さい。

余談ですが
GcMultiRowクラス、その他編集エディタを使用するセルはすべてラップクラスを用意することをお勧めします。
出たばっかりですからね。やっぱ色々と細かい問題があります。

2008年12月5日金曜日

GrapeCityTool MultiRow5.0J 注意点

Enabled=Falseに設定するときの注意点


Enabled=Falseに設定しても、そのセルは選択できる。
セルを選択できないようにしたい場合は、Enabledプロパティ設定時にSelectableプロパティも設定する。


ボタンセルをEnabled=Falseに設定するときの注意点


Enabled=FalseかつSelectable=Falseにしているボタンセルで、セルをクリックすると
CellContentClickが発生する!Σ( ̄ロ ̄lll)
CellContentClickではそのセルのEnabledをチェックしてからボタンのクリック処理を記述すること。

.NET フォームを閉じるときにValidatingイベントを走らせないようにするには

Validatingイベントは
フォームの×ボタンをクリックしフォームを閉じようとしたり、
CausesValidatin=Falseを設定しているコんトロールであっても、
そのコントロールの処理内でMe.Closeを記載していたりすると、
Validatingイベントが走ってしまいます。

フォームを閉じるときにValidatingイベントを走らせないようにするには
Public Class Form1
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_CLOSE As Integer = &H10
Const WM_SYSCOMMAND As Integer = &H112
Const SC_CLOSE As Integer = &HF060

Select Case m.Msg
Case WM_SYSCOMMAND
If m.WParam.ToInt32() = SC_CLOSE Then
'Xボタン、コントロールメニューの「閉じる」、
'コントロールボックスのダブルクリック、
'Atl+F4などにより閉じられようとしている
'このときValidatingイベントを発生させない。
Me.AutoValidate = Windows.Forms.AutoValidate.Disable

End If
Case WM_CLOSE
'Application.Exit以外で閉じられようとしている
'このときValidatingイベントを発生させない。
Me.AutoValidate = Windows.Forms.AutoValidate.Disable
End Select

MyBase.WndProc(m)
End Sub
End Class

GrapeCityTool MultiRow5.0J 基本

セルに値を設定するには


Dim iCurRow As Integer = Me.GcMultiRowSheet.CurrentRow.Index
Dim value As Object = "XXX"
Me.GcMultiRowSheet.SetValue(iCurRow,"textBoxCell1", value )
※Itemプロパティを使用して値を設定するとパフォーマンスが悪い。
Me.GcMultiRowSheet.Item(iCurRow,"textBoxCell1").Value = value

セルの値を取得するには


Dim iCurRow As Integer = Me.GcMultiRowSheet.CurrentRow.Index
Dim value As Object = Me.GcMultiRowSheet.GetValue(iCurRow,"textBoxCell1")
※Itemプロパティを使用して値を設定するとパフォーマンスが悪い。
Dim value As Object = Me.GcMultiRowSheet.Item(iCurRow,"textBoxCell1").Value


ボタンセルの背景色を変更するには


ボタンセルのFlatStyleをSystem以外に設定する。
注意
FlatStyleをSystem以外に設定した場合、SelectionBackColor(選択色)をTransparentに設定しないと
ボタンセルにフォーカスした際に背景色が選択色(デフォルト紺)になってしまいます。


セルのイベント発生順序


1.以下の操作で発生するイベント
Enterキーで編集状態にする
セルに入力
Enterキーで編集状態を終了する
Tabキーでセル移動

CellEnter
CellBeginEdit
CellEditingControlShowing
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellEndEdit
CellLeave
CellValidating
CellValidated

2.以下の操作で発生するイベント
マウスダブルクリックで編集状態にする
セルに入力
マウスクリックでセル移動
CellEnter
CellBeginEdit
CellEditingControlShowing
CellLeave
CellValidating
CellParsing
CellValueChanged ※セルの値が変更された場合のみ発生する。
CellValidated
CellEndEdit

CellValueChanged以降のイベントでセルの入力値をGetValueで取得できます。


セルの値を検証するには


CellValidatingイベントを使用します。
入力値はe.FormattedValueを使用します。
※上記のイベント発生順序の2番ではGetValueメソッドを使用して入力値を取得できません。
入力値が異常でセルを移動させたくない場合はe.CancelプロパティにTrueを設定します。
注意
WindowsControlのValidating同様にフォームの×ボタンやMe.CloseでCellValidatingイベントが発生します。
フォームを閉じるときにValidatingイベントを走らせないようにするにはなどの対策が必要です。

2008年10月2日木曜日

.NET ストアドファンクションをキックするには?

ストアドプロシージャではなくストアドファンクションをキックする方法です。

Using cnn As DbConnection = 略
Using cmd As DbCommand = 略
cmd.Connection = cnn 
cmd.CommandType = CommandType.Text
cmd.CommandText = "BEGIN :nReturnValue := FuncAAA.FuncAAA(:nCd); END;"

Dim pRet As DbParameter = 略
pRet.ParameterName = "nReturnValue"
pRet.Direction = ParameterDirection.ReturnValue
pRet.DbType = DbType.Int32
cmd.Parameters.Add(pRet)

Dim pCd As DbParameter = 略
pCd .ParameterName = "nCd"
pCd .Value = 123
pCd .Direction = ParameterDirection.Input
pCd .DbType = DbType.Int32
cmd.Parameters.Add(pCd )

cmd.ExecuteNonQuery()

Console.WriteLine(pRet.Value.ToString)
End Using
End Using

.NET DateTimePickerで1桁入力しTabキーで移動すると入力値が取得できない

DateTimePickerで日付などを1桁入力し、Tabキーで移動するとDateTimePickerのValidatingイベント、Validatedイベントで入力値を取得することができません。

VS2005、VS2008で確認しました。
このような画面でDateTimePickerの日付部分に1桁「1」を入力し、Tabキーでコントロールを移動します。


Formには以下のようなコードを書きました。
Public Class Form1

Private Sub DateTimePicker1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.Leave
Console.WriteLine("Leave:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_Validated(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.Validated
Console.WriteLine("Validated:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles DateTimePicker1.Validating
Console.WriteLine("Validating:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.ValueChanged
Console.WriteLine("ValueChanged:" & Me.DateTimePicker1.Value.ToString)
End Sub

End Class


実行結果は以下のようになりました。
Leave:2008/10/02 8:54:24
Validating:2008/10/02 8:54:24
Validated:2008/10/02 8:54:24
ValueChanged:2008/10/01 8:54:24

これは年月日のいずれでも1桁入力し、Tabキーで移動した場合に発生します。

回避方法はLeaveイベントでMe.ActiveControl = Me.ActiveControlを記述します。
Public Class Form1

Private Sub DateTimePicker1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.Leave
     '1桁入力しTabキーで移動した際に入力値が取得できない問題を回避します。
     Me.ActiveControl = Me.ActiveControl
Console.WriteLine("Leave:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_Validated(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.Validated
Console.WriteLine("Validated:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles DateTimePicker1.Validating
Console.WriteLine("Validating:" & Me.DateTimePicker1.Value.ToString)
End Sub

Private Sub DateTimePicker1_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DateTimePicker1.ValueChanged
Console.WriteLine("ValueChanged:" & Me.DateTimePicker1.Value.ToString)
End Sub

End Class


実行結果は以下のようになりました。
ValueChanged:2008/10/01 9:12:40
Leave:2008/10/01 9:12:40
Validating:2008/10/01 9:12:40
Validated:2008/10/01 9:12:40

DateTimePickerを継承したカスタムコントロールを作成し、Leaveイベントで以下のようにしておくと便利かと思います。
Public Class CustomControl1
Inherits System.Windows.Forms.DateTimePicker

Protected Overrides Sub OnLeave(ByVal e As System.EventArgs)
Me.FindForm.ActiveControl = Me.FindForm.ActiveControl
MyBase.OnLeave(e)
End Sub
End Class

2008年9月4日木曜日

.NET カスタム構成セクションを作成する

方法 : ConfigurationSection を使用してカスタム構成セクションを作成する


アプリケーション構成ファイルに以下のようなカスタム構成セクションを定義し読み込みたいと思います。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<configSections>
<section name="customConfigSection"  type="CustomConfigSample.CustomConfig.CustomConfigSection, CustomConfigSample" />
</configSections>


<customConfigSection>
<childElemnts>
<add key="key1" value1="Value1_1" value2="Value2_1" />
<add key="key2" value1="Value1_2" value2="Value2_2" />
<add key="key3" value1="Value1_3"  />
</childElemnts >
</customConfigSection >

</system.diagnostics>
</configuration>


1、Windowsアプリケーション「CustomConfigSample」を作成します。

2、参照設定でSystem.configurationの参照を追加します。

3、カスタム構成セクションクラスを作成します。
Imports System
Imports System.Collections
Imports System.Text
Imports System.Configuration
Imports System.Xml

Namespace CustomConfig

''' <summary>
''' カスタムセクション
''' </summary>
''' <remarks></remarks>
Public Class CustomConfigSection
Inherits ConfigurationSection

''' <summary>
''' コンストラクタ
''' </summary>
''' <remarks></remarks>
Public Sub New()
End Sub

''' <summary>
''' 子要素を表すコレクションです。
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
<ConfigurationProperty("childElemnts")> _
Public Property ChildElemnts() As CustomConfigElementCollection
Get
Return CType(Me("childElemnts"), CustomConfigElementCollection)
End Get
Set(ByVal value As CustomConfigElementCollection)
Me("childElemnts") = CType(value, CustomConfigElementCollection)
End Set
End Property
End Class


End Namespace


3、カスタム構成セクションの子要素クラスを作成します。
Imports System
Imports System.Collections
Imports System.Text
Imports System.Configuration
Imports System.Xml

Namespace CustomConfig

''' <summary>
''' カスタムセクション子要素を定義するクラスです。
''' </summary>
''' <remarks></remarks>
Public Class CustomConfigElement
Inherits ConfigurationElement

''' <summary>
''' コンストラクタ
''' </summary>
''' <remarks></remarks>
Public Sub New()
End Sub

''' <summary>
''' キー
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
<ConfigurationProperty("key", IsRequired:=True)> _
Public Property Key() As String
Get
Return CStr(Me("key"))
End Get
Set(ByVal value As String)
Me("key") = value
End Set
End Property

''' <summary>
''' 値1
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
<ConfigurationProperty("value1", IsRequired:=True)> _
Public Property Value1() As String
Get
Return CStr(Me("value1"))
End Get
Set(ByVal value As String)
Me("value1") = value
End Set
End Property

''' <summary>
''' 値2
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
<ConfigurationProperty("value2", IsRequired:=False)> _
Public Property Value2() As String
Get
Return CStr(Me("value2"))
End Get
Set(ByVal value As String)
Me("value2") = value
End Set
End Property

End Class
End Namespace


4、カスタム構成セクションの子要素のコレクションクラスを作成します。
Imports System
Imports System.Collections
Imports System.Text
Imports System.Configuration
Imports System.Xml

Namespace CustomConfig

''' <summary>
''' CustomConfigElementのコレクションを扱うためのクラスです。
''' </summary>
''' <remarks></remarks>
Public Class CustomConfigElementCollection
Inherits ConfigurationElementCollection

''' <summary>
''' コンストラクタ
''' </summary>
''' <remarks></remarks>
Public Sub New()
End Sub

''' <summary>
''' 指定したインデックスのCustomConfigElementを取得します。
''' </summary>
''' <param name="index"></param>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Default Public Shadows Property Item(ByVal index As Integer) As CustomConfigElement
Get
Return CType(BaseGet(index), CustomConfigElement)
End Get
Set(ByVal value As CustomConfigElement)
If Not (BaseGet(index) Is Nothing) Then
BaseRemoveAt(index)
End If
BaseAdd(index, value)
End Set
End Property

''' <summary>
''' 指定した名称のCustomConfigElementを取得します。
''' </summary>
''' <param name="Name"></param>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Default Public Shadows ReadOnly Property Item(ByVal Name As String) As CustomConfigElement
Get
Return CType(BaseGet(Name), CustomConfigElement)
End Get
End Property

''' <summary>
''' 新しい ConfigurationElement を作成します。  
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Protected Overloads Overrides Function CreateNewElement() As ConfigurationElement
Return New CustomConfigElement()
End Function

''' <summary>
''' 指定したConfigurationElementを特定するキーを取得します。  
''' </summary>
''' <param name="element"></param>
''' <returns></returns>
''' <remarks></remarks>
Protected Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object
Return CType(element, CustomConfigElement).Key
End Function

End Class
End Namespace


5、アプリケーション構成ファイルのカスタムセクションをアプリケーションから読み込みます。
Public Class Main


Public Shared Sub Main()

Dim section As CustomConfig.CustomConfigSection
section = CType(System.Configuration.ConfigurationManager.GetSection("customConfigSection"), CustomConfig.CustomConfigSection)

Console.WriteLine("customConfigSectionの要素数={0}", section.ChildElemnts.Count)

Console.WriteLine("customConfigSectionの0番目要素のKey={0}", section.ChildElemnts.Item(0).Key)

Console.WriteLine("customConfigSectionの[key2]要素のValue1={0}", section.ChildElemnts.Item("key2").Value1)

End Sub

End Class

2008年8月28日木曜日

.NET VisualStudioからAccessを操作する際の注意点

VisualSutdioからAccessを操作する場合、知らないと色々とハマることが多いです。

1.DbParameterでAccessの時間を含む日付型フィールドのパラメータを作成できない

2.SQLのJOIN句には括弧が必要。
SELECT * FROM ((TABLE1
INNER JOIN TABLE2 ON TABLE1.CD = TABLE2.CD)
INNER JOIN TABLE3 ON TABLE1.CD = TABLE3.CD)

3.名前付きパラメータであっても、設定する順番が重要。疑問符(?)で指定する位置指定パラメータが適用される。
Dim sql As String = "SELECT * FROM TABLE1 WHERE CD=@CD AND NAME=@NAME"
Dim cmd AS DbCommand = 略
cmd.CommandText = sql

Dim prm1 As DbParameter = cmd.CreateParameter
prm1.ParameterName = "CD"

Dim prm2 As DbParameter = cmd.CreateParameter
prm2.ParameterName ="NAME"

'Sqlのパラメータの順序通りに追加する必要がある。
cmd.Parameters.Add(prm1)
cmd.Parameters.Add(prm2)
’順番を変えるとエラーになる。
cmd.Parameters.Add(prm2)
cmd.Parameters.Add(prm1)

.NET DbParameterでAccessの時間を含む日付型フィールドのパラメータを作成できない

Accessデータベースの日付型フィールドを、System.Data.Common.DbParameterで処理する際
DbTypeプロパティの設定に注意する必要があります。

日付型フィールドに時間を含まない場合、
DbParameterのDbTypeプロパティはDbType.Dateで問題なく動作しますが
日付型フィールドに時間を含む場合、
DbParameterのDbTypeプロパティをDbType.DateTimeにすると「OleDbException:抽出条件でデータ型が一致しません。」と例外が発生します。


DbParameterをOleDbParameterにキャストしOleDbType.Dateを指定しなければいけません。

Dim setting As ConnectionStringSettings
setting = ConfigurationManager.ConnectionStrings("MyAccessDb")

Dim factory As DbProviderFactory
factory = DbProviderFactories.GetFactory(setting.ProviderName)

Using cnn As DbConnection = factory.CreateConnection
cnn.ConnectionString = setting.ConnectionString
Using cmd As DbCommand = factory.CreateCommand
cmd.Connection = cnn
cmd.CommandText = "INSERT INTO MyTable (MyDate) Values (@MyDate)"
Dim prm As DbParameter = factory.CreateParameter
prm.ParameterName = "MyDate"
'時間を含む日付パラメータ
prm.DbType = DbType.DateTime
     If TypeOf (prm) Is OleDb.OleDbParameter AndAlso dbtype = Data.DbType.DateTime Then     
CType(prm, OleDb.OleDbParameter).OleDbType = OleDb.OleDbType.Date
End If
prm.Value = Me.DateTimePicker1.Value
cmd.Parameters.Add(prm)

cnn.Open()
Try
cmd.ExecuteNonQuery()
Finally
cnn.Close()
End Try
End Using
End Using

2008年8月20日水曜日

.NET DbConnection.GetSchemaメソッドでテーブル一覧を取得するには?

GetOleDbSchemaTable と Visual Basic .NET を使用してスキーマ情報を取得する方法

Accessデータベースのテーブル一覧を取得する方法。
Using cnn As DbConnection = 省略
cnn.Open()
Try
Dim dt As DataTable
dt = cnn.GetSchema("Tables", New String() {Nothing, Nothing, Nothing, "TABLE"})

For row As Integer = 0 To dt.Rows.Count - 1
For col As Integer = 0 To dt.Columns.Count - 1
Console.WriteLine("[{0},{1}]={2}", row, col, dt.Rows(row).Item(col).ToString)
Next
Next
Finally
cnn.Close()
End Try
End Using 'cnn

2008年8月18日月曜日

.NET Vista対応 その5 ビルド時にマニフェストファイルを埋め込む

Vista対応 その2 マニフェストファイルを使用するで「マニフェストファイルをプログラムファイルに埋め込む」方法を記載しました。
この方法以外にビルド時にビルド後アクションとしてマニフェストファイルを埋め込む方法を紹介します。

1、マニフェストを埋め込むWindowsアプリケーションプロジェクトに同名のマニフェストファイルを追加します。


2、Windowsアプリケーションのプロパティより「コンパイルタブ」の「ビルドイベント」ボタンをクリックします。


3、「ビルド後に実行するコマンドライン」にマニフェストファイルを埋め込むビルドアクションを設定します。


"$(DevEnvDir)..\..\SDK\v2.0\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest" -outputresource:"$(TargetDir)$(TargetFileName)";#1
"$(DevEnvDir)..\..\SDK\v2.0\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest" -outputresource:"$(ProjectDir)obj\$(ConfigurationName)\$(TargetFileName)";#1


※注意点
インストーラのプライマリ出力は\obj\[debug|release]\に作成されるexeを対象としてます。
\bin\[debug|release]\ではないので注意してください。
ビルド後イベントでは\obj\[debug|release]\に作成されるexeと、\bin\[debug|release]\に作成されるexeの両方にマニフェストファイルを埋め込んでいます。

2008年8月1日金曜日

.NET IntegerとLongの数値範囲

メモ
範囲
Int16-32,768 から +32,767 までの値の符号付き整数
Int32(Integer)-2,147,483,648 から +2,147,483,647 までの値の符号付き整数
Int64(Long)-9,223,372,036,854,775,808 から +9,223,372,036,854,775,807までの値の符号付き整数

2008年7月28日月曜日

.NET Vista対応 その4 任意のフォルダでファイルを共有する

UACはProgram FilesフォルダやWindowsフォルダへの書き込みを制限しています。
制限フォルダへの書き込みには管理者権限での許可が必要です。
Vista対応 その1

制限フォルダ以外の他のフォルダではUACはどのように機能するのでしょうか?
まずファイルの作成者は作成したファイルへの書き込み、読み込み、削除が可能です。
ファイルの作成者以外は読込みは可能ですが、書き込み・削除はUACによりリダイレクトされます。

具体例
1、ユーザAがC:/ProgramDataにファイルを作成します。

2、ユーザBがユーザAの作成したファイルを読み込みます。
読み込みは制限されていないのでC:/ProgramDataのファイルが読込まれます。

3、ユーザBがユーザAの作成したファイルに対して書き込みを行います。
UACによりC:/Users/ユーザB/AppData/Local/VirtualStore/ProgramDataにリダイレクトされます。

4、ユーザBが再度ユーザAの作成したファイルを読み込みます。
UACはリダイレクト先(C:/Users/ユーザB/AppData/Local/VirtualStore/ProgramData)に該当ファイルを探しに行き、3で作成されたファイルを読み込みます。

5、ユーザAがC:/ProgramDataのファイルを読み込みます。
もちろんユーザBが追記した内容は表示されません。


Vistaでは、基本的に他のユーザが作成したフォルダおよびファイルは閲覧のみで編集することができません。
他のユーザが作成したフォルダおよびファイルをすべてのユーザで共有するには、フォルダまたはファイルのアクセス権限を変更します。
すべてのユーザおよびネットワーク上のすべてのユーザで共有するには「Everyone」のアクセス権限を変更します。
ネットワーク上のユーザーと共有しない場合は「Users」のアクセス権限を変更します。



フォルダまたはファイルのアクセス権限を手動で変更するには



■方法その1
1、共有したいフォルダまたはファイルを右クリックし「共有」をクリックします。
2、グループ名またはユーザー名を入力します。
3、アクセス許可レベルを「閲覧者」、「投稿者」、「共同所有者」のいずれかを設定します。

閲覧者:共有ファイルの表示が可能。追加、変更、削除はできない
投稿者:共有ファイルの表示、追加が可能。自分が投稿したファイルのみ変更または削除できる
共同所有者:共有ファイルの表示、追加、変更、削除が可能


■方法その2
1、共有したいフォルダまたはファイルを右クリックし「プロパティ」をクリックします。
2、共有タブを選択し、「詳細な共有」ボタンをクリックします。
3、「このフォルダを共有する」チェックボックスをチェックONにし、「アクセス許可」ボタンをクリックします。
4、「追加」ボタンをクリックし、グループ名またはユーザ名を追加します。
3、アクセス許可レベルを「読取」、「変更」、「フルコントロールを適宜チェックONに設定します。



フォルダまたはファイルのアクセス権限をプログラムから変更するには



■ファイルのアクセス権限を変更する
Imports System.IO
Imports System.Security.AccessControl

Public Class Form1
'「C:/ProgramData/Test.txt」ファイルのアクセス権限を変更します。
'「Users」グループに「フルコントロール」を設定します。
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click
Try
Dim fileName As String _
= System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Test.txt")

AddFileSecurity(fileName, "Users", FileSystemRights.FullControl, AccessControlType.Allow)

Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub

Private Sub AddFileSecurity(ByVal fileName As String, _
ByVal account As String, _
                            ByVal rights As FileSystemRights, _
ByVal controlType As AccessControlType)

Dim fSecurity As FileSecurity = File.GetAccessControl(fileName)

Dim accessRule As New FileSystemAccessRule(account, rights, controlType)

fSecurity.AddAccessRule(accessRule)

File.SetAccessControl(fileName, fSecurity)

End Sub

End Class


■フォルダのアクセス権限を変更する

Imports System.IO
Imports System.Security.AccessControl

Public Class Form1
'「C:/ProgramData/Test」フォルダのアクセス権限を変更します。
'「Users」グループに「フルコントロール」を設定します。
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click
Try
Dim dirName As String _
= System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "User")

AddDirectorySecurity(dirName, "Users", FileSystemRights.FullControl, AccessControlType.Allow)

Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub

Private Sub AddDirectorySecurity(ByVal DirName As String, _
ByVal Account As String, _
ByVal Rights As FileSystemRights, _
ByVal ControlType As AccessControlType)

Dim dirInfo As New DirectoryInfo(DirName)

Dim dirSecurity As DirectorySecurity = dirInfo.GetAccessControl()

'このフォルダ、サブフォルダおよびファイルにアクセス権限を適用
Dim rule As New FileSystemAccessRule(Account, _
Rights, _
InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, _
PropagationFlags.None, _
AccessControlType.Allow)

dirSecurity.AddAccessRule(rule)

dirInfo.SetAccessControl(dirSecurity)

End Sub

End Class

※FileSystemAccessRuleコンストラクタのInheritanceFlags、PropagationFlags引数は、設定するアクセス権限の適用範囲に応じて適宜変更します。
適用先InheritanceFlagsPropagationFlags
このフォルダのみNoneNone
このフォルダとファイルObjectInheritNone
このフォルダとサブフォルダContainerInheritNone
このフォルダ、サブフォルダ、およびファイルContainerInherit | ObjectInheritNone
ファイルのみObjectInheritInheritOnly
サブフォルダのみContainerInheritInheritOnly
サブフォルダとファイルのみContainerInherit | ObjectInheritInheritOnly

適用先の確認は、アクセス権限を設定したフォルダを右クリックし、プロパティをクリックします。
セキュリティタブを選択し、「詳細設定」ボタンをクリックし、表示されたウィンドウで確認できます。



インストーラでフォルダを作成しアクセス権限を変更するには


インストーラでCommonApplicationDataにアプリケーション用データフォルダを作成し、アクセス権限を適宜設定します。アンインストール時に作成したフォルダは削除されます。
CommonApplicationDataはVistaでは「C:/ProgramData」、XPでは「C:\Documents and Settings\All Users\Application Data」となります。


CommonApplicationDataに「Test」フォルダを作成し、そのサブフォルダとファイルのアクセス権限を「Usersグループ、フルコントロール」に設定します。

1、セットアッププロジェクトのファイルシステムエディタで「対象コンピュータ上のファイルシステム」を右クリックし「特殊フォルダの追加」で「カスタムフォルダ」を追加します。
2、「カスタムフォルダ」を選択し、DefaultLocationプロパティを「[CommonAppDataFolder]」とします。
3、そこにさらにサブフォルダを追加し、Nameプロパティを「Test」、AlwaysCreateプロパティを「True」に設定します。
4、以下のクラスを定義したクラスライブラリをソリューションに追加します。
5、セットアッププロジェクトのファイルシステムエディタで、アプリケーションフォルダに4で作成したクラスライブラリのプライマリ出力を追加します。
6、カスタム動作の確定ノードに5で作成したクラスライブラリのプライマリ出力を追加します。
5、セットアッププロジェクトをビルドし、作成したmsiをOrcaで編集します。
6、CustomActionテーブルのActionフィールドが「xxxx.commit」のデータのTypeフィールド値に2048を加算します。
  インストーラは管理者権限で実行されるが、カスタム動作に管理者権限が与えられないため、Orcaで管理者権限を与えます。

Imports System.IO
Imports System.Security.AccessControl

Public Class Installer1
Inherits System.Configuration.Install.Installer

Public Overrides Sub Commit(ByVal savedState As System.Collections.IDictionary)

Dim dirName As String _
= System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Test")     

Dim dirInfo As New DirectoryInfo(DirName)

Dim dirSecurity As DirectorySecurity = dirInfo.GetAccessControl()

'サブフォルダとファイルのみにアクセス権限を適用
Dim rule As New FileSystemAccessRule("Users", _
FileSystemRights.FullControl, _
InheritanceFlags.ContainerInherit Or InheritanceFlags.ObjectInherit, _
PropagationFlags.InheritOnly, _
AccessControlType.Allow)

dirSecurity.AddAccessRule(rule)

dirInfo.SetAccessControl(dirSecurity)

MyBase.Commit(savedState)
End Sub
End Class

2008年7月25日金曜日

.NET Vista対応 その3 パブリックフォルダでファイルを共有する

Windows Vista ヘルプ:パブリックフォルダを使用したファイルの共有によると
パブリックフォルダ内のファイルは、同じコンピュータを使用している他のユーザーや、同じネットワーク上で別のコンピュータを使用しているユーザーと共有できます。
パブリック フォルダ内に配置したファイルまたはフォルダは、そのパブリック フォルダにアクセスできるユーザーと自動的に共有されます。
とあります。

パブリックフォルダとは、Vistaでは「C:/ユーザ/パブリック」、XPでは「C:/Documents and Settings/All Users」にあたります。

VisualStudioで特殊フォルダのパスを取得するSystem.EnvironmentクラスのGetFolderPathメソッドでは、このパブリックフォルダのパスは取得できません。

方法その1:APIを使用してパブリックフォルダのパスを取得する
パブリックフォルダのパスを取得するにはAPI「SHGetSpecialFolderPath」を使用して「CSIDL_COMMON_DOCUMENTS」のパスを取得するようにします。
Public Class Form1

Private Const CSIDL_COMMON_DOCUMENTS As Int32= &H2E


Private Declare Function SHGetSpecialFolderPath Lib "shell32.dll" Alias "SHGetSpecialFolderPathA" _
(ByVal hwndOwner As IntPtr, ByVal lpszpath As System.Text.StringBuilder, ByVal nfolder As Int32, ByVal fcreate As Int32) As Int32


Function GetSpecialPath(ByVal nfolder As Int32) As String
Dim resultPath As New System.Text.StringBuilder(255)
SHGetSpecialFolderPath(Nothing, resultPath, nfolder, 0)
Return resultPath.ToString()
End Function

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MessageBox.Show(GetSpecialPath(CSIDL_COMMON_DOCUMENTS))
End Sub

End Class


Vistaでは「C:/Users/Public/Documents」、XPでは「C:/Documents and Settings/All Users/Documents」が取得できます。


方法その2:レジストリを使用してパブリックフォルダのパスを取得する
Public Class Form2

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim allUsersDocuments As String = ""
Dim regKey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders", False)
Try
allUsersDocuments = regKey.GetValue("Common Documents", "").ToString
Finally
regKey.Close()
End Try
MessageBox.Show(allUsersDocuments)
End Sub

End Class


こちらもVistaでは「C:/Users/Public/Documents」、XPでは「C:/Documents and Settings/All Users/Documents」が取得できます。

.NET Vista コントロールのフォーカス枠が表示されない

Formにコントロールを配置しVistaで実行すると、コントロールにFocusが当たっているにもかかわらず、
フォーカス枠(点線の矩形)が表示されません。
コードでコントロール.Focus()としてもフォーカス枠は表示されません。

タブキーで移動するとフォーカス枠は表示されるようになり、一度フォーカス枠が表示されると、その後は
コードでコントロール.Focus()としてもフォーカス枠が表示されます。

対応方法は
「コントロールパネル」→「コンピュータの簡単操作」→「キーボードを使いやすくします」で
「ショートカットキーとアクセスキーに下線を表示します。」をチェックONにします。

2008年7月18日金曜日

.NET Vista対応 その2 マニフェストファイルを使用する

Vista対応 その1

マニフェストファイルを作成します


WindowsApplication1.exeというプログラムがあるとしたら、マニフェストはWindowsApplication1.exe.manifestという名前
でプログラムと同じフォルダに配置します。

マニフェストファイルの内容は



Description of your application











3行目のプログラム名と11行目の権限レベルを指定します。

権限レベルは以下の3種です。
AsInvoker:制限された権限で動作可能
highestAvailable:ユーザーが持つ制限されない権限が必要
requireAdministrator:管理者権限が必要

1、マニフェストファイルがない場合、または権限レベルが指定されていない場合
UACの制御により、「%SystemRoot%」,「%ProgramData%」,「%ProgramFiles%」フォルダへの書き込み、レジストリ・キー「HKEY_LOCAL_MACHINE\Software」への書き込みはリダイレクト先に書き込まれます。

2、マニフェストファイルがあり、かつ権限レベルが指定されている場合
UACの制御によりリダイレクトは行われません。

3、マニフェストファイルの権限レベルがAsInvekerの場合
UACの制御により、「%SystemRoot%」,「%ProgramData%」,「%ProgramFiles%」フォルダへの書き込み、レジストリ・キー「HKEY_LOCAL_MACHINE\Software」への書き込みはリダイレクトされず、書き込みに失敗します。

4、マニフェストファイルの権限レベルがhighestAvailable、requireAdministratorの場合
UACの制御により、「%SystemRoot%」,「%ProgramData%」,「%ProgramFiles%」フォルダへの書き込み、レジストリ・キー「HKEY_LOCAL_MACHINE\Software」への書き込みはリダイレクトされず、UAC 昇格確認ダイアログが表示され書き込みを行えます。
また権限の昇格を行わないと実行できないマークである「シールドアイコン」が自動でアプリケーションアイコンに表示されます。

5、その他
基本的には requestedExecutionLevel 要素の level 属性値が優先されますが、この要素記述が無い場合 「install」、「setup」、「update」 などのキーワードがファイル名に含まれるとインストーラと判断され、UAC 昇格確認ダイアログが表示されます。


※ちなみにuiAccessにTrue を指定するには、Microsoftへの申請が必要です。ユーザー補助アプリケーションのみが対象となっています。


マニフェストファイルをプログラムファイルに埋め込む


1.VSを起動し「ファイルを開く」で組み込み先のEXEファイルを選択します。


2.EXEファイルを右クリックして「リソースの追加」を選択します。


3.「インポート」をクリックし、作成しておいたマニフェストファイルを選択し、リソースの種類を「RT_MANIFEST」と入力します。


4.IDを101から1に変更し、保存します。


※ちなみに外部マニフェストと埋め込みマニフェストが共にある場合
Windows XP では、外部マニフェストを使用します。
Windows Server 2003 およびそれ以降のバージョンの Windows では、埋め込みマニフェストが存在する場合はそれが使用され、外部マニフェストは無視されます。

.NET Vista対応 その1

ユーザーアカウント制御(UAC:User Account Control)について


Vistaには、“管理者”と“標準ユーザ”が存在し、UACではたとえ管理者権限でログインしていても標準ユーザ権限しか与えられていません。
そのためアプリケーションのインストールやシステム設定の変更を行うプログラムを実行するたびに警告画面が表示され、ユーザーの許可を求めるようになります。
このとき管理者権限でログインしていれば許可することで権限が管理者に昇格し、プログラムを実行できます。
標準ユーザでログインしていれば、管理者ユーザのパスワードを求められます。
これにより、ユーザーが知らない間に悪意のあるソフトウェアをインストールされたり、システム設定を変更されることを防止できるようになります。


既存アプリの影響


Vistaでは,「%ProgramFiles%」「%WinDir%」フォルダへの書き込み、レジストリ・キー「HKEY_LOCAL_MACHINE\Software」への書き込みを制限しています。
既存アプリで上記のフォルダやレジストリに書き込みをしているアプリケーションは注意が必要です。

UACでは制限フォルダへの書き込みを行っているプログラムがVistaでも正常に動作するように、別の安全なフォルダへリダイレクトし、そのフォルダへ書き込みを行います。
「%ProgramFiles%」「%WinDir%」へのアクセスは、「%LoadlAppData%\VirtualStore」にリダイレクトされます。
レジストリ「HKEY_LOCAL_MACHINE\Software」以下へのアクセスは,「HKEY_CURRENT_USER\Software\Classes\VirtualStore\Machine\Software」にリダイレクトされます。

制限フォルダから読み込みは、UACにより制限されていませんが、
UACはまずリダイレクト先を見に行き、該当ファイルがある場合はそのファイルを読み込みます。
リダイレクト先に該当ファイルがない場合は、制限フォルダから該当ファイルを読み込みます。


具体例
1、
ユーザAがC:/Program Filesにファイルを作成し書き込みを行う。
実際にはリダイレクト先(C:/ユーザー/ユーザA/AppData/Local/VirtualStore/Program Files)にファイルが作成される。

2、
ユーザAがC:/Program Filesから1で作成したファイルを読み込む。
実際にはリダイレクト先(C:/ユーザー/ユーザA/AppData/Local/VirtualStore/Program Files)よりファイルが読み込まれる。

ここで「一見」正常に動作しているように思えてしまいます。
ところが

3、
ユーザBがC:/Program FilesからユーザAが1で作った(つもり)のファイルを読み込もうとします。
まずリダイレクト先(C:/ユーザー/ユーザB/AppData/Local/VirtualStore/Program Files)にファイルを探しに行きます。
リダイレクト先にはファイルはありませんので、C:/Program Filesを探しに行きますが、もちろんここにもファイルはありません。


問題が起こるのはユーザ間でファイルを共有する場合です。


UACの対応


■対応方法その1 UACを無効にしてしまう
かなり強硬手段ですが、UACの機能を無効にしてしまえばこんな問題も起こらないわけです。
制限フォルダへの書き込みも自由です。お勧めはしませんが。

UAC機能はデフォルトではオンになっています。
これを無効にするには、[コントロール パネル]で[ユーザー アカウント]を表示し「ユーザーアカウントの有効化または無効化」をクリックします。
「ユーザーアカウント制御(UAC)を使ってコンピュータの保護に役立たせる」のチェックをOFFにします。


■対応方法その2 プログラムのプロパティでに管理者権限を与える
制限フォルダへの書き込みは、管理者権限での許可が必要です。
まずプログラム自体に「管理者権限が必要ですよ」と設定します。

プログラムのexeのプロパティより「互換性」タブの「特権レベル 管理者としてこのプログラムを実行」をチェックONにします。

プログラムを実行すると、管理者権限でログインしている場合は許可を求めるダイアログが表示されます。
標準ユーザでログインしている場合は、管理者アカウントのパスワードを求められます。


■対応方法その3 マニフェストファイルを使用する
方法その2の代わりにマニフェストとよばれる外部ファイルで「管理者権限が必要ですよ」と設定します。
Vista対応 その2 マニフェストファイルを使用するには?
Vista対応 その5 ビルド時にマニフェストファイルを埋め込む


プログラムを実行すると、管理者権限でログインしている場合は許可を求めるダイアログが表示されます。
標準ユーザでログインしている場合は、管理者アカウントのパスワードを求められます。



ここでもまた問題が発生します。
管理者権限ユーザと標準ユーザがファイルを共有するには、%Program Files%といった制限フォルダは使えません。
標準ユーザが制限フォルダに書き込むためには管理者権限アカウントのパスワードを求められるからです。



すべてのユーザで共有するファイルの保存場所


Vistaではすべてのユーザで共有するファイルはどこに保存すればいいのでしょうか?

■対応方法その1 仕様を見直す
そもそもすべてのユーザで共有しなければ、なにも問題はおこらないのです。
可能なら仕様を見直しましょう。



■対応方法その2 パブリックフォルダでファイルを共有する
Vistaでは「C:/ユーザ/パブリック」フォルダというファイルを共有するためのフォルダが用意されています。
Vista対応 その3 パブリックフォルダでファイルを共有する


■対応方法その3 任意のフォルダでファイルを共有する
たとえばCommonApplicationDataフォルダ(Vistaでは「C:/ProgramData」、XPでは「C:\Documents and Settings\All Users\Application Data」)でファイルを共有します。
Vista対応 その4 任意のフォルダでファイルを共有する


許可ダイアログの表示を最小限にする


基本的にはインストーラを除くプログラムから制限フォルダへの書き込みは行わないようにしますが、どうしても制限フォルダに書き込みを行いたい場合があります。
制限フォルダに書き込みを行う場合、埋め込みマニフェストを使用して管理者権限で実行することを明示しますが、かならず許可を求めるダイアログが表示されます。

このような場合は制限フォルダへの書き込み部分を別のexeとし、このexeに対してのみ管理者権限用マニフェストを埋め込みます。他のexeは一般権限用マニフェストを埋め込みます。こうすることで許可ダイアログの表示を最小限に抑えることができます。


Vista対応 まとめ


1、プログラムは埋め込みマニフェストを使用する。
2、管理者権限を必要とするプログラムは、別のプログラムにわけることで許可ダイアログの表示を最小限にする。
3、すべてのユーザで共有するファイルがある場合、インストーラでCommonApplicationDataフォルダにアプリケーション用のデータフォルダを作成し、カスタム動作でアクセス権限を変更する。

.NET 厳密名の付いたアセンブリをデバッグ実行してもブレークポイントに止まらない

厳密名の付いたアセンブリをデバッグ実行してもブレークポイントに止まりません。
プロジェクトのプロパティで「アセンブリの署名」をチェックOFFにしてデバッグ実行するとブレークポイントに止まります。


2008年6月20日金曜日

.NET 列挙体に属性を付けるには?

名前属性を作成します。
Imports System.Reflection
 _
Friend Class NameAttribute
Inherits Attribute

Private _name As String

Public Property Name() As String
Get
Return Me._name
End Get
Private Set(ByVal value As String)
Me._name = value
End Set
End Property

Public Sub New(ByVal name As String)
Me.Name = name
End Sub

Public Shared Function GetName(ByVal enumValue As [Enum]) As String
Dim enumType As Type = enumValue.[GetType]()
Dim enumName As String = [Enum].GetName(enumType, enumValue)
If enumName Is Nothing Then
Return Nothing
Else
Return GetName(enumType.GetField(enumName))
End If
End Function

Private Shared Function GetName(ByVal type As MemberInfo) As String
Dim attributes As Attribute()
attributes = TryCast(type.GetCustomAttributes(GetType(NameAttribute), True), Attribute())
If attributes Is Nothing OrElse attributes.Length = 0 Then
Return Nothing
End If
Dim nameAttribute As NameAttribute = TryCast(attributes(0), NameAttribute)
Return nameAttribute.Name
End Function

End Class


別名属性を作成します。
Imports System.Reflection
 _
Friend Class AliasNameAttribute
Inherits Attribute

Private _aliasName As String

Public Property AliasName() As String
Get
Return Me._aliasName
End Get
Private Set(ByVal value As String)
Me._aliasName = value
End Set
End Property

Public Sub New(ByVal aliasName As String)
Me.AliasName = aliasName
End Sub

Public Shared Function GetAliasName(ByVal enumValue As [Enum]) As String
Dim enumType As Type = enumValue.GetType
Dim enumName As String = [Enum].GetName(enumType, enumValue)
If enumName Is Nothing Then
Return Nothing
Else
Return GetAliasName(enumType.GetField(enumName))
End If
End Function

Private Shared Function GetAliasName(ByVal type As MemberInfo) As String
Dim attributes As Attribute()
attributes = TryCast(type.GetCustomAttributes(GetType(AliasNameAttribute), True), Attribute())
If attributes Is Nothing OrElse attributes.Length = 0 Then
Return Nothing
End If
Dim nameAttribute As AliasNameAttribute = TryCast(attributes(0), AliasNameAttribute)
Return nameAttribute.AliasName
End Function

End Class


列挙体に名前と別名の属性を付けます。
Public Enum DayOfWeek As Integer
<NameAttribute("日曜日"), AliasNameAttribute("日")> _
Sun = 0
<NameAttribute("月曜日"), AliasNameAttribute("月")> _
Mon = 1
<NameAttribute("火曜日"), AliasNameAttribute("火")> _
Tue = 2
<NameAttribute("水曜日"), AliasNameAttribute("水")> _
Wed = 3
<NameAttribute("木曜日"), AliasNameAttribute("木")> _
Thu = 4
<NameAttribute("金曜日"), AliasNameAttribute("金")> _
Fri = 5
<NameAttribute("土曜日"), AliasNameAttribute("土")> _
Sat = 6
End Enum


列挙体の属性を取得するには以下のコードを書きます。
Dim dayOfWeek As DayOfWeek = DayOfWeek.Wed 

Dim name As String
name = NameAttribute.GetName(CType(dayOfWeek , [Enum]))

Dim alias As String
alias = AliasNameAttribute.GetAliasName(CType(dayOfWeek , [Enum]))

2008年6月18日水曜日

.NET System.Typeオブジェクトよりバージョン情報を取得するには

Public Sub GetFileVersionInfo(ByVal type As System.Type)
Dim ver As System.Diagnostics.FileVersionInfo
ver = System.Diagnostics.FileVersionInfo.GetVersionInfo(type.Assembly.Location)
Dim manufacturer As String = ver.CompanyName
Dim productName As String = ver.ProductName
End Sub

2008年6月17日火曜日

.NET セットアッププロジェクト インストール完了後にプログラムを実行するには

「チェックボックス」ダイアログを使用して、インストール終了後にプログラムを実行するかを選択し
チェックON時のみプログラムを実行する方法です。

まずユーザーインターフェースエディタで終了ノードに「チェックボックス(A)」ダイアログを追加します。



次にカスタムアクションとして実行するVBScript「CustomAction.vbs」を用意します。
Dim WshShell
Dim path, manufacturer, productName

if Session.Property("CHECKBOXA1") then
manufacturer = Session.Property("Manufacturer")
productName = Session.Property("ProductName")
path = """" & Session.Property("TARGETDIR") & "WindowsApplication1.exe " & """"  & " " & manufacturer & " " & productName 
set WshShell = CreateObject("WScript.Shell")
WshShell.Run path
end if

※パスの指定方法
 コマンドライン引数がない場合
 path = """" & Session.Property("TARGETDIR") & "WindowsApplication1.exe " & """"
 コマンドライン引数がある場合
 path = """" & Session.Property("TARGETDIR") & "WindowsApplication1.exe " & """" & " " & 引数1 & " " & 引数2


作成したカスタムアクション「CustomAction.vbs」ファイルをアプリケーションフォルダに配置します。


セットアッププロジェクトをビルドし、MSIファイルを作成します。

作成したMSIファイルをORCAで開きます。
「File]」テーブルの「FileName」フィールドが「CustomAction.vbs」のデータを探します。
該当データの「File」フィールド値をコピーします。
※「File」フィールド値が「_77760E20AE324214B847ADADACA85FA6」であるとします。


「CustomAction」テーブルに以下の行を追加します。
ActionTypeSourceTarget
AC122_77760E20AE324214B847ADADACA85FA6

ActionフィールドはCustomActionテーブル内で一意になるよう名前をつけます。
TypeフィールドはVBScriptファイルである定数値22を指定します。
その他の定数値はCustom Action Typesを参照してください。
Sourceフィールドは先程の「CustomAction.vbs」のFileフィールド値を指定します。


「ControlEvent」テーブルに以下の行を追加します。
Dialog_Control_EventArgumentConditionOrdering
FinishedFormCloseButtonDoActionAC111



以上でORCAでの編集は終了です。

作成したMSIファイルでインストールを実行し、チェックボックスダイアログでチェックONした時のみプログラムが実行される事を確認してください。

.NET セットアッププロジェクト すべてのユーザをデフォルトにするには?

msiファイルをORCAで編集します。
Propertyテーブルの「FolderForm_AllUsers」のValueを「ME」から「ALL」に変更します。

2008年6月13日金曜日

.NET 文字列を暗号化するには?(DES暗号化)

System.Security.Cryptography.DESCryptoServiceProvider クラスを使用し、DES (Data Encryption Standard) アルゴリズムにより文字列の暗号化、復号化を行います。

''' <summary>
''' 文字列を暗号化します。
''' </summary>
''' <param name="value">暗号化する文字列</param>
''' <param name="key">暗号キー</param>
''' <returns>暗号化後の文字列</returns>
''' <remarks></remarks>
Public Shared Function Encrypt(ByVal value As String, ByVal key As String) As String

'キーをバイト型配にし、8バイトにリサイズします。
Dim bKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
Array.Resize(bKey, 8)

'DES暗号化プロバイダオブジェクトを作成します。
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
des.Key = bKey
des.IV = bKey

'暗号化されたデータを書き出すためのメモリーストリームオブジェクトを作成します。
Using memStream As New System.IO.MemoryStream()

'データ ストリームを暗号変換にリンクするストリームを定義します。 
Using cryptStream As New System.Security.Cryptography.CryptoStream( _
memStream, des.CreateEncryptor, _
System.Security.Cryptography.CryptoStreamMode.Write)

'書き込むためのストリームライターを作成します。
Using sw As New System.IO.StreamWriter(cryptStream)

'暗号化前の文字列を書き込みます。
sw.WriteLine(value)

sw.Close()
cryptStream.Close()
End Using 'sw
End Using 'cryptStream

'メモリーストリームから暗号化後のバイト配列を取得します。
Dim bRet As Byte() = memStream.ToArray()

memStream.Close()

'Base64で文字列に変更し返します。
Return System.Convert.ToBase64String(bRet)

End Using 'memStream

End Function

''' <summary>
''' 文字列を復号化します。
''' </summary>
''' <param name="value">復号化する文字列</param>
''' <param name="key">暗号キー</param>
''' <returns>復号化後の文字列</returns>
''' <remarks></remarks>
Public Shared Function Decrypt(ByVal value As String, ByVal key As String) As String

'キーをバイト型配にし、8バイトにリサイズします。
Dim bKey As Byte() = System.Text.Encoding.UTF8.GetBytes(key)
Array.Resize(bKey, 8)

'DES暗号化プロバイダオブジェクトを作成します。
Dim des As New System.Security.Cryptography.DESCryptoServiceProvider
des.Key = bKey
des.IV = bKey

'Base64で文字列をバイト配列に戻します。
Dim bValue As Byte() = System.Convert.FromBase64String(value)

'暗号化されたデータを読み込むためのメモリーストリームオブジェクトを作成します。
Using memStream As New System.IO.MemoryStream(bValue)

'データ ストリームを暗号変換にリンクするストリームを定義します。 
Using cryptStream As New System.Security.Cryptography.CryptoStream( _
memStream, des.CreateDecryptor, _
System.Security.Cryptography.CryptoStreamMode.Read)

'読み込むためのストリームリーダーオブジェクトを作成します。
Using sr As New System.IO.StreamReader(cryptStream, _
System.Text.Encoding.UTF8)

'メモリーストリームから復号化後のバイト配列を取得します。
Dim vRet As String = sr.ReadLine

'ストリームを閉じます。
sr.Close()
cryptStream.Close()
memStream.Close()

Return vRet

End Using
End Using 'cryptStream

End Using 'memStream
End Function




他にもSystem.Security.Cryptography名前空間には暗号化複号化を行うクラスとして、以下のものがあります。いずれも使い方はほとんど同じです。


  • DESCryptoServiceProvider
  • RC2CryptoServiceProvider
  • RijndaelManaged
  • TripleDESCryptoServiceProvider

.NET Orcaの使用方法

すべてのプログラムよりOrcaを起動します。
「File」→「Open」より編集するmsiを選択します。

編集後は「File」→「Save」で保存し、Orcaを終了してください。
終了せずにインストールを行うと「インストールパッケージを開くことができませんでした。」とエラーメッセージが表示されます。


ダイアログのボタンを無効にするには?


「Control」テーブルより無効にしたいボタンを探し、Atributesフィールドを「1」にします。
有効にしたい場合はAtributesフィールドを「3」にします。



ダイアログに表示されているテキストを変更するには?


「Control」テーブルより変更したいテキストを探し、Textフィールドを変更します。



ダイアログの×ボタンでインストール中止メッセージを表示しないようにするには?


あるダイアログのキャンセルボタンを無効にしても、ダイアログの×ボタンをクリックすると「インストールを中止しますか?」とメッセージが表示されてしまいます。
×ボタンでメッセージを表示せずにダイアログを閉じるには
「ControlEvent」テーブルより
「Dialog_」フィールドが変更したいダイアログで「Control_」フィールドが「CanncelButton」のデータを探し、下記のように変更します。

Dialog_Control_EventArgumentConditionOrdering
変更したいダイアログCanncelButtonEndDialogReturn0

.NET インストーラでアンインストールに失敗した場合、再インストール出来るようにするには?

アンインストール時にカスタム動作を設定したセットアッププロジェクトでインストールを行いました。
が、アンインストール時のカスタム動作で失敗してしまい、アンインストールできません・・・。
アンインストール出来なければ、再インストールもできません。

こんなたときはレジストリを操作することで再インストールが可能になります。

「すべてのユーザ」でインストールした場合はHKEY_CLASSES_ROOT\Installer\Products以下のサブキー
「このユーザのみ」でインストールした場合はHKEY_CURRENT_USER\Software\Microsoft\Installer\Products以下のサブキーを一つ一つ確認しそのサブキー内のProductsNameキーを確認して、該当するものをサブキーごと削除します。


これで再インストールが可能になります。

2008年6月12日木曜日

.NET Image画像の上に配置したLabelの背景を透過させるには?

Image画像の上に配置したLabelのBackColorプロパティをTransparentに設定しても
そのままではLabelの背景は透過されません。
これはLabelの親コントロールがFormになるため、ラベルの背景色がFormの背景色になるためです。
Labelの背景色を透過するためには、Labelの親コントロールをImageコントロールに変更します。

LabelコントロールのBackColorプロパティをTransparentに設定し
FormのコンストラクタでLabelの親コントロールを変更します。
Public Sub New()
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent()

' InitializeComponent() 呼び出しの後で初期化を追加します。
Me.Controls.RemoveByKey(Me.Label1e.Name)
Me.PictureBox1.Controls.Add(Me.Lable1)

End Sub

.NET msi編集ツールOrca

Windows インストーラ ツールの制限により、Windows インストーラ パッケージ (.msi) ファイルを直接編集する必要がある場合があります。Orca エディタでは、マージ モジュール (.msm) ファイル、内部整合性検証 (.cub) ファイル、およびパッチ作成ファイル (.pcp) の編集と作成を行うことができます。


Orca のインストールと実行



Orcaは単体では配布されていません。OrcaはPlatform SDKに含まれています。
まずWindows Installer SDK を次のページからダウンロードしてインストールします。
Windows SDK for Windows Server 2008 and .NET Framework 3.5


インストールしたフォルダにある Orca.msi を実行しインストールします。(デフォルトではC:\Program Files\Microsoft SDK\Bin\Orca.Msi)

すべてのプログラムより[Orca] をクリックし実行します。
[File] メニューの [Open] をクリックし、編集する .msi ファイルを指定します。

2008年6月10日火曜日

.NET セットアッププロジェクト カスタム動作

Installクラスを使用したdllファイルのカスタムアクションを定義します。

前々回自作のアセンブリのインストールで、[参照の追加]ダイアログに表示するには?
で作成したソリューションを流用します。

ソリューションにクラスライブラリ「Action1」を追加し、Installerクラスを継承したInstaller1クラスを作成します。


Installer1.vb
<System.ComponentModel.RunInstaller(True)> _
Public Class Installer1
Inherits System.Configuration.Install.Installer

 

Public Overrides Sub Install( _
ByVal stateSaver As System.Collections.IDictionary)
MyBase.Install(stateSaver)

 

Dim id As String = Me.Context.Parameters("id")
Dim pswd As String = Me.Context.Parameters("pswd")

 

System.Windows.Forms.MessageBox.Show("ID:" & id & "   PSWD:" & pswd)
End Sub

 

End Class


MSDNライブラリ RunInstallerAttribute クラス
MSDNライブラリ Installer クラス


セットアッププロジェクトを選択し、右クリックメニューより「表示」→「カスタム動作」をクリックします。
カスタム動作エディタが表示されます。


「インストール」を選択し、右クリックメニューより「カスタム動作の追加」をクリックします。
「プロジェクトから項目を選択」ダイアログが起動します。


「プロジェクトから項目を選択」ダイアログの検索対象コンボボックスを「アプリケーションフォルダ」に変更し
「出力の追加」ボタンをクリックします。
「プロジェクト出力グループの追加」ダイアログより「Action1のプライマリ出力」を追加します。


カスタム動作エディタの「インストール」ノードに「Action1のプライマリ出力」が追加されますので
「Action1のプライマリ出力」を右クリックし、プロパティウインドウを表示します。



CustomActionDataプロパティを「/id=[ID] /pswd=[PSWD]」と入力します。
[ID]と/の間に半角スペースがありますので注意してください。

Installerクラスを使ったカスタムアクションにデータを渡すときは、
CustomActionDataプロパティに「/パラメータ名=パラメータ値」という形式で指定します。
複数のデータを渡す場合は、スペース文字で区切ります。
また、Windows Installerのプロパティを使うときは、[]で囲みます。
Installerクラス内でデータを取得するには、InstallContext.Parametersプロパティを使います。

つまり
カスタム動作のCustomActionDataプロパティでは
前回セットアッププロジェクト ユーザーインターフェースで追加した
テキストボックス(A)ダイアログのEdit1Propertyプロパティの値とEdit2Properyプロパティの値を、id、pswdというパラメータ名でInstallerクラスに渡すよう設定します。
Installer1クラスでは、Me.Context.Parameters("id")でパラメータ名がidのパラメータ値を取得しています。




動作確認


セットアッププロジェクトをビルドし、インストールを実行します。
ID、パスワードに適当に値を入力します。


インストール終了後に、ID、パスワードが表示されればOKです。


捕捉


VSのセットアップの機能ではインストール終了後にカスタム動作が発生します。
たとえばID、パスワードを入力し「次へ」ボタンで条件分岐を行いたいなどの要件には対応できません。
ORCAなどを使って追加作業を行うか、VSセットアッププロジェクトではなく、WiXやInstalllShieldなどのインストーラ作成ツールを使用する必要があります。

.NET セットアッププロジェクト ユーザーインターフェース

セットアッププロジェクトでテキストボックス付きダイアログを表示できるようにします。

前回自作のアセンブリのインストールで、[参照の追加]ダイアログに表示するには?
で作成したセットアッププロジェクトを流用します。

セットアッププロジェクトを選択し、右クリックメニューより「表示」→「ユーザーインターフェイス」をクリックします。
ユーザーインターフェイスエディタが起動します。

この画面には[インストール]と、[管理インストール]という2つのノードがありますが
インストールは通常のインストール、管理インストールはセットアップに必要なファイルをファイル・サーバ上に転送しておき、そこからセットアップを可能にする機能です。

「インストール」の「開始」ノードを選択し、右クリックメニューの「ダイアログの追加」をクリックします。
「ダイアログの追加」ダイアログが起動しますので「テキストボックス(A)」を追加します。


追加できるダイアログの種類については下記を参照してください。
MSDNライブラリ Installation User Interface Dialog Boxes

「インストール」の「開始」ノードの最後に「テキストボックス(A)」が追加されます。


「テキストボックス(A)」をドラッグして「ようこそ」の次に移動します。


「テキストボックス(A)」は標準で4つのテキストボックスが表示されます。
今回は2つのテキストボックスを表示しようと思います。
「テキストボックス(A)」のプロパティウインドウより下記項目を設定します。
BannerTextプロパティ・・・「ライセンス登録」
BodyTextプロパティ・・・「IDおよびパスワードを入力してください。」
Edit1Labelプロパティ・・・「ID:」
Edit1Propertyプロパティ・・・「ID」
Edit2Labelプロパティ・・・「パスワード:」
Edit2Propertyプロパティ・・・「PSWD」
Edit3Visibleプロパティ・・・「False」
Edit4Visibleプロパティ・・・「False」


この状態でセットアッププロジェクトをビルドし、インストールを実行すると「ようこそ」画面の次に
以下のような画面が表示されます。


次回のセットアッププロジェクト カスタム動作で「ID」「パスワード」の入力値を取得する方法を説明します。

2008年6月6日金曜日

.NET 自作のアセンブリのインストールで、[参照の追加]ダイアログに表示するには?

自作アセンブリのインストーラで、GAC登録を行い、レジストリを追加することで、ツールボックスの「アイテムの選択」ダイアログや「参照の追加」ダイアログに自作アセンブリを表示することができます。

自作アセンブリに厳密名をつける


例えばMyControlsクラスライブラリにMyTextBox、MyComboBoxを作成したとします。
このMyControlsクラスライブラリに厳密名をつけます。

※厳密名をつけることで以下のようなメリットがあります。
メリット1:参照DLLが改竄されていないことを証明する
メリット2:GACに登録することができるようになる
メリット3:コードアクセスセキュリティで厳密名による設定が可能になる
詳しくはアセンブリ署名とコードアクセスセキュリティを参照してください。

VS2005で厳密名をつける手順は簡単です。
MyControlsクラスライブラリのプロパティページの署名タブを開きます。
アセンブリの署名チェックボックスをチェックONにし、コンボボックスより新規作成を選択します。


厳密な名前のキー作成ダイアログが表示されますので、キーファイル名とパスワードを入力します。


ソリューションエクスプローラに、厳密な名前のキー作成ダイアログで指定したキーファイル名.pfxファイルが追加されます。


セットアッププロジェクトを作成します。


ソリューションにセットアッププロジェクトを追加します。
セットアッププロジェクトのプロパティを必要に応じて変更してください。


続いて「アプリケーションフォルダ」を右クリックし「追加」→「プロジェクト出力」を選択ます。


表示された「プロジェクト出力グループの追加」ダイアログより「プライマリ出力」を選択します。




GAC登録


「対象コンピュータ上のファイルシステム」を右クリックし「特別なフォルダの追加」より「グローバルアセンブリキャッシュフォルダ」を選択します。
依存関係のあるdllをGACに登録する場合、グローバルアセンブリキャッシュに依存関係のあるdllを含め、アプリケーションフォルダにはプライマリ出力だけになるようにします。
手順はグローバルアセンブリキャッシュにプライマリ出力を行った後、アプリケーションフォルダにプロジェクト出力を行います。



「グローバルアセンブリキャッシュフォルダ」を右クリックし「追加」→「プロジェクト出力」を選択ます。


表示された「プロジェクト出力グループの追加」ダイアログより「プライマリ出力」を選択します。


レジストリの追加


まず自PCのレジストリを確認します。
「スタート」→「ファイル名を指定して実行」をクリックし、「regedit」と打ち込んで「OK」をクリックしレジストリエディタを起動してください。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFoldersを確認してください。
ここに自作コントロールライブラリMyControlsを作成することにより、GACに登録されていれば、ツールボックスのアイテムの選択ダイアログに表示されるようになります。

確認できたらVSの作業に戻ります。
ツールメニューの「表示」→「エディタ」→「レジストリエディタ」をクリックしレジストリエディタを起動します。


HKEY_LOCAL_MACHINEを右クリックし「新しいキー」で下記構成になるようにキーを作成します。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\[ProductName]


[ProductName]を右クリックし「新規作成」→「文字列の値」を選択します。
Nameプロパティは空値、Valueプロパティは[TARGETDIR]とします。


以上でインストーラの作成は終了です。


動作確認


MyControlsのインストールを行います。

C:\WINDOWS\assemblyフォルダ内にMyControlsがあるかどうか確認します。


レジストリエディタを起動し、HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFoldersにMyControlsが作成され、インストールパスが正しいか確認します。


VSを起動しWindowsアプリケーションを作成します。
ツールボックスを右クリックし「アイテムの選択」をクリックします。
「ツールボックス アイテムの選択」ダイアログにMyTextBoxとMyComboBoxが表示されている事を確認します。

2008年5月23日金曜日

MySQLのインストール(Windows) その3

MySQLの環境設定



MySQLの環境設定ウィザードが起動します。
Next」ボタンをクリックします。


デフォルトの「Detailed Configuration」のまま、「Next」ボタンをクリックします。


デフォルトの「Developer Machine」のまま、「Next」ボタンをクリックします。


デフォルトの「Multi Function Database」のまま、「Next」ボタンをクリックします。


デフォルトのまま、「Next」ボタンをクリックします。


デフォルトの「Decisison Support(DSS)/OLAP」のまま、「Next」ボタンをクリックします。


デフォルトの「Enable TCP/IP Networking」を選択し、「Port Number」も既定の「3306」のままとし、「Next」ボタンをクリックします。


デフォルトの「Install As Windows Service」と、「Inclue Bin Directory in Windows PATH」もチェックし、「Next」ボタンをクリックします。



「Modify Security Settings」をチェックし、「 New root password 」欄に root用パスワードを記入し、「Confirm」欄に rootパスワードをもう1回入力し、「Next」ボタンをクリックします。


「Execute」ボタンをクリックします。


正常に環境設定が完了すれば、下記ダイアログボックスが表示されます。「Finish」ボタンをクリックし、環境設定ウィザ-ドを終了します。