.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

.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

.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の両方にマニフェストファイルを埋め込んでいます。

.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までの値の符号付き整数

.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

.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」が取得できます。