none
所有者情報取得 RRS feed

  • 質問

  • フォルダ、ファイルの所有者情報取得ときにそれぞれが下記エラー発生しました。ファイル、フォルダによって取得できる場合もありますが
    原因を分かりませんでした。分かる方がいらしゃいましたら教えていただけますでしょうか。

    Private Shared Function GetFileOwner(ByVal fileName As String) As String

            Try
                Dim shell As New Shell()
                Dim f As Folder = shell.NameSpace(Path.GetDirectoryName(fileName.Substring(4)))
                Dim item As FolderItem = f.ParseName(Path.GetFileName(fileName.Substring(4)))
                Return f.GetDetailsOf(item, 10)
            Catch ex As Exception
                logger.Error("ファイル所有者取得中にエラーが発生しました。" & ex.Message)
                Return ""
            End Try

        End Function

        '=====================================================================================================
        '機能:フォルダ所有者情報取得
        '-----------------------------------------------------------------------------------------------------
        '返値:所有者
        '=====================================================================================================
        Private Shared Function GetDirOwner(ByVal folderName As String) As String

            Try
                Dim di As New DirectoryInfo(folderName)
                Dim ds As Security.AccessControl.DirectorySecurity = di.GetAccessControl
                Dim owner As Security.Principal.NTAccount = CType(ds.GetOwner(GetType(Security.Principal.NTAccount)), Security.Principal.NTAccount)
                Return owner.ToString()
            Catch ex As Exception
                logger.Error("フォルダ所有者取得中にエラーが発生しました。" & ex.Message)
                Return ""
            End Try

        End Function
        
    エラーは:    
    -----------------------------------------------------------------------------------------------------------------
    フォルダ所有者取得中エラーが発生されました。ID 参照の一部またはすべてを変換できませんでした。
    フォルダ所有者取得中エラーが発生されました。名前が無効です。
    パラメーター名:name
    -----------------------------------------------------------------------------------------------------------------
    ファイル所有者取得中にエラーが発生しました。オブジェクト参照がオブジェクト インスタンスに設定されていません。
    -----------------------------------------------------------------------------------------------------------------


    2019年6月3日 7:50

