使用 WMI 将用户添加到远程 windows 共享

Add user to remote windows share using WMI

我在远程计算机 IMPC-1111 的 C:\ 根目录中有一个共享,我正在尝试添加一个用户 /w 访问掩码:

FullControl = 2032127
Change = 1245631
[ReadOnly] = 1179817

我已经想出了如何阅读它们....但无法弄清楚如何 write/change 它们。这是我用来阅读它们的代码。

Private Function GetSharedFolderAccessRule() As DataTable

    Dim DT As DataTable = New DataTable()

    Try

        DT.Columns.Add("ShareName")
        DT.Columns.Add("Caption")
        DT.Columns.Add("Path")
        DT.Columns.Add("Domain")
        DT.Columns.Add("User")
        DT.Columns.Add("AccessMask")
        DT.Columns.Add("AceType")

        Dim Con As ConnectionOptions = New ConnectionOptions
        Con.Username = "Username"
        Con.Password = "Password"

        'Dim Scope As ManagementScope = New ManagementScope("\.\root\cimv2", Con)
        Dim Scope As ManagementScope = New ManagementScope("\IMPC-1111\root\cimv2", Con)
        Scope.Connect()

        Dim Query As ObjectQuery = New ObjectQuery("SELECT * FROM Win32_LogicalShareSecuritySetting")
        Dim Searcher As ManagementObjectSearcher = New ManagementObjectSearcher(Scope, Query)
        Dim QueryCollection As ManagementObjectCollection = Searcher.[Get]()

        For Each SharedFolder As ManagementObject In QueryCollection

            If True Then
                Dim ShareName As String = CType(SharedFolder("Name"), String)
                Dim Caption As String = CType(SharedFolder("Caption"), String)
                Dim LocalPath As String = String.Empty
                Dim Win32Share As ManagementObjectSearcher = New ManagementObjectSearcher("SELECT Path FROM Win32_share WHERE Name = '" & ShareName & "'")

                For Each ShareData As ManagementObject In Win32Share.[Get]()
                    LocalPath = CType(ShareData("Path"), String)
                Next

                Dim Method As ManagementBaseObject = SharedFolder.InvokeMethod("GetSecurityDescriptor", Nothing, New InvokeMethodOptions())
                Dim Descriptor As ManagementBaseObject = CType(Method("Descriptor"), ManagementBaseObject)
                Dim DACL As ManagementBaseObject() = CType(Descriptor("DACL"), ManagementBaseObject())

                For Each ACE As ManagementBaseObject In DACL
                    Dim Trustee As ManagementBaseObject = CType(ACE("Trustee"), ManagementBaseObject)
                    Dim Row As DataRow = DT.NewRow()
                    Row("ShareName") = ShareName
                    Row("Caption") = Caption
                    Row("Path") = LocalPath
                    Row("Domain") = CType(Trustee("Domain"), String)
                    Row("User") = CType(Trustee("Name"), String)
                    Row("AccessMask") = CType(ACE("AccessMask"), UInt32)
                    Row("AceType") = CType(ACE("AceType"), UInt32)
                    DT.Rows.Add(Row)
                    DT.AcceptChanges()
                Next
            End If
        Next

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

    Return DT

End Function

任何人都可以指出我正确的方向吗?这甚至可以使用 WMI 编写吗?

谢谢!

您可以使用Win32_LogicalShareSecuritySetting.SetSecurityDescriptor() method修改共享权限。修改现有权限的工作方式如下...

  1. 获取所需的 Win32_LogicalShareSecuritySetting 实例。
  2. 调用GetSecurityDescriptor() to get the share's security descriptor as a Win32_SecurityDescriptor实例。
  3. 通过其 DACL 属性.
  4. 获取安全描述符的自由访问控制列表
  5. 创建一个新的访问控制条目。
    1. 得到一个reference to the Win32_ACE class.
    2. 创建 Win32_ACE class 的实例。
    3. 设置访问控制条目的AccessMaskAceFlagsAceTypeTrustee属性。
      1. 得到一个reference to the Win32_Trustee class.
      2. 创建 Win32_Trustee class 的实例。
      3. 通过设置受托人的SIDStringName(和Domain)属性来指定条目的委托人。
  6. 创建一个结合现有条目和新条目的新访问控制列表。
  7. 将安全描述符的 DACL 属性 设置为新的访问控制列表。
  8. 调用 SetSecurityDescriptor() 保存共享的安全描述符。

