C# - 检查程序是否在当前用户下运行

6

我需要检查程序(xyz.exe)是否在运行,但只针对当前用户。使用的任何方法都不能需要提升的权限,并且必须快速运行(因此WMI不可用)。

Process.GetProcessesByName("xyz")返回所有已登录用户的"xyz"结果...但我只关心当前用户。

有什么想法吗?


2
https://dev59.com/5nVC5IYBdhLWcg3wbQbA - DLeh
如果这是一个重复的问题,我还没有看到一个真正回答它的帖子。请提供证据表明这是一个重复的问题。请记住,我明确表示“不使用WMI”。 - ltwally
请查看上面第二条评论中的链接,该链接不使用WMI。第一个链接使用了WMI,这就是为什么我没有将其作为重复链接的原因。 - Ken White
@KenWhite:那就是我得到使用Process.GetProcessesByName的想法的地方。但它并没有提供任何过滤用户列表的方法。这正是我正在寻找的。也许我很蠢,但是考虑到我正在寻找超出该链接所包含内容的东西,因此此线程不是重复的。 - ltwally
sameAsthisSession相关的部分匹配当前会话。当前会话是当前用户的会话,因此所有与当前会话匹配的会话ID都应该是正在为当前用户运行的会话。(如果您的代码基于此处的另一篇帖子,请在问题中提供链接以供参考。) - Ken White
显示剩余3条评论
4个回答

11
使用当前流程SessionId来过滤流程列表:
    public static bool IsProcessRunningSameSession(string processName)
    {
        var currentSessionID = Process.GetCurrentProcess().SessionId;
        return Process.GetProcessesByName(processName).Where(p => p.SessionId == currentSessionID).Any();
    }

0

我在这里找到了答案: http://dotbay.blogspot.com/2009/06/finding-owner-of-process-in-c.html

如果那个博客不再存在,我会复制/粘贴它。

////
        // 'WindowsIdentity' Extension Method Demo: 
        //   Enumerate all running processes with their associated Windows Identity
        ////

        foreach (var p in Process.GetProcesses())
        {
            string processName;

            try
            {
                processName = p.WindowsIdentity().Name;

            }
            catch (Exception ex)
            {

                processName = ex.Message; // Probably "Access is denied"
            }

            Console.WriteLine(p.ProcessName + " (" + processName + ")");
        }

这是对应的类:
//-----------------------------------------------------------------------
// <copyright file="ProcessExtensions.cs" company="DockOfTheBay">
//     http://www.dotbay.be
// </copyright>
// <summary>Defines the ProcessExtensions class.</summary>
//-----------------------------------------------------------------------

namespace DockOfTheBay
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security.Principal;

    /// <summary>
    /// Extension Methods for the System.Diagnostics.Process Class.
    /// </summary>
    public static class ProcessExtensions
    {
        /// <summary>
        /// Required to query an access token.
        /// </summary>
        private static uint TOKEN_QUERY = 0x0008;

        /// <summary>
        /// Returns the WindowsIdentity associated to a Process
        /// </summary>
        /// <param name="process">The Windows Process.</param>
        /// <returns>The WindowsIdentity of the Process.</returns>
        /// <remarks>Be prepared for 'Access Denied' Exceptions</remarks>
        public static WindowsIdentity WindowsIdentity(this Process process)
        {
            IntPtr ph = IntPtr.Zero;
            WindowsIdentity wi = null;
            try
            {
                OpenProcessToken(process.Handle, TOKEN_QUERY, out ph);
                wi = new WindowsIdentity(ph);
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (ph != IntPtr.Zero)
                {
                    CloseHandle(ph);
                }
            }

            return wi;
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);

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

对此,我只是这样做:if(processName.Contains(Environment.UserName)){...} - ltwally
我发现一个稍微更好的解决方案,可以更好地处理活动目录,而不是使用 Environment.UserName,可以使用 WindowsIdentity.GetCurrent().Name - Todd A. Stedel

0

这是完整的程序。它是一个命令行C#应用程序。它很丑,没有注释。但它能工作。你输入一个EXE的名称(包括路径),它会检查它是否已经在运行,如果没有,就启动它。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;


namespace SingleRun
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = "";
            var prog = "";
            if (args.Length == 0) {
                MessageBox.Show("Please include a program to start.\n\nExample: \nSingleRun.exe \"C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe\"", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                System.Environment.Exit(1);
            }else{
                path = args[0];
                if (!File.Exists(path)) {
                    MessageBox.Show("\"" + path + "\" does not exist.\nPlease check the location.\nAnything with spaces in it needs to be inside double-quotes.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    System.Environment.Exit(1);
                }else{
                    var splits = path.Split('\\');
                    prog = splits[splits.Length - 1];
                    foreach (var p in Process.GetProcessesByName(prog.Replace(".exe",""))) {
                        string processOwner;
                        try {
                            processOwner = p.WindowsIdentity().Name;
                        }
                        catch (Exception ex) {
                            processOwner = ex.Message; // Probably "Access is denied"
                        }
                        if (processOwner.Contains(Environment.UserName)) {
                            MessageBox.Show("Program already running with PID " + p.Id, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            System.Environment.Exit(1);
                        }
                    }
                    Process newProcess = Process.Start(path);
                    Console.WriteLine("Launching " + prog + " with PID: " + newProcess.Id);
                }
            }
        }
    }

    /// <summary>
    /// Extension Methods for the System.Diagnostics.Process Class.
    /// </summary>
    public static class ProcessExtensions {
        /// <summary>
        /// Required to query an access token.
        /// </summary>
        private static uint TOKEN_QUERY = 0x0008;

        /// <summary>
        /// Returns the WindowsIdentity associated to a Process
        /// </summary>
        /// <param name="process">The Windows Process.</param>
        /// <returns>The WindowsIdentity of the Process.</returns>
        /// <remarks>Be prepared for 'Access Denied' Exceptions</remarks>
        public static WindowsIdentity WindowsIdentity(this Process process) {
            IntPtr ph = IntPtr.Zero;
            WindowsIdentity wi = null;
            try {
                OpenProcessToken(process.Handle, TOKEN_QUERY, out ph);
                wi = new WindowsIdentity(ph);
            }
            catch (Exception) {
                throw;
            }
            finally {
                if (ph != IntPtr.Zero) {
                    CloseHandle(ph);
                }
            }

            return wi;
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);

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

0

以上代码运行良好。但是如果您只想知道当前用户是否看到了打开的应用程序:如果进程不是来自当前用户,则在尝试获取句柄时已经出现异常。 因此,您可以使用此扩展更简单地完成此操作:

    public static bool ProcessAccessibleForCurrentUser(this Process process)
    {
        try
        {
            var ptr = process.Handle;
            return true;
        }
        catch
        {
            return false;
        }
    }

如果当前用户具有管理员权限,则可以在不生成异常的情况下获取句柄,因此这种简化方法不足以解决问题。 - nwsmith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接