【问题标题】:How does one invoke the Windows Permissions dialog programmatically?如何以编程方式调用 Windows 权限对话框?
【发布时间】:2015-03-18 02:21:14
【问题描述】:

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

我在这里和那里看到过一些关于 shell 函数 SHObjectProperties 的提及,但对于如何使用它并不清楚。任何帮助将不胜感激。

【问题讨论】:

标签: c# winforms security permissions


【解决方案1】:

您可以使用ShellExecuteEx(使用“属性”动词和“安全”参数)显示 Windows 文件权限对话框。

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

这是一个使用 Windows 窗体的示例,其中选择了一个文件,然后显示该文件的安全属性。我使用了来自this Stackoverflow 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 https://stackoverflow.com/a/1936957/4486839
        [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,但这不会为您提供其他要求,即为您处理文件权限,因为您必须提供一个接口,如果您沿着该路线进行安全属性的获取和设置,而且看起来代码很多。

【讨论】:

    【解决方案2】:

    这是一个实用类,它允许您只拥有安全属性表(不是外壳显示的所有表)。

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

    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");
        }
    }
    

    这是主要课程。它基本上使用与 shell 相同的东西,但在它自己的属性表中。

    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 
        }
    }
    

    【讨论】:

      【解决方案3】:

      Opps 我糟糕的“属性”是过去的样子。

      我相信您要做的是“更改文件夹和文件的安全描述符”。命令行工具是 cacls。

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

      【讨论】:

      • 是的...我正在尝试调用 Windows 对话框来管理它。我试图避免自己编写所有的 ACL 管理和 UI 代码。最坏的情况是,我只会复制 Windows UI 并自己管理 ACL,但这会很痛苦......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-25
      • 1970-01-01
      • 2016-10-29
      • 2011-05-20
      相关资源
      最近更新 更多