以下 class 包含获取共享属性的方法,特别是通过 AddAccessControlEntry() 方法添加新的访问控制条目...

Imports System.Management
Imports System.Security.AccessControl

Friend Class ShareSecurity
    Private ReadOnly Property ShareName As String

    Private ReadOnly Property GetOptions As ObjectGetOptions

    Private ReadOnly Property Scope As ManagementScope

    Public Sub New(host As String, shareName As String, username As String, password As String)
        Me.ShareName = shareName
        GetOptions = New ObjectGetOptions()
        Scope = New ManagementScope(
            New ManagementPath() With {
                .NamespacePath = "root\cimv2",
                .Server = host
            },
            New ConnectionOptions() With {
 _ ' ***** For demonstration purposes only! *****
 _ 'TODO: Find a secure way to store the remote password
                .Password = password,
                .Username = username
            }
        )
    End Sub

    Public Function GetShareProperties() As IDictionary(Of String, Object)
        Dim sharePath As New ManagementPath($"Win32_Share.Name=""{ShareName}""")

        Using share As New ManagementObject(Scope, sharePath, GetOptions)
            Return GetPropertyDictionary(share)
        End Using
    End Function

    Public Function GetShareSecurityDescriptorProperties() As IDictionary(Of String, Object)
        Using shareSecurity As ManagementObject = GetShareSecurity()
            Using securityDescriptor As ManagementBaseObject = GetShareSecurityDescriptor(shareSecurity)
                Return GetPropertyDictionary(securityDescriptor)
            End Using
        End Using
    End Function

    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, securityIdentifier As String)
        AddAccessControlEntry(
            entryType,
            entryRights,
            Sub(trustee)
                trustee("SIDString") = securityIdentifier
            End Sub
        )
    End Sub

    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, domain As String, account As String)
        AddAccessControlEntry(
            entryType,
            entryRights,
            Sub(trustee)
                trustee("Domain") = domain
                trustee("Name") = account
            End Sub
        )
    End Sub

    ''' <param name="trusteeInitializer">Initializes the Win32_Trustee instance for the access control entry to be added.</param>
    Public Sub AddAccessControlEntry(entryType As AceType, entryRights As FileSystemRights, trusteeInitializer As Action(Of ManagementObject))
        Using shareSecurity As ManagementObject = GetShareSecurity()
            Using securityDescriptor As ManagementBaseObject = GetShareSecurityDescriptor(shareSecurity)
                Dim accessControlEntries As ManagementBaseObject() = DirectCast(securityDescriptor("DACL"), ManagementBaseObject())

                ' The class must not be created in the remote scope otherwise CreateInstance()
                ' throws "System.UnauthorizedAccessException: 'Access is denied'."
                Using accessControlEntryClass As New ManagementClass("Win32_ACE")
                    Using accessControlEntry As ManagementObject = accessControlEntryClass.CreateInstance()
                        accessControlEntry("AccessMask") = CUInt(entryRights)
                        accessControlEntry("AceFlags") = CUInt(AceFlags.None)
                        accessControlEntry("AceType") = CUInt(entryType)

                        ' The class must not be created in the remote scope otherwise CreateInstance()
                        ' throws "System.UnauthorizedAccessException: 'Access is denied'."
                        Using trusteeClass As New ManagementClass("Win32_Trustee")
                            Using trustee As ManagementObject = trusteeClass.CreateInstance()
                                trusteeInitializer.Invoke(trustee)
                                accessControlEntry("Trustee") = trustee

                                ' Create a new access control list including the new access control
                                ' entry, sorted with Deny entries first (true sorts after false)
                                ' https://docs.microsoft.com/windows/win32/secauthz/order-of-aces-in-a-dacl
                                securityDescriptor("DACL") = accessControlEntries _
                                    .Append(accessControlEntry) _
                                    .OrderByDescending(Function(entry) CType(entry("AceType"), AceType)) _
                                    .ToArray()

                                SetShareSecurityDescriptor(shareSecurity, securityDescriptor)
                            End Using
                        End Using
                    End Using
                End Using
            End Using
        End Using
    End Sub

    Private Function GetShareSecurity() As ManagementObject
        Dim shareSecurityPath As New ManagementPath($"Win32_LogicalShareSecuritySetting.Name=""{ShareName}""")

        Return New ManagementObject(Scope, shareSecurityPath, GetOptions)
    End Function

    Private Function GetShareSecurityDescriptor(shareSecurity As ManagementObject) As ManagementBaseObject
        ' Create an array to store the output parameter
        Dim invokeParameters(0) As ManagementBaseObject
        Dim invokeResult As UInteger = shareSecurity.InvokeMethod("GetSecurityDescriptor", invokeParameters)

        If invokeResult = 0 Then
            Return invokeParameters(0)
        Else
            'TODO: Handle failure of GetSecurityDescriptor()...
            Return Nothing
        End If
    End Function

    Private Sub SetShareSecurityDescriptor(shareSecurity As ManagementObject, securityDescriptor As ManagementBaseObject)
        ' Create an array to store the input parameter
        Dim invokeParameters() As ManagementBaseObject = {securityDescriptor}
        Dim invokeResult As UInteger = shareSecurity.InvokeMethod("SetSecurityDescriptor", invokeParameters)

        If invokeResult <> 0 Then
            'TODO: Handle failure of SetSecurityDescriptor()...
        End If
    End Sub

    Private Shared Function GetPropertyDictionary(obj As ManagementBaseObject) As IDictionary(Of String, Object)
        Return obj.Properties _
            .Cast(Of PropertyData)() _
            .ToDictionary(
                Function([property]) [property].Name,
                Function([property])
                    ' Recursively create dictionaries in place of management objects
                    Dim baseObjectArray As ManagementBaseObject() = TryCast([property].Value, ManagementBaseObject())

                    If baseObjectArray IsNot Nothing Then
                        Return baseObjectArray.Select(AddressOf GetPropertyDictionary).ToArray()
                    Else
                        Dim baseObject As ManagementBaseObject = TryCast([property].Value, ManagementBaseObject)

                        If baseObject IsNot Nothing Then
                            Return GetPropertyDictionary(baseObject)
                        Else
                            Return [property].Value
                        End If
                    End If
                End Function
            )
    End Function
End Class

下面的程序使用上面的 ShareSecurity class 来显示远程共享的属性,并且可以选择向它添加一个新的访问控制条目...

Imports System.Security.AccessControl

Public Class Program
    Public Shared Sub Main(args As String())
        If args Is Nothing OrElse args.Length = 0 Then
            DisplayUsage()
        ElseIf args.Length < 4 Then
            DisplayError("Too few arguments.")
        Else
            Dim host As String = args(0)
            Dim shareName As String = args(1)
            Dim username As String = args(2)
            ' ***** For demonstration purposes only! *****
            'TODO: Find a secure way to store the remote password
            Dim password As String = args(3)
            Dim shareSecurity As New ShareSecurity(host, shareName, username, password)

            DisplayShareProperties(shareSecurity)
            DisplaySecurityDescriptor(shareSecurity)

            If args.Length > 4 Then
                If args.Length <> 7 AndAlso args.Length <> 8 Then
                    DisplayError("Argument count mismatch.")
                Else
                    Dim entryType As AceType

                    If Not [Enum].TryParse(args(4), True, entryType) Then
                        DisplayError($"Invalid <type> value ""{args(4)}"".")
                    Else
                        Dim entryRights As FileSystemRights

                        If Not [Enum].TryParse(args(5), True, entryRights) Then
                            DisplayError($"Invalid <rights> value ""{args(5)}"".")
                        Else
                            DisplayTextWithSeparator("New access control entry")
                            Console.WriteLine($"   Type: {entryType}")
                            Console.WriteLine($" Rights: {entryRights}")

                            If args.Length = 7 Then
                                Dim securityIdentifier As String = args(6)

                                Console.WriteLine($"    SID: {securityIdentifier}")
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, securityIdentifier)
                            Else ' args.Length = 8
                                Dim domain As String = args(6)
                                Dim account As String = args(7)

                                Console.WriteLine($" Domain: {domain}")
                                Console.WriteLine($"Account: {account}")
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, domain, account)
                            End If
                            Console.WriteLine()
                            DisplaySecurityDescriptor(shareSecurity)
                        End If
                    End If
                End If
            End If
        End If
    End Sub

    Private Shared Sub DisplayUsage()
        Dim entryAssemblyPath As String = System.Reflection.Assembly.GetEntryAssembly().Location
        Dim entryAssemblyName As String = System.IO.Path.GetFileName(entryAssemblyPath)

        Console.WriteLine("Display share properties:")
        Console.WriteLine()
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password>")
        Console.WriteLine()
        Console.WriteLine("Add access control entry:")
        Console.WriteLine()
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <sid>")
        Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <domain> <account>")
        Console.WriteLine()
        Console.WriteLine(vbTab & $"  <type> - A {GetType(AceType).FullName} enumeration value.")
        Console.WriteLine(vbTab & $"<rights> - A {GetType(FileSystemRights).FullName} enumeration value.")
        Console.WriteLine(vbTab & "   <sid> - An account security identifier.")
        Console.WriteLine()
    End Sub

    Private Shared Sub DisplayError(message As String)
        Console.WriteLine($"ERROR: {message}")
        Console.WriteLine()
        DisplayUsage()
    End Sub

    Private Shared Sub DisplayTextWithSeparator(text As String)
        Console.WriteLine(text)
        Console.WriteLine(New String("-"c, text.Length))
    End Sub

    Private Shared Sub DisplayShareProperties(shareSecurity As ShareSecurity)
        Dim shareProperties As IDictionary(Of String, Object) = shareSecurity.GetShareProperties()

        DisplayTextWithSeparator("Share properties")
        For Each propertyName As String In New String() {"Description", "Name", "Path"}
            DisplayProperty(shareProperties, propertyName)
        Next
        Console.WriteLine()
    End Sub

    Private Shared Sub DisplaySecurityDescriptor(shareSecurity As ShareSecurity)
        Dim securityDescriptorProperties As IDictionary(Of String, Object) = shareSecurity.GetShareSecurityDescriptorProperties()

        DisplayTextWithSeparator("Share security descriptor")
        DisplayProperty(securityDescriptorProperties, "ControlFlags", Function(value) CType(value, ControlFlags))
        Console.WriteLine()

        Dim accessControlList As IDictionary(Of String, Object)() = securityDescriptorProperties("DACL")
        For i As Integer = 0 To accessControlList.Length - 1
            Dim accessControlEntryProperties As IDictionary(Of String, Object) = accessControlList(i)

            DisplayTextWithSeparator($"Access control entry #{i}")
            DisplayProperty(accessControlEntryProperties, "AccessMask", Function(value) CType(value, FileSystemRights))
            DisplayProperty(accessControlEntryProperties, "AceFlags", Function(value) CType(value, AceFlags))
            DisplayProperty(accessControlEntryProperties, "AceType", Function(value) CType(value, AceType))
            DisplayProperty(accessControlEntryProperties, "Trustee", Function(value) DirectCast(value, IDictionary(Of String, Object))("Name"))
            Console.WriteLine()
        Next
    End Sub

    Private Shared Sub DisplayProperty(properties As IDictionary(Of String, Object), propertyName As String)
        DisplayProperty(properties, propertyName, Nothing)
    End Sub

    Private Shared Sub DisplayProperty(properties As IDictionary(Of String, Object), propertyName As String, selector As Func(Of Object, Object))
        Dim propertyValue As Object = properties(propertyName)
        Dim displayValue As Object = If(
            selector IsNot Nothing,
            selector.Invoke(propertyValue),
            If(propertyValue, "<null>")
        )

        Console.WriteLine($"{propertyName}: {displayValue}")
    End Sub
End Class

给这样的分享...

...将 Full Control 授予 Administrators,将 Read 授予 Everyone,这样调用程序...

SO60271689.exe MyComputer "My Share" MyUser MyPassword AccessDenied "Modify, Synchronize" S-1-5-32-546

...或者像这样...

SO60271689.exe MyComputer "My Share" MyUser MyPassword AccessDenied "Modify, Synchronize" BUILTIN Guests

...产生这样的输出...

Share properties
----------------
Description: This is the share description.
Name: My Share
Path: C:\My Share

Share security descriptor
-------------------------
ControlFlags: DiscretionaryAclPresent, SelfRelative

Access control entry #0
-----------------------
AccessMask: ReadAndExecute, Synchronize
AceFlags: None
AceType: AccessAllowed
Trustee: Everyone

Access control entry #1
-----------------------
AccessMask: FullControl
AceFlags: None
AceType: AccessAllowed
Trustee: Administrators

New access control entry
------------------------
   Type: AccessDenied
 Rights: Modify, Synchronize
    SID: S-1-5-32-546

Share security descriptor
-------------------------
ControlFlags: DiscretionaryAclPresent, SelfRelative

Access control entry #0
-----------------------
AccessMask: Modify, Synchronize
AceFlags: None
AceType: AccessDenied
Trustee: Guests

Access control entry #1
-----------------------
AccessMask: ReadAndExecute, Synchronize
AceFlags: None
AceType: AccessAllowed
Trustee: Everyone

Access control entry #2
-----------------------
AccessMask: FullControl
AceFlags: None
AceType: AccessAllowed
Trustee: Administrators

由于 VB.NET 不是我的母语,这里是我开始使用的等效 C# 代码,以使一切正常...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Security.AccessControl;

namespace SO60271689.CSharp
{
    internal class ShareSecurity
    {
        private string ShareName
        {
            get;
        }

        private ObjectGetOptions GetOptions
        {
            get;
        }

        private ManagementScope Scope
        {
            get;
        }

        public ShareSecurity(string host, string shareName, string username, string password)
        {
            ShareName = shareName;
            GetOptions = new ObjectGetOptions();
            Scope = new ManagementScope(
                new ManagementPath() {
                    NamespacePath = @"root\cimv2",
                    Server = host
                },
                new ConnectionOptions() {
                    // ***** For demonstration purposes only! *****
                    //TODO: Find a secure way to store the remote password
                    Password = password,
                    Username = username
                }
            );
        }

        public IDictionary<string, object> GetShareProperties()
        {
            ManagementPath sharePath = new ManagementPath($"Win32_Share.Name=\"{ShareName}\"");

            using (ManagementObject share = new ManagementObject(Scope, sharePath, GetOptions))
                return GetPropertyDictionary(share);
        }

        public IDictionary<string, object> GetShareSecurityDescriptorProperties()
        {
            using (ManagementObject shareSecurity = GetShareSecurity())
            using (ManagementBaseObject securityDescriptor = GetShareSecurityDescriptor(shareSecurity))
                return GetPropertyDictionary(securityDescriptor);
        }

        public void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, string securityIdentifier)
        {
            AddAccessControlEntry(
                entryType,
                entryRights,
                trustee => trustee["SIDString"] = securityIdentifier
            );
        }

        public void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, string domain, string account)
        {
            AddAccessControlEntry(
                entryType,
                entryRights,
                trustee => {
                    trustee["Domain"] = domain;
                    trustee["Name"] = account;
                }
            );
        }

        /// <param name="trusteeInitializer">Initializes the Win32_Trustee instance for the access control entry to be added.</param>
        private void AddAccessControlEntry(AceType entryType, FileSystemRights entryRights, Action<ManagementObject> trusteeInitializer)
        {
            using (ManagementObject shareSecurity = GetShareSecurity())
            using (ManagementBaseObject securityDescriptor = GetShareSecurityDescriptor(shareSecurity))
            {
                ManagementBaseObject[] accessControlEntries = (ManagementBaseObject[]) securityDescriptor["DACL"];

                // The class must not be created in the remote scope otherwise CreateInstance()
                // throws "System.UnauthorizedAccessException: 'Access is denied'."
                using (ManagementClass accessControlEntryClass = new ManagementClass("Win32_ACE"))
                using (ManagementObject accessControlEntry = accessControlEntryClass.CreateInstance())
                {
                    accessControlEntry["AccessMask"] = (uint) entryRights;
                    accessControlEntry["AceFlags"] = (uint) AceFlags.None;
                    accessControlEntry["AceType"] = (uint) entryType;

                    // The class must not be created in the remote scope otherwise CreateInstance()
                    // throws "System.UnauthorizedAccessException: 'Access is denied'."
                    using (ManagementClass trusteeClass = new ManagementClass("Win32_Trustee"))
                    using (ManagementObject trustee = trusteeClass.CreateInstance())
                    {
                        trusteeInitializer.Invoke(trustee);
                        accessControlEntry["Trustee"] = trustee;

                        // Create a new access control list including the new access control
                        // entry, sorted with Deny entries first (true sorts after false)
                        // https://docs.microsoft.com/windows/win32/secauthz/order-of-aces-in-a-dacl
                        securityDescriptor["DACL"] = accessControlEntries
                            .Append(accessControlEntry)
                            .OrderByDescending(entry => (AceType) (uint) entry["AceType"] == AceType.AccessDenied)
                            .ToArray();

                        SetShareSecurityDescriptor(shareSecurity, securityDescriptor);
                    }
                }
            }
        }

        private ManagementObject GetShareSecurity()
        {
            ManagementPath shareSecurityPath = new ManagementPath($"Win32_LogicalShareSecuritySetting.Name=\"{ShareName}\"");

            return new ManagementObject(Scope, shareSecurityPath, GetOptions);
        }

        private ManagementBaseObject GetShareSecurityDescriptor(ManagementObject shareSecurity)
        {
            // Create an array to store the output parameter
            ManagementBaseObject[] invokeParameters = new ManagementBaseObject[1];
            uint invokeResult = (uint) shareSecurity.InvokeMethod("GetSecurityDescriptor", invokeParameters);

            if (invokeResult == 0)
                return invokeParameters[0];
            else
            {
                //TODO: Handle failure of GetSecurityDescriptor()...
                return null;
            }
        }

        private void SetShareSecurityDescriptor(ManagementObject shareSecurity, ManagementBaseObject securityDescriptor)
        {
            // Create an array to store the input parameter
            ManagementBaseObject[] invokeParameters = new ManagementBaseObject[1] { securityDescriptor };
            uint invokeResult = (uint) shareSecurity.InvokeMethod("SetSecurityDescriptor", invokeParameters);

            if (invokeResult != 0)
            {
                //TODO: Handle failure of SetSecurityDescriptor()...
            }
        }

        private static IDictionary<string, object> GetPropertyDictionary(ManagementBaseObject obj)
        {
            return obj.Properties
                .Cast<PropertyData>()
                .ToDictionary(
                    property => property.Name,
                    // Recursively create dictionaries in place of management objects
                    property => property.Value is ManagementBaseObject[] baseObjectArray
                        ? baseObjectArray.Select(GetPropertyDictionary).ToArray()
                        : property.Value is ManagementBaseObject baseObject
                        ? GetPropertyDictionary(baseObject)
                        : property.Value
                );
        }
    }
}
using System;
using System.Collections.Generic;
using System.Security.AccessControl;

namespace SO60271689.CSharp
{
    class Program
    {
        public static void Main(string[] args)
        {
            if (args == null || args.Length == 0)
                DisplayUsage();
            else if (args.Length < 4)
                DisplayError("Too few arguments.");
            else
            {
                string host = args[0];
                string shareName = args[1];
                string username = args[2];
                // ***** For demonstration purposes only! *****
                //TODO: Find a secure way to store the remote password
                string password = args[3];
                ShareSecurity shareSecurity = new ShareSecurity(host, shareName, username, password);

                DisplayShareProperties(shareSecurity);
                DisplaySecurityDescriptor(shareSecurity);

                if (args.Length > 4)
                {
                    if (args.Length != 7 && args.Length != 8)
                        DisplayError("Argument count mismatch.");
                    else
                    {
                        if (!Enum.TryParse(args[4], true, out AceType entryType))
                            DisplayError($"Invalid <type> value \"{args[4]}\".");
                        else if (!Enum.TryParse<FileSystemRights>(args[5], true, out FileSystemRights entryRights))
                            DisplayError($"Invalid <rights> value \"{args[5]}\".");
                        else
                        {
                            DisplayTextWithSeparator("New access control entry");
                            Console.WriteLine($"   Type: {entryType}");
                            Console.WriteLine($" Rights: {entryRights}");

                            if (args.Length == 7)
                            {
                                string securityIdentifier = args[6];

                                Console.WriteLine($"    SID: {securityIdentifier}");
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, securityIdentifier);
                            }
                            else // args.Length == 8
                            {
                                string domain = args[6];
                                string account = args[7];

                                Console.WriteLine($" Domain: {domain}");
                                Console.WriteLine($"Account: {account}");
                                shareSecurity.AddAccessControlEntry(entryType, entryRights, domain, account);
                            }
                            Console.WriteLine();
                            DisplaySecurityDescriptor(shareSecurity);
                        }
                    }
                }
            }
        }

        private static void DisplayUsage()
        {
            string entryAssemblyPath = System.Reflection.Assembly.GetEntryAssembly().Location;
            string entryAssemblyName = System.IO.Path.GetFileName(entryAssemblyPath);

            Console.WriteLine("Display share properties:");
            Console.WriteLine();
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password>");
            Console.WriteLine();
            Console.WriteLine("Add access control entry:");
            Console.WriteLine();
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <sid>");
            Console.WriteLine($"{entryAssemblyName} <host> <share> <username> <password> <type> <rights> <domain> <account>");
            Console.WriteLine();
            Console.WriteLine($"\t  <type> - A {typeof(AceType).FullName} enumeration value.");
            Console.WriteLine($"\t<rights> - A {typeof(FileSystemRights).FullName} enumeration value.");
            Console.WriteLine("\t   <sid> - An account security identifier.");
            Console.WriteLine();
        }

        private static void DisplayError(string message)
        {
            Console.WriteLine($"ERROR: {message}");
            Console.WriteLine();
            DisplayUsage();
        }

        private static void DisplayTextWithSeparator(string text)
        {
            Console.WriteLine(text);
            Console.WriteLine(new string('=', text.Length));
        }

        private static void DisplayShareProperties(ShareSecurity shareSecurity)
        {
            IDictionary<string, object> shareProperties = shareSecurity.GetShareProperties();

            DisplayTextWithSeparator("Share properties");
            foreach (string propertyName in new string[] { "Description", "Name", "Path" })
                DisplayProperty(shareProperties, propertyName);
            Console.WriteLine();
        }

        private static void DisplaySecurityDescriptor(ShareSecurity shareSecurity)
        {
            IDictionary<string, object> securityDescriptorProperties = shareSecurity.GetShareSecurityDescriptorProperties();

            DisplayTextWithSeparator("Share security descriptor");
            DisplayProperty(securityDescriptorProperties, "ControlFlags", value => (ControlFlags) (uint) value);
            Console.WriteLine();

            IDictionary<string, object>[] accessControlList = (IDictionary<string, object>[]) securityDescriptorProperties["DACL"];
            for (int i = 0; i < accessControlList.Length; i++)
            {
                IDictionary<string, object> accessControlEntryProperties = accessControlList[i];

                DisplayTextWithSeparator($"Access control entry #{i}");
                DisplayProperty(accessControlEntryProperties, "AccessMask", value => (FileSystemRights) (uint) value);
                DisplayProperty(accessControlEntryProperties, "AceFlags", value => (AceFlags) (uint) value);
                DisplayProperty(accessControlEntryProperties, "AceType", value => (AceType) (uint) value);
                DisplayProperty(accessControlEntryProperties, "Trustee", value => ((IDictionary<string, object>) value)["Name"]);
                Console.WriteLine();
            }
        }

        private static void DisplayProperty(IDictionary<string, object> properties, string propertyName)
        {
            DisplayProperty(properties, propertyName, null);
        }

        private static void DisplayProperty(IDictionary<string, object> properties, string propertyName, Func<object, object> selector)
        {
            object propertyValue = properties[propertyName];
            object displayValue = selector != null
                ? selector.Invoke(propertyValue)
                : propertyValue ?? "<null>";

            Console.WriteLine($"{propertyName}: {displayValue}");
        }
    }
}