回答

  • Luky9 さんが提示された Function GetFileOwner のコードを見ると、COM の Shell Object 経由で所有者情報を取得されていたようですが、特に理由が無ければ FileInfo.GetAccessControl() 経由で取得する方法に揃えた方が良いのでは無いでしょうか。

     'Return owner.ToString()
     Return owner.Value

    NTAccount クラスに関して言えば、上記の処理は同義のようですね。

    Value プロパティの実装を確認してみたところ、「自身の ToString() メソッドを呼び出しているだけ」でした。( Reference Source でも、dotnet/corefx でも )

    また、等価演算子「public static bool operator==( NTAccount left, NTAccount right )」についても、ToString() メソッドの戻り値の一致性を返す実装になっていましたので、どうやら Value プロパティに拘る必要は無さそうです。

    怪しいのは、その前の ds.GetOwner(GetType(NTAccount)) の段階かと予想しています。

    アカウント名への名前解決ができなかったときに得られる値が SID のままになっても構わなければ、こんな感じで取得できるかもしれません。

    Try
        'Dim info As New System.IO.FileInfo(fileName)   'ファイルの所有権を得る場合
        Dim info As New DirectoryInfo(folderName)       'フォルダーの所有権を得る場合
        Dim security = di.GetAccessControl()
        Try
            Return security.GetOwner(GetType(System.Security.Principal.NTAccount)).ToString()
        Catch ex As System.Security.Principal.IdentityNotMappedException
            Return security.GetOwner(GetType(System.Security.Principal.SecurityIdentifier)).ToString()
        End Try
    Catch ex As Exception
        'logger.Error("ファイル(" & fileName & ")の所有者情報の取得中にエラーが発生しました。" & ex.Message)
        logger.Error("フォルダー(" & folderName & ")の所有者情報の取得中にエラーが発生しました。" & ex.Message)
        Return ""
    End Try

    2019年6月4日 1:03
  • ID 参照の一部またはすべてを変換できませんでした。

    ファイル側が COM 実装で、ディレクトリ側がマネージ実装という非対称性が気になりましたが、それはさておき。
    ひとまずエラーメッセージで検索してみたところ、IdentityNotMappedException の例外となる例が見つかりました。

    原因を分かりませんでした。

    「原因『』分かりませんでした。」ですね。

    可能性として思い当たるのは、何らかの原因で SID をユーザー名またはグループ名に変換できなくなっているというものです。(アカウントを削除してしまったとか、ドメインが失われたとか)

    問題点を整理するため、下記 5 つの逆質問にお答えください。

    1. アプリケーションは「管理者として実行」モードで起動されているのでしょうか。それとも一般ユーザー権限での実行でしょうか。
    2. 今回の事象に再現性はありますか? (つまり、特定のパスでのみ発生する事象なのか、それとも、同じ文字列を渡しているに、例外になる場合とならない場合があるのかどうか、ということです。)
    3. エラーが発生するパスをエクスプローラーで確認した場合、所有者情報を正しく取得できるかどうかを確認してみてください。エクスプローラーでも取得できないなら、Visual Basic 側でも対処できない事象であると予想します。
    4. エクスプローラーであれば取得できる場合、本来であれば何という文字列が返却されるべきなのかを教えてください。
    5. エクスプローラーであれば取得できる場合、Function GetDirOwner 内のどの処理で例外が発生しているかを突き止めてください。
      ①New DirectoryInfo(folderName) のコンストラクタ呼び出し
      ②di.GetAccessControl のメソッド呼び出し
      ③ds.GetOwner(GetType(Security.Principal.NTAccount)) のメソッド呼び出し
      ④CType(ds.GetOwner(…), Security.Principal.NTAccount) の型変換
      ⑤owner.ToString() のメソッド呼び出し
    Imports System.Security.AccessControl
    Imports System.Security.Principal
    Imports System.IO
    
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'ディレクトリ の所有者
            Dim di As New DirectoryInfo(TextBox1.Text)
            Dim ds As DirectorySecurity = di.GetAccessControl()
            Dim ir As IdentityReference = ds.GetOwner(GetType(NTAccount))
            Dim owner As NTAccount = CType(ir, NTAccount)
            MsgBox(owner.Value, MsgBoxStyle.Information)
            MsgBox(owner.ToString(), MsgBoxStyle.Information)
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            'ファイルの所有者
            Dim fi As New FileInfo(TextBox2.Text)
            Dim fs As FileSecurity = fi.GetAccessControl()
            Dim ir As IdentityReference = fs.GetOwner(GetType(NTAccount))
            Dim owner As NTAccount = CType(ir, NTAccount)
            MsgBox(owner.Value, MsgBoxStyle.Information)
            MsgBox(owner.ToString(), MsgBoxStyle.Information)
        End Sub
    End Class

    Windows のファイル操作は、突き詰めると本当に面倒ですね…。

    Windows でファイル操作のコードを書くときは、決して油断してはならない。

    2019年6月3日 9:39
  • リモートのディレクトリだったり、別PCにつないであったNTFSのドライブを実行PCにつないでいて、実行PCではSIDからNTアカウント名へ名前解決できないとか?

    Security.Principal.NTAccountではなくSecurity.Principal.SecurityIdentifierでSIDをつかってみる。

    Declare Function LookupAccountSid Lib "advapi32.dll" Alias "LookupAccountSidA" _
    (ByVal systemName As String _
    , ByVal psid As Byte() _
    , ByVal accountName As StringBuilder _
    , ByRef cbAccount As Integer _
    , ByVal domainName As StringBuilder _
    , ByRef cbDomainName As Integer _
    , ByRef use As SID_NAME_USE) As Boolean
    
    Enum SID_NAME_USE
        SidTypeUser = 1
        SidTypeGroup
        SidTypeDomain
        SidTypeAlias
        SidTypeWellKnownGroup
        SidTypeDeletedAccount
        SidTypeInvalid
        SidTypeUnknown
        SidTypeComputer
        SidTypeLabel
    End Enum
    
    Private Shared Function GetDirOwner(ByVal folderName As String) As String
    
        Try
            Dim di As New DirectoryInfo(folderName)
            Dim ds As Security.AccessControl.DirectorySecurity = di.GetAccessControl
    
            Try
                Dim owner As Security.Principal.NTAccount = CType(ds.GetOwner(GetType(Security.Principal.NTAccount)), Security.Principal.NTAccount)
                Return owner.ToString()
            Catch ex As System.Security.Principal.IdentityNotMappedException
            End Try
    
    
            Dim ownerSID As Security.Principal.SecurityIdentifier _
                = CType(ds.GetOwner(GetType(Security.Principal.SecurityIdentifier)), Security.Principal.SecurityIdentifier)
            Dim domain As New StringBuilder(1000)
            Dim name As New StringBuilder(1000)
            Dim use As SID_NAME_USE
            Dim bs As Byte()
            ReDim bs(ownerSID.BinaryLength - 1)
    
            Dim systemName As String
    
            Dim fullpath = System.IO.Path.GetFullPath(folderName)
            If (fullpath.StartsWith("\\")) Then
                systemName = fullpath.Split(New Char() {"\"c}, StringSplitOptions.RemoveEmptyEntries)(0)
            Else
                systemName = "."
            End If
    
    
    
            ownerSID.GetBinaryForm(bs, 0)
            LookupAccountSid(systemName, bs, name, name.Capacity, domain, domain.Capacity, use)
            Dim accountName = domain.ToString() & "\" & name.ToString()
            Return accountName
        Catch ex As Exception
            'logger.Error("フォルダ所有者取得中にエラーが発生しました。" & ex.Message)
            Return ""
        End Try
    
    End Function


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク Luky9 2019年6月5日 4:07
    2019年6月3日 9:52
  • 参考 URL を見て戴ければ分かりますが、比較は Value で行われています。
     演算子 Equality(NTAccount, NTAccount) 等。
    Null (VB の場合 Nothing ) で戻る以外は値が戻って来てエラーを吐かないと想像出来ます。

    尚、俗に、不明なアカウント(SID S-1-15-3-4096)等と言うのがあります。
    他のPC で作成したファイルを属性そのままで移行したり、
    ユーザーを削除してしまった場合、
    該当 PC にはそのアカウントがないので SID のみで、
    Tostring() では取得出ず、エラー(IdentityNotMappedException)を吐いているのではありませんか?。

     'Return owner.ToString()
     Dim result As String = owner.Value
     If result Is Nothing Then
       Return ""
     Else
       Retuen result
     End If


    • 編集済み ShiroYuki_Mot 2019年6月3日 13:48 コード追記
    • 回答としてマーク Luky9 2019年6月5日 4:07
    2019年6月3日 13:10

すべての返信

  • Luky9 さま よろしく。

    以下では如何でしょう?。

     'Return owner.ToString()
     Return owner.Value


    違っていたら、ごめんなさい。

    参考 URL : https://docs.microsoft.com/ja-jp/dotnet/api/system.security.principal.ntaccount?view=netframework-4.8

    2019年6月3日 9:02
  • ID 参照の一部またはすべてを変換できませんでした。

    ファイル側が COM 実装で、ディレクトリ側がマネージ実装という非対称性が気になりましたが、それはさておき。
    ひとまずエラーメッセージで検索してみたところ、IdentityNotMappedException の例外となる例が見つかりました。

    原因を分かりませんでした。

    「原因『』分かりませんでした。」ですね。

    可能性として思い当たるのは、何らかの原因で SID をユーザー名またはグループ名に変換できなくなっているというものです。(アカウントを削除してしまったとか、ドメインが失われたとか)

    問題点を整理するため、下記 5 つの逆質問にお答えください。

    1. アプリケーションは「管理者として実行」モードで起動されているのでしょうか。それとも一般ユーザー権限での実行でしょうか。
    2. 今回の事象に再現性はありますか? (つまり、特定のパスでのみ発生する事象なのか、それとも、同じ文字列を渡しているに、例外になる場合とならない場合があるのかどうか、ということです。)
    3. エラーが発生するパスをエクスプローラーで確認した場合、所有者情報を正しく取得できるかどうかを確認してみてください。エクスプローラーでも取得できないなら、Visual Basic 側でも対処できない事象であると予想します。
    4. エクスプローラーであれば取得できる場合、本来であれば何という文字列が返却されるべきなのかを教えてください。
    5. エクスプローラーであれば取得できる場合、Function GetDirOwner 内のどの処理で例外が発生しているかを突き止めてください。
      ①New DirectoryInfo(folderName) のコンストラクタ呼び出し
      ②di.GetAccessControl のメソッド呼び出し
      ③ds.GetOwner(GetType(Security.Principal.NTAccount)) のメソッド呼び出し
      ④CType(ds.GetOwner(…), Security.Principal.NTAccount) の型変換
      ⑤owner.ToString() のメソッド呼び出し
    Imports System.Security.AccessControl
    Imports System.Security.Principal
    Imports System.IO
    
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'ディレクトリ の所有者
            Dim di As New DirectoryInfo(TextBox1.Text)
            Dim ds As DirectorySecurity = di.GetAccessControl()
            Dim ir As IdentityReference = ds.GetOwner(GetType(NTAccount))
            Dim owner As NTAccount = CType(ir, NTAccount)
            MsgBox(owner.Value, MsgBoxStyle.Information)
            MsgBox(owner.ToString(), MsgBoxStyle.Information)
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            'ファイルの所有者
            Dim fi As New FileInfo(TextBox2.Text)
            Dim fs As FileSecurity = fi.GetAccessControl()
            Dim ir As IdentityReference = fs.GetOwner(GetType(NTAccount))
            Dim owner As NTAccount = CType(ir, NTAccount)
            MsgBox(owner.Value, MsgBoxStyle.Information)
            MsgBox(owner.ToString(), MsgBoxStyle.Information)
        End Sub
    End Class

    Windows のファイル操作は、突き詰めると本当に面倒ですね…。

    Windows でファイル操作のコードを書くときは、決して油断してはならない。

    2019年6月3日 9:39
  • リモートのディレクトリだったり、別PCにつないであったNTFSのドライブを実行PCにつないでいて、実行PCではSIDからNTアカウント名へ名前解決できないとか?

    Security.Principal.NTAccountではなくSecurity.Principal.SecurityIdentifierでSIDをつかってみる。

    Declare Function LookupAccountSid Lib "advapi32.dll" Alias "LookupAccountSidA" _
    (ByVal systemName As String _
    , ByVal psid As Byte() _
    , ByVal accountName As StringBuilder _
    , ByRef cbAccount As Integer _
    , ByVal domainName As StringBuilder _
    , ByRef cbDomainName As Integer _
    , ByRef use As SID_NAME_USE) As Boolean
    
    Enum SID_NAME_USE
        SidTypeUser = 1
        SidTypeGroup
        SidTypeDomain
        SidTypeAlias
        SidTypeWellKnownGroup
        SidTypeDeletedAccount
        SidTypeInvalid
        SidTypeUnknown
        SidTypeComputer
        SidTypeLabel
    End Enum
    
    Private Shared Function GetDirOwner(ByVal folderName As String) As String
    
        Try
            Dim di As New DirectoryInfo(folderName)
            Dim ds As Security.AccessControl.DirectorySecurity = di.GetAccessControl
    
            Try
                Dim owner As Security.Principal.NTAccount = CType(ds.GetOwner(GetType(Security.Principal.NTAccount)), Security.Principal.NTAccount)
                Return owner.ToString()
            Catch ex As System.Security.Principal.IdentityNotMappedException
            End Try
    
    
            Dim ownerSID As Security.Principal.SecurityIdentifier _
                = CType(ds.GetOwner(GetType(Security.Principal.SecurityIdentifier)), Security.Principal.SecurityIdentifier)
            Dim domain As New StringBuilder(1000)
            Dim name As New StringBuilder(1000)
            Dim use As SID_NAME_USE
            Dim bs As Byte()
            ReDim bs(ownerSID.BinaryLength - 1)
    
            Dim systemName As String
    
            Dim fullpath = System.IO.Path.GetFullPath(folderName)
            If (fullpath.StartsWith("\\")) Then
                systemName = fullpath.Split(New Char() {"\"c}, StringSplitOptions.RemoveEmptyEntries)(0)
            Else
                systemName = "."
            End If
    
    
    
            ownerSID.GetBinaryForm(bs, 0)
            LookupAccountSid(systemName, bs, name, name.Capacity, domain, domain.Capacity, use)
            Dim accountName = domain.ToString() & "\" & name.ToString()
            Return accountName
        Catch ex As Exception
            'logger.Error("フォルダ所有者取得中にエラーが発生しました。" & ex.Message)
            Return ""
        End Try
    
    End Function


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク Luky9 2019年6月5日 4:07
    2019年6月3日 9:52
  • 参考 URL を見て戴ければ分かりますが、比較は Value で行われています。
     演算子 Equality(NTAccount, NTAccount) 等。
    Null (VB の場合 Nothing ) で戻る以外は値が戻って来てエラーを吐かないと想像出来ます。

    尚、俗に、不明なアカウント(SID S-1-15-3-4096)等と言うのがあります。
    他のPC で作成したファイルを属性そのままで移行したり、
    ユーザーを削除してしまった場合、
    該当 PC にはそのアカウントがないので SID のみで、
    Tostring() では取得出ず、エラー(IdentityNotMappedException)を吐いているのではありませんか?。

     'Return owner.ToString()
     Dim result As String = owner.Value
     If result Is Nothing Then
       Return ""
     Else
       Retuen result
     End If


    • 編集済み ShiroYuki_Mot 2019年6月3日 13:48 コード追記
    • 回答としてマーク Luky9 2019年6月5日 4:07
    2019年6月3日 13:10
  • Luky9 さんが提示された Function GetFileOwner のコードを見ると、COM の Shell Object 経由で所有者情報を取得されていたようですが、特に理由が無ければ FileInfo.GetAccessControl() 経由で取得する方法に揃えた方が良いのでは無いでしょうか。

     'Return owner.ToString()
     Return owner.Value

    NTAccount クラスに関して言えば、上記の処理は同義のようですね。

    Value プロパティの実装を確認してみたところ、「自身の ToString() メソッドを呼び出しているだけ」でした。( Reference Source でも、dotnet/corefx でも )

    また、等価演算子「public static bool operator==( NTAccount left, NTAccount right )」についても、ToString() メソッドの戻り値の一致性を返す実装になっていましたので、どうやら Value プロパティに拘る必要は無さそうです。

    怪しいのは、その前の ds.GetOwner(GetType(NTAccount)) の段階かと予想しています。

    アカウント名への名前解決ができなかったときに得られる値が SID のままになっても構わなければ、こんな感じで取得できるかもしれません。

    Try
        'Dim info As New System.IO.FileInfo(fileName)   'ファイルの所有権を得る場合
        Dim info As New DirectoryInfo(folderName)       'フォルダーの所有権を得る場合
        Dim security = di.GetAccessControl()
        Try
            Return security.GetOwner(GetType(System.Security.Principal.NTAccount)).ToString()
        Catch ex As System.Security.Principal.IdentityNotMappedException
            Return security.GetOwner(GetType(System.Security.Principal.SecurityIdentifier)).ToString()
        End Try
    Catch ex As Exception
        'logger.Error("ファイル(" & fileName & ")の所有者情報の取得中にエラーが発生しました。" & ex.Message)
        logger.Error("フォルダー(" & folderName & ")の所有者情報の取得中にエラーが発生しました。" & ex.Message)
        Return ""
    End Try

    2019年6月4日 1:03
  • みなさん、

    お返事ありがとうございます。

    確認してみます。本当に助かります。ありがとうございます。

    2019年6月4日 2:10
  • 魔界の仮面弁士 さま ご指摘ありがとうございます。

    公式ドキュメントの表記に拘り、誤りを犯していた様です。
    実装の確認迄 して戴き、感謝致します。

    2019年6月4日 2:56