如何以编程方式调用 Windows 权限对话框?

How does one invoke the Windows Permissions dialog programmatically?

我正在尝试编写一个证书管理器,我想管理证书文件的权限。我不想重新发明 Windows 权限对话框的轮子,所以理想情况下会有某种 shell 命令,我可以传递其权限正在管理的项目的路径。然后,我可以调用它并让 shell 负责更新权限。

我看到有人在这里和那里提到了一个 shell 函数,SHObjectProperties,但不清楚如何使用它。任何帮助将不胜感激。

哎呀我的坏事 'attrib' 以前是这样的。

我相信你想做的是"change security descriptors on folders and files"。命令行工具是icacls.

请看:http://en.wikipedia.org/wiki/Cacls

您可以使用 ShellExecuteEx(使用 "properties" 动词和 "Security" 参数)显示 Windows 文件权限对话框。

这将在您的进程中显示如下所示的对话框,文件权限查看和编辑功能将完全正常,就像您通过 Windows 资源管理器 shell 获得此对话框一样:

下面是一个带有 Windows 表单的示例,其中选择了一个文件,然后显示了该文件的安全属性。我使用 this Whosebug answerShellExecuteEx 的 P/Invoke 代码。

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FileSecurityProperties
{
    public partial class FileSelectorForm : Form
    {
        private static bool ShowFileSecurityProperties(string Filename, IntPtr parentHandle)
        {
            SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
            info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
            info.lpVerb = "properties";
            info.lpFile = Filename;
            info.nShow = SW_SHOW;
            info.fMask = SEE_MASK_INVOKEIDLIST;
            info.hwnd = parentHandle;
            info.lpParameters = "Security"; // Opens the file properties on the Security tab
            return ShellExecuteEx(ref info);
        }

        private void fileSelectButton_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                ShowFileSecurityProperties(
                    openFileDialog.FileName,
                    this.Handle); // Pass parent window handle for properties dialog
            }
        }

        #region P/Invoke code for ShellExecuteEx from 
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SHELLEXECUTEINFO
        {
            public int cbSize;
            public uint fMask;
            public IntPtr hwnd;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpVerb;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpFile;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpParameters;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpDirectory;
            public int nShow;
            public IntPtr hInstApp;
            public IntPtr lpIDList;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpClass;
            public IntPtr hkeyClass;
            public uint dwHotKey;
            public IntPtr hIcon;
            public IntPtr hProcess;
        }

        private const int SW_SHOW = 5;
        private const uint SEE_MASK_INVOKEIDLIST = 12;
        #endregion

        #region Irrelevant Windows forms code
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
            this.fileSelectButton = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // openFileDialog1
            // 
            this.openFileDialog.FileName = "";
            // 
            // fileSelectButton
            // 
            this.fileSelectButton.Location = new System.Drawing.Point(52, 49);
            this.fileSelectButton.Name = "fileSelectButton";
            this.fileSelectButton.Size = new System.Drawing.Size(131, 37);
            this.fileSelectButton.TabIndex = 0;
            this.fileSelectButton.Text = "Select file ...";
            this.fileSelectButton.UseVisualStyleBackColor = true;
            this.fileSelectButton.Click += new System.EventHandler(this.fileSelectButton_Click);
            // 
            // FileSelectorForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(248, 162);
            this.Controls.Add(this.fileSelectButton);
            this.Name = "FileSelectorForm";
            this.Text = "File Selector";
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.OpenFileDialog openFileDialog;
        private System.Windows.Forms.Button fileSelectButton;

        public FileSelectorForm()
        {
            InitializeComponent();
        }

        #endregion
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FileSelectorForm());
        }
    }
}

如果您希望单独获得文件权限对话框,而不是作为一般文件属性对话框中的选项卡,可以使用 aclui.dll,例如使用 EditSecurity function,但这不会给你其他要求,即为你处理文件权限,因为你必须提供一个界面,如果你失败,它会获取和设置安全属性那条路线,看起来像很多编码。

这是一个实用程序 class,它允许您仅具有安全性 属性 sheet(并非 shell 显示的所有 sheet)。

您可以在控制台应用程序中这样调用它:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if the dialog looks old fashioned (for example if used in a console app),
        // then add an app.manifest and uncomment the dependency section about Microsoft.Windows.Common-Controls
        PermissionDialog.Show(IntPtr.Zero, @"d:\temp\killroy_was_here.png");
    }
}

或在 winform 应用程序中像这样

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        PermissionDialog.Show(IntPtr.Zero, @"d:\temp\killroy_was_here.png");
    }
}

