using System;
using System.Collections.Generic;
using System.Text;

using System.Security.Principal;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace AccuDemo
{
    public class ImpersonatingOperation
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool LogonUser(string user, string domain, string pwd, int type, int provider, out IntPtr token);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        IntPtr userHandle = IntPtr.Zero;
        WindowsImpersonationContext impersonationContext;
        bool isTrusted;

        public bool Trusted
        {
            get { return isTrusted; }
        }

        public void Go(bool impersonateOnCallStack)
        {
            Logon();

            try
            {
                System.Console.WriteLine("Go (before impersonation): " + WindowsIdentity.GetCurrent().Name);

                if (impersonateOnCallStack)
                {
                    impersonationContext = WindowsIdentity.Impersonate(userHandle);
                }
                else
                {
                    Impersonate();
                }

                System.Console.WriteLine("Go (after impersonation): " + WindowsIdentity.GetCurrent().Name);

                Go();
            }
            finally
            {
                // Clean up
                CloseHandle(userHandle);
                if (impersonationContext != null)
                {
                    impersonationContext.Undo();
                    impersonationContext = null;
                }
            }
        }

        public void BeltAndBraces()
        {
            try
            {
                Go();
            }
            catch
            {
                throw;
            }
        }

        public void Go()
        {
            try
            {
                isTrusted = true;
                ThrowExceptionWhileImpersonating();
            }
            finally
            {
                isTrusted = false;
            }
        }

        private void Logon()
        {
            // Call LogonUser to get a token for the user
            bool loggedOn = LogonUser(
                "administrator",
                "",
                "password",
                2,
                0,
                out userHandle);
            if (!loggedOn)
            {
                int err = Marshal.GetLastWin32Error();
                throw new Win32Exception(err);
            }
        }

        private void Impersonate()
        {
            impersonationContext = WindowsIdentity.Impersonate(userHandle);
        }

        static void ThrowExceptionWhileImpersonating()
        {
            throw new ArgumentOutOfRangeException("Oops");
        }
    }
}
