This project has moved and is read-only. For the latest updates, please go here.

Support to concurrent users

Dec 12, 2013 at 10:49 PM
First of all, thanks Lordamit for this quite useful tool. I was about to write one myself to help my development team to work with IISExpress in a similar way to what they're use to work with the plain old IIS; then I decided to see if someone else thought the same and here you are.

I wanted to suggest an improvement, so that the IISEM supports concurrent users (which is our case that we share a development server). Since there's a check to avoid multiple instances of the console running at the same time that prevented the multiple users working in parallel. I did a small amendment in the code of the IISEMSingleton from:
        internal static bool InstanceAlreadyExists()
        {
            var processes = Process.GetProcessesByName("iisexpressmanager");
            if (processes.Length > 1) return true;
            return false;
        }
to
        internal static bool InstanceAlreadyExists()
        {
            var currentUserIdentity = WindowsIdentity.GetCurrent();

            return Process.GetProcessesByName("iisexpressmanager").Count(x => x.IsOwnedBy(currentUserIdentity)) > 1;
        }
where the IsOwnedBy is an extension method provided by a class based on the code that you can find in this article and it looks like this:
    static class ProcessExtensions
    {
        public const int TOKEN_QUERY = 0X00000008;

        const int ERROR_NO_MORE_ITEMS = 259;

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId
        }

        [StructLayout(LayoutKind.Sequential)]
        struct TOKEN_USER
        {
            public _SID_AND_ATTRIBUTES User;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct _SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public int Attributes;
        }

        [DllImport("advapi32")]
        static extern bool OpenProcessToken(
            IntPtr ProcessHandle, // handle to process
            int DesiredAccess, // desired access to process
            ref IntPtr TokenHandle // handle to open access token
        );

        [DllImport("kernel32")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32", CharSet = CharSet.Auto)]
        static extern bool GetTokenInformation(
            IntPtr hToken,
            TOKEN_INFORMATION_CLASS tokenInfoClass,
            IntPtr TokenInformation,
            int tokeInfoLength,
            ref int reqLength
        );

        [DllImport("kernel32")]
        static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32", CharSet = CharSet.Auto)]
        static extern bool ConvertSidToStringSid(
            IntPtr pSID,
            [In, Out, MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid
        );

        [DllImport("advapi32", CharSet = CharSet.Auto)]
        static extern bool ConvertStringSidToSid(
            [In, MarshalAs(UnmanagedType.LPTStr)] string pStringSid,
            ref IntPtr pSID
        );

        /// <span class="code-SummaryComment"><summary></span>
        /// Collect User Info
        /// <span class="code-SummaryComment"></summary></span>
        /// <span class="code-SummaryComment"><param name="pToken">Process Handle</param></span>
        public static bool DumpUserInfo(IntPtr pToken, out IntPtr SID)
        {
            int Access = TOKEN_QUERY;
            IntPtr procToken = IntPtr.Zero;
            bool ret = false;
            SID = IntPtr.Zero;
            try
            {
                if (OpenProcessToken(pToken, Access, ref procToken))
                {
                    ret = ProcessTokenToSid(procToken, out SID);
                    CloseHandle(procToken);
                }
                return ret;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private static bool ProcessTokenToSid(IntPtr token, out IntPtr SID)
        {
            TOKEN_USER tokUser;
            const int bufLength = 256;
            IntPtr tu = Marshal.AllocHGlobal(bufLength);
            bool ret = false;
            SID = IntPtr.Zero;
            try
            {
                int cb = bufLength;
                ret = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenUser, tu, cb, ref cb);
                if (ret)
                {
                    tokUser = (TOKEN_USER)Marshal.PtrToStructure(tu, typeof(TOKEN_USER));
                    SID = tokUser.User.Sid;
                }
                return ret;
            }
            catch (Exception)
            {
                return false;
            }
            finally
            {
                Marshal.FreeHGlobal(tu);
            }
        }

        public static string GetOwnerSID(this Process process)
        {
            IntPtr _SID = IntPtr.Zero;
            var result = String.Empty;
            try
            {
                if (DumpUserInfo(process.Handle, out _SID))
                    ConvertSidToStringSid(_SID, ref result);
            }
            catch
            {
                result = null;
            }

            return result;
        }

        public static bool IsOwnedBy(this Process process, WindowsIdentity userIdentity)
        {
            return (String.Compare(process.GetOwnerSID(), userIdentity.User.Value, true) == 0);
        }
    }
As I've said, it's just an improvement but wanted to share it back with you as you did a great work with the rest. Regards