Top / .NET備忘録 / 99.小ネタ / 10.App.PrevInstance

「二重起動を禁止する方法について」
https://msdn.microsoft.com/ja-jp/library/cc440887.aspx

を見ると

 Protected Shared Sub Main()
     If UBound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)) = 0 Then
         Application.Run(New Form1)
     Else
         Application.Exit()
     End If
 End Sub

のような記述が紹介されていますが、XenApp 等、複数のユーザがログインする環境では、他のユーザが起動していると起動できなくなってしまいます。

プロセスを列挙し、ユーザ名を調べて、自分と同一であれば起動済みと判断します。

WMI を使うと、少し遅いので、API を使うことにします。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

public class App
{
    public static bool PrevInstance {
        get {
            string currentDomain, currentAccount;
            bool result = false;
            using (Process current = Process.GetCurrentProcess()) {
                // 自分のアカウント(ドメイン、ユーザ)を取得
                if (GetAccountFromPID(current.Id, out currentDomain, out currentAccount)) {
                    // 同一モジュール名をもつプロセスを列挙
                    foreach (Process p in Process.GetProcessesByName(current.ProcessName)) {
                        // 自分と違うプロセスID でアカウントが同一なら実行中と判断する
                        if (!result && p.Id != current.Id) {
                            string domainName, accountName;
                            if (GetAccountFromPID(p.Id, out domainName, out accountName)) {
                                if (string.Compare(domainName, currentDomain, true) == 0 &&
                                    string.Compare(accountName, currentAccount, true) == 0) {
                                    result = true;
                                }
                            }
                        }
                        p.Dispose();
                    }
                }
            }
            return result;
        }
    }

    private static bool GetAccountFromPID(int pid, out string domainName, out string accountName) {
        bool result = false;
        domainName = string.Empty;
        accountName = string.Empty;
        IntPtr hProcess = NativeMethods.OpenProcess(
                                NativeMethods.PROCESS_QUERY_INFORMATION,
                                false,
                                pid);
        if (hProcess != IntPtr.Zero) {
            if (GetAccountFromHProcess(hProcess, out domainName, out accountName)) {
                result = true;
            }
            NativeMethods.CloseHandle(hProcess);
        }
        return result;
    }

    private static bool GetAccountFromHProcess(IntPtr hProcess, out string domainName, out string accountName) {
        bool result = false;
        IntPtr hAccessToken;
        domainName = string.Empty;
        accountName = string.Empty;
        if (NativeMethods.OpenProcessToken(
                            hProcess,
                            NativeMethods.TOKEN_QUERY,
                            out hAccessToken)) {
            int bufferSize = 1024;
            IntPtr tokenUserBuffer = Marshal.AllocHGlobal(bufferSize);
            int resultSize;
            if (NativeMethods.GetTokenInformation(
                                    hAccessToken,
                                    NativeMethods.TOKEN_INFORMATION_CLASS.TokenUser,
                                    tokenUserBuffer,
                                    bufferSize,
                                    out resultSize)) {
                NativeMethods.TOKEN_USER ts = (NativeMethods.TOKEN_USER)Marshal.PtrToStructure(
                                                                                    tokenUserBuffer,
                                                                                    typeof(NativeMethods.TOKEN_USER));
                StringBuilder szAccountName = new StringBuilder(1024);
                StringBuilder szDomainName = new StringBuilder(1024);
                int dwAccountSize = szAccountName.Capacity;
                int dwDomainSize = szDomainName.Capacity;
                NativeMethods.SID_NAME_USE snu;
                if (NativeMethods.LookupAccountSid(
                                            null,
                                            ts.User.Sid,
                                            szAccountName,
                                            ref dwAccountSize,
                                            szDomainName,
                                            ref dwDomainSize,
                                            out snu)) {
                    domainName = szDomainName.ToString();
                    accountName = szAccountName.ToString();
                    result = true;
                }
            }
            NativeMethods.CloseHandle(hAccessToken);
            Marshal.FreeHGlobal(tokenUserBuffer);
        }
        return result;
    }

    internal static class NativeMethods
    {
        public const int
        PROCESS_CREATE_PROCESS = 0x0080,
        PROCESS_CREATE_THREAD = 0x0002,
        PROCESS_DUP_HANDLE = 0x0040,
        PROCESS_QUERY_INFORMATION = 0x0400,
        PROCESS_QUERY_LIMITED_INFORMATION = 0x1000,
        PROCESS_SET_INFORMATION = 0x0200,
        PROCESS_SET_QUOTA = 0x0100,
        PROCESS_SUSPEND_RESUME = 0x0800,
        PROCESS_TERMINATE = 0x0001,
        PROCESS_VM_OPERATION = 0x0008,
        PROCESS_VM_READ = 0x0010,
        PROCESS_VM_WRITE = 0x0020,
        DELETE = 0x00010000,
        READ_CONTROL = 0x00020000,
        SYNCHRONIZE = 0x00100000,
        WRITE_DAC = 0x00040000,
        WRITE_OWNER = 0x00080000,
        STANDARD_RIGHTS_READ = 0x00020000,
        STANDARD_RIGHTS_REQUIRED = 0x000f0000,
        PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff);

        public const uint
        TOKEN_ASSIGN_PRIMARY = 0x0001,
        TOKEN_DUPLICATE = 0x0002,
        TOKEN_IMPERSONATE = 0x0004,
        TOKEN_QUERY = 0x0008,
        TOKEN_QUERY_SOURCE = 0x0010,
        TOKEN_ADJUST_PRIVILEGES = 0x0020,
        TOKEN_ADJUST_GROUPS = 0x0040,
        TOKEN_ADJUST_DEFAULT = 0x0080,
        TOKEN_ADJUST_SESSIONID = 0x0100,
        TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY),
        TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
                            TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
                            TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
                            TOKEN_ADJUST_SESSIONID);

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(
             uint processAccess,
             bool bInheritHandle,
             int processId
        );

        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenProcessToken(
            IntPtr ProcessHandle,
            uint DesiredAccess,
            out IntPtr TokenHandle
            );

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

        public enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin,
            TokenElevationType,
            TokenLinkedToken,
            TokenElevation,
            TokenHasRestrictions,
            TokenAccessInformation,
            TokenVirtualizationAllowed,
            TokenVirtualizationEnabled,
            TokenIntegrityLevel,
            TokenUIAccess,
            TokenMandatoryPolicy,
            TokenLogonSid,
            MaxTokenInfoClass
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool GetTokenInformation(
            IntPtr TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength);

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

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

        public enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool LookupAccountSid(
            string lpSystemName,
            IntPtr Sid,
            StringBuilder lpName,
            ref int cchName,
            StringBuilder ReferencedDomainName,
            ref int cchReferencedDomainName,
            out SID_NAME_USE peUse);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetCurrentProcessId();
    }
}



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2016-02-24 (水) 14:50:27 (608d)