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

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

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

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

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

.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にしてデバッグ実行するとブレークポイントに止まります。