这是主要的 class。它基本上使用与 shell 相同的东西,但在它自己的 属性 sheet.

public static class PermissionDialog
{
    public static bool Show(IntPtr hwndParent, string path)
    {
        if (path == null)
            throw new ArgumentNullException("path");

        SafePidlHandle folderPidl;
        int hr;
        hr = SHILCreateFromPath(Path.GetDirectoryName(path), out folderPidl, IntPtr.Zero);
        if (hr != 0)
            throw new Win32Exception(hr);

        SafePidlHandle filePidl;
        hr = SHILCreateFromPath(path, out filePidl, IntPtr.Zero);
        if (hr != 0)
            throw new Win32Exception(hr);

        IntPtr file = ILFindLastID(filePidl);

        System.Runtime.InteropServices.ComTypes.IDataObject ido;
        hr = SHCreateDataObject(folderPidl, 1, new IntPtr[] { file }, null, typeof(System.Runtime.InteropServices.ComTypes.IDataObject).GUID, out ido);
        if (hr != 0)
            throw new Win32Exception(hr);

        // if you get a 'no such interface' error here, make sure the running thread is STA
        IShellExtInit sei = (IShellExtInit)new SecPropSheetExt();
        sei.Initialize(IntPtr.Zero, ido, IntPtr.Zero);

        IShellPropSheetExt spse = (IShellPropSheetExt)sei;
        IntPtr securityPage = IntPtr.Zero;
        spse.AddPages((p, lp) =>
        {
            securityPage = p;
            return true;
        }, IntPtr.Zero);

        PROPSHEETHEADER psh = new PROPSHEETHEADER();
        psh.dwSize = Marshal.SizeOf(psh);
        psh.hwndParent = hwndParent;
        psh.nPages = 1;
        psh.phpage = Marshal.AllocHGlobal(IntPtr.Size);
        Marshal.WriteIntPtr(psh.phpage, securityPage);

        // TODO: adjust title & icon here, also check out the available flags
        psh.pszCaption = "Permissions for '" + path + "'";

        IntPtr res;
        try
        {
            res = PropertySheet(ref psh);
        }
        finally
        {
            Marshal.FreeHGlobal(psh.phpage);
        }
        return res == IntPtr.Zero;
    }

    private class SafePidlHandle : SafeHandle
    {
        public SafePidlHandle()
            : base(IntPtr.Zero, true)
        {
        }

        public override bool IsInvalid
        {
            get { return handle == IntPtr.Zero; }
        }

        protected override bool ReleaseHandle()
        {
            if (IsInvalid)
                return false;

            Marshal.FreeCoTaskMem(handle);
            return true;
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct PROPSHEETHEADER
    {
        public int dwSize;
        public int dwFlags;
        public IntPtr hwndParent;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public string pszCaption;
        public int nPages;
        public IntPtr nStartPage;
        public IntPtr phpage;
        public IntPtr pfnCallback;
    }

    [DllImport("shell32.dll")]
    private static extern IntPtr ILFindLastID(SafePidlHandle pidl);

    [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
    private static extern int SHILCreateFromPath(string pszPath, out SafePidlHandle ppidl, IntPtr rgflnOut);

    [DllImport("shell32.dll")]
    private static extern int SHCreateDataObject(SafePidlHandle pidlFolder, int cidl, IntPtr[] apidl, System.Runtime.InteropServices.ComTypes.IDataObject pdtInner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out System.Runtime.InteropServices.ComTypes.IDataObject ppv);

    [DllImport("comctl32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr PropertySheet(ref PROPSHEETHEADER lppsph);

    private delegate bool AddPropSheetPage(IntPtr page, IntPtr lParam);

    [ComImport]
    [Guid("1f2e5c40-9550-11ce-99d2-00aa006e086c")] // this GUID points to the property sheet handler for permissions
    private class SecPropSheetExt
    {
    }

    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214E8-0000-0000-C000-000000000046")]
    private interface IShellExtInit
    {
        void Initialize(IntPtr pidlFolder, System.Runtime.InteropServices.ComTypes.IDataObject pdtobj, IntPtr hkeyProgID);
    }

    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214E9-0000-0000-C000-000000000046")]
    private interface IShellPropSheetExt
    {
        void AddPages([MarshalAs(UnmanagedType.FunctionPtr)] AddPropSheetPage pfnAddPage, IntPtr lParam);
        void ReplacePage(); // not fully defined, we don't use it 
    }
}