/// <summary>
        /// Get's the status of all the privileges on the token specified
        /// </summary>
        /// <param name="token">The process token to get the privilege status on</param>
        /// <returns>Dictionary where the key is the privilege constant and the value is the PrivilegeAttributes flags</returns>
        public static Dictionary <String, PrivilegeAttributes> GetAllPrivilegeInfo(SafeHandle token)
        {
            SafeNativeHandle hToken = null;

            if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query, out hToken))
            {
                throw new Win32Exception("OpenProcessToken() failed");
            }

            using (hToken)
            {
                UInt32 tokenLength = 0;
                NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, new SafeMemoryBuffer(0), 0, out tokenLength);

                NativeHelpers.LUID_AND_ATTRIBUTES[] privileges;
                using (SafeMemoryBuffer privilegesPtr = new SafeMemoryBuffer((int)tokenLength))
                {
                    if (!NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, privilegesPtr, tokenLength, out tokenLength))
                    {
                        throw new Win32Exception("GetTokenInformation() for TOKEN_PRIVILEGES failed");
                    }

                    NativeHelpers.TOKEN_PRIVILEGES privilegeInfo = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
                        privilegesPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_PRIVILEGES));
                    privileges = new NativeHelpers.LUID_AND_ATTRIBUTES[privilegeInfo.PrivilegeCount];
                    PtrToStructureArray(privileges, IntPtr.Add(privilegesPtr.DangerousGetHandle(), Marshal.SizeOf(privilegeInfo.PrivilegeCount)));
                }

                return(privileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => p.Attributes));
            }
        }
        /// <summary>
        /// Do a bulk set of multiple privileges
        /// </summary>
        /// <param name="token">The process token to use when setting the privilege state</param>
        /// <param name="state">A dictionary that contains the privileges to set, the key is the constant name and the value can be;
        ///     true - enable the privilege
        ///     false - disable the privilege
        ///     null - remove the privilege (this cannot be reversed)
        /// </param>
        /// <param name="strict">When true, will fail if one privilege failed to be set, otherwise it will silently continue</param>
        /// <returns>The previous state that can be passed to SetTokenPrivileges to revert the action</returns>
        public static Dictionary <string, bool?> SetTokenPrivileges(SafeHandle token, IDictionary state, bool strict = true)
        {
            NativeHelpers.LUID_AND_ATTRIBUTES[] privilegeAttr = new NativeHelpers.LUID_AND_ATTRIBUTES[state.Count];
            int i = 0;

            foreach (DictionaryEntry entry in state)
            {
                string             key = (string)entry.Key;
                NativeHelpers.LUID luid;
                if (!NativeMethods.LookupPrivilegeValue(null, key, out luid))
                {
                    throw new Win32Exception(String.Format("LookupPrivilegeValue({0}) failed", key));
                }

                PrivilegeAttributes attributes;
                switch ((bool?)entry.Value)
                {
                case true:
                    attributes = PrivilegeAttributes.Enabled;
                    break;

                case false:
                    attributes = PrivilegeAttributes.Disabled;
                    break;

                default:
                    attributes = PrivilegeAttributes.Removed;
                    break;
                }

                privilegeAttr[i].Luid       = luid;
                privilegeAttr[i].Attributes = attributes;
                i++;
            }

            return(AdjustTokenPrivileges(token, privilegeAttr, strict));
        }
        private static Dictionary <string, bool?> AdjustTokenPrivileges(SafeHandle token, NativeHelpers.LUID_AND_ATTRIBUTES[] newState, bool strict)
        {
            bool             disableAllPrivileges;
            SafeMemoryBuffer newStatePtr;

            NativeHelpers.LUID_AND_ATTRIBUTES[] oldStatePrivileges;
            UInt32 returnLength;

            if (newState == null)
            {
                disableAllPrivileges = true;
                newStatePtr          = new SafeMemoryBuffer(0);
            }
            else
            {
                disableAllPrivileges = false;

                // Need to manually marshal the bytes requires for newState as the constant size
                // of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
                // always contains at least 1 entry so we need to calculate the extra size if there are
                // nore than 1 LUID_AND_ATTRIBUTES entry
                int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
                int luidAttrSize        = 0;
                if (newState.Length > 1)
                {
                    luidAttrSize = Marshal.SizeOf(typeof(NativeHelpers.LUID_AND_ATTRIBUTES)) * (newState.Length - 1);
                }
                int    totalSize     = tokenPrivilegesSize + luidAttrSize;
                byte[] newStateBytes = new byte[totalSize];

                // get the first entry that includes the struct details
                NativeHelpers.TOKEN_PRIVILEGES tokenPrivileges = new NativeHelpers.TOKEN_PRIVILEGES()
                {
                    PrivilegeCount = (UInt32)newState.Length,
                    Privileges     = new NativeHelpers.LUID_AND_ATTRIBUTES[1],
                };
                if (newState.Length > 0)
                {
                    tokenPrivileges.Privileges[0] = newState[0];
                }
                int offset = StructureToBytes(tokenPrivileges, newStateBytes, 0);

                // copy the remaining LUID_AND_ATTRIBUTES (if any)
                for (int i = 1; i < newState.Length; i++)
                {
                    offset += StructureToBytes(newState[i], newStateBytes, offset);
                }

                // finally create the pointer to the byte array we just created
                newStatePtr = new SafeMemoryBuffer(newStateBytes.Length);
                Marshal.Copy(newStateBytes, 0, newStatePtr.DangerousGetHandle(), newStateBytes.Length);
            }

            using (newStatePtr)
            {
                SafeNativeHandle hToken;
                if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges, out hToken))
                {
                    throw new Win32Exception("OpenProcessToken() failed with Query and AdjustPrivileges");
                }

                using (hToken)
                {
                    if (!NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, 0, new SafeMemoryBuffer(0), out returnLength))
                    {
                        int errCode = Marshal.GetLastWin32Error();
                        if (errCode != 122) // ERROR_INSUFFICIENT_BUFFER
                        {
                            throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed to get old state size");
                        }
                    }

                    using (SafeMemoryBuffer oldStatePtr = new SafeMemoryBuffer((int)returnLength))
                    {
                        bool res     = NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, returnLength, oldStatePtr, out returnLength);
                        int  errCode = Marshal.GetLastWin32Error();

                        // even when res == true, ERROR_NOT_ALL_ASSIGNED may be set as the last error code
                        // fail if we are running with strict, otherwise ignore those privileges
                        if (!res || ((strict && errCode != 0) || (!strict && !(errCode == 0 || errCode == 0x00000514))))
                        {
                            throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed");
                        }

                        // Marshal the oldStatePtr to the struct
                        NativeHelpers.TOKEN_PRIVILEGES oldState = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
                            oldStatePtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_PRIVILEGES));
                        oldStatePrivileges = new NativeHelpers.LUID_AND_ATTRIBUTES[oldState.PrivilegeCount];
                        PtrToStructureArray(oldStatePrivileges, IntPtr.Add(oldStatePtr.DangerousGetHandle(), Marshal.SizeOf(oldState.PrivilegeCount)));
                    }
                }
            }

            return(oldStatePrivileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => (bool?)p.Attributes.HasFlag(PrivilegeAttributes.Enabled)));
        }