无需 Process.start 打开网站

发布于 2024-09-25 05:19:10 字数 229 浏览 4 评论 0原文

如何在浏览器中不使用 Process.start(...) 打开网站 URL:

System.Diagnostics.Process.Start(@"http://www.google.com");

我无法在以下位置使用 Process.Start() Windows服务,我不知道为什么。

How to open a website URL in browser without of Process.start(...) :

System.Diagnostics.Process.Start(@"http://www.google.com");

I can not use Process.Start() in Windows Service , I do not no know why.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

星光不落少年眉 2024-10-02 05:19:10

请参阅问题“Windows 服务如何执行GUI 应用程序?”:

使用 WTSEnumerateSessions 查找正确的桌面,然后使用 CreateProcessAsUser 在该桌面上启动应用程序

另请注意,您不应该这样做:)

如果您所做的只是启动 URL,则命令可能是

cmd.exe /c start http://example.com/

如果您想禁止短暂显示的命令提示符窗口,您可以设置 wShowWindow 字段a href="http://msdn.microsoft.com/en-us/library/ms686331%28v=VS.85%29.aspx" rel="nofollow noreferrer">STARTUPINFO 结构 到 SW_HIDE,或在 .NET 中设置 ProcessStartInfo.WindowStyle 属性设置为 ProcessWindowStyle.Hidden

See the answer to the question "How can a Windows service execute a GUI application?":

use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop

Note also the opinion that you shouldn't do this :)

If all you are doing is launching a URL, the command might be

cmd.exe /c start http://example.com/

If you want to suppress the briefly-displayed Command Prompt window, you can set the wShowWindow field of the STARTUPINFO structure to SW_HIDE, or in .NET set the ProcessStartInfo.WindowStyle property to ProcessWindowStyle.Hidden.

离线来电— 2024-10-02 05:19:10

服务在隔离会话中运行。该会话有自己的桌面,很像登录屏幕。然而,用户永远无法查看它。这是一项非常基本的安全措施,服务通常使用非常特权的帐户运行。您可以使用 Process.Start(),用户将永远无法看到程序的 UI。

这不是一个真正的问题,在服务中启动浏览器是零意义的。

Services run in an isolated session. That session has its own desktop, much like the login screen. A user however can never look at it. This is a very basic security measure, services typically run with a very privileged account. You can use Process.Start(), the user just will never be able to see the UI of the program.

This is not a real problem, it makes zero sense to start a browser in a service.

铃予 2024-10-02 05:19:10

服务不使用桌面运行,因此我不建议尝试打开浏览器。

如果您只需要相关网站的信息,为了下载和解析信息,您可能需要考虑使用 WebClient 而不是浏览器。这将允许您从任何 Uri 下载,并在服务中解析结果。

Services don't run using the Desktop, so I would not recommend trying to open a browser.

If you just need information from the website in question, in order to download and parse information, you might want to consider using a WebClient instead of a browser. This will allow you to download from any Uri, and parse the results in a service.

街角迷惘 2024-10-02 05:19:10

Windows 7 上的服务无法以任何方式与桌面交互。

因此,您需要的是一个小进程,它将使用某种与服务通信的方法,为登录的用户启动它并等待服务消息。您可以在启动组或任何其他方式中启动它以达到此目的,只需将其设置得非常小并且不引人注目即可。当然,如果您希望它被注意到,您可以使用托盘图标,甚至某些状态窗口。

服务消息可以像在服务和用户进程之间共享的目录中写入带有 URL 的文件一样简单。这样您就可以获得所需的内容并与大多数 Windows 版本保持兼容。

Services on Windows 7 CAN'T interact with desktop in any way.

So, what you need is a small process that will use some method of communication with the service, start it for the user that is logged on and wait for service message. You can start it in Startup group or any other means for that purpose, just make it very small and unnoticeable. Of course, if you want it to bi noticed, you can use tray icon for it or even some status window.

Service message could be something as simple as writing a file with an URL in the directory that is shared between service and the user process. That way you'll get what you need and stay compatible with most Windows versions.

缘字诀 2024-10-02 05:19:10

此处的固定代码: http:// 18and5.blogspot.com/2008/01/i-hope-my-frustration-can-help-someone.html
我必须修复参数处理,以便下面的示例能够实际工作。

using System;
using System.Reflection;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Diagnostics;


namespace Common.Utilities.Processes
{
    public class ProcessUtilities
    {
        /*** Imports ***/
        #region Imports

        [DllImport("advapi32.dll", EntryPoint = "AdjustTokenPrivileges", SetLastError = true)]
        public static extern bool AdjustTokenPrivileges(IntPtr in_hToken, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

        [DllImport("advapi32.dll", EntryPoint = "OpenProcessToken", SetLastError = true)]
        public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

        [DllImport("advapi32.dll", EntryPoint = "LookupPrivilegeValue", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

        [DllImport("userenv.dll", EntryPoint = "CreateEnvironmentBlock", SetLastError = true)]
        public static extern bool CreateEnvironmentBlock(out IntPtr out_ptrEnvironmentBlock, IntPtr in_ptrTokenHandle, bool in_bInheritProcessEnvironment);

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("wtsapi32.dll", EntryPoint = "WTSQueryUserToken", SetLastError = true)]
        public static extern bool WTSQueryUserToken(UInt32 in_nSessionID, out IntPtr out_ptrTokenHandle);

        [DllImport("kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId", SetLastError = true)]
        public static extern uint WTSGetActiveConsoleSessionId();

        [DllImport("Wtsapi32.dll", EntryPoint = "WTSQuerySessionInformation", SetLastError = true)]
        public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out uint pBytesReturned);

        [DllImport("wtsapi32.dll", EntryPoint = "WTSFreeMemory", SetLastError = false)]
        public static extern void WTSFreeMemory(IntPtr memory);

        [DllImport("userenv.dll", EntryPoint = "LoadUserProfile", SetLastError = true)]
        public static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);

        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool CreateProcessAsUser(IntPtr in_ptrUserTokenHandle, string in_strApplicationName, string in_strCommandLine, ref SECURITY_ATTRIBUTES in_oProcessAttributes, ref SECURITY_ATTRIBUTES in_oThreadAttributes, bool in_bInheritHandles, CreationFlags in_eCreationFlags, IntPtr in_ptrEnvironmentBlock, string in_strCurrentDirectory, ref STARTUPINFO in_oStartupInfo, ref PROCESS_INFORMATION in_oProcessInformation);

        #endregion //Imports

        /*** Delegates ***/

        /*** Structs ***/
        #region Structs

        [StructLayout(LayoutKind.Sequential)]
        public struct LUID
        {
            public uint m_nLowPart;
            public uint m_nHighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_PRIVILEGES
        {
            public int m_nPrivilegeCount;
            public LUID m_oLUID;
            public int m_nAttributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROFILEINFO
        {
            public int dwSize;
            public int dwFlags;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpUserName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpProfilePath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpDefaultPath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpServerName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpPolicyPath;
            public IntPtr hProfile;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessID;
            public Int32 dwThreadID;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        #endregion //Structs

        /*** Classes ***/

        /*** Enums ***/
        #region Enums

        public enum CreationFlags
        {
            CREATE_SUSPENDED = 0x00000004,
            CREATE_NEW_CONSOLE = 0x00000010,
            CREATE_NEW_PROCESS_GROUP = 0x00000200,
            CREATE_UNICODE_ENVIRONMENT = 0x00000400,
            CREATE_SEPARATE_WOW_VDM = 0x00000800,
            CREATE_DEFAULT_ERROR_MODE = 0x04000000,
        }

        public enum WTS_INFO_CLASS
        {
            WTSInitialProgram,
            WTSApplicationName,
            WTSWorkingDirectory,
            WTSOEMId,
            WTSSessionId,
            WTSUserName,
            WTSWinStationName,
            WTSDomainName,
            WTSConnectState,
            WTSClientBuildNumber,
            WTSClientName,
            WTSClientDirectory,
            WTSClientProductId,
            WTSClientHardwareId,
            WTSClientAddress,
            WTSClientDisplay,
            WTSClientProtocolType
        }

        #endregion //Enums

        /*** Defines ***/
        #region Defines

        private const int TOKEN_QUERY = 0x08;
        private const int TOKEN_ADJUST_PRIVILEGES = 0x20;
        private const int SE_PRIVILEGE_ENABLED = 0x02;

        public const int ERROR_NO_TOKEN = 1008;
        public const int RPC_S_INVALID_BINDING = 1702;

        #endregion //Defines

        /*** Methods ***/
        #region Methods

        /*
             If you need to give yourself permissions to inspect processes for their modules,
             and create tokens without worrying about what account you're running under,
             this is the method for you :) (such as the token privilege "SeDebugPrivilege")
         */
        static public bool AdjustProcessTokenPrivileges(IntPtr in_ptrProcessHandle, string in_strTokenToEnable)
        {
            IntPtr l_hProcess = IntPtr.Zero;
            IntPtr l_hToken = IntPtr.Zero;
            LUID l_oRestoreLUID;
            TOKEN_PRIVILEGES l_oTokenPrivileges;

            Debug.Assert(in_ptrProcessHandle != IntPtr.Zero);

            //Get the process security token
            if (false == OpenProcessToken(in_ptrProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out l_hToken))
            {
                return false;
            }

            //Lookup the LUID for the privilege we need
            if (false == LookupPrivilegeValue(String.Empty, in_strTokenToEnable, out l_oRestoreLUID))
            {
                return false;
            }

            //Adjust the privileges of the current process to include the new privilege
            l_oTokenPrivileges.m_nPrivilegeCount = 1;
            l_oTokenPrivileges.m_oLUID = l_oRestoreLUID;
            l_oTokenPrivileges.m_nAttributes = SE_PRIVILEGE_ENABLED;

            if (false == AdjustTokenPrivileges(l_hToken, false, ref l_oTokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
            {
                return false;
            }

            return true;
        }

        /*
            Start a process the simplest way you can imagine
        */
        static public int SimpleProcessStart(string in_strTarget, string in_strArguments)
        {
            Process l_oProcess = new Process();
            Debug.Assert(l_oProcess != null);

            l_oProcess.StartInfo.FileName = in_strTarget;
            l_oProcess.StartInfo.Arguments = in_strArguments;

            if (true == l_oProcess.Start())
            {
                return l_oProcess.Id;
            }

            return -1;
        }

        /*
            All the magic is in the call to WTSQueryUserToken, it saves you changing DACLs,
            process tokens, pulling the SID, manipulating the Windows Station and Desktop
            (and its DACLs) - if you don't know what those things are, you're lucky and should
            be on your knees thanking God at this moment.

            DEV NOTE:  This method currently ASSumes that it should impersonate the user
                              who is logged into session 1 (if more than one user is logged in, each
                              user will have a session of their own which means that if user switching
                              is going on, this method could start a process whose UI shows up in
                              the session of the user who is not actually using the machine at this
                              moment.)

            DEV NOTE 2:    If the process being started is a binary which decides, based upon
                                the user whose session it is being created in, to relaunch with a
                                different integrity level (such as Internet Explorer), the process
                                id will change immediately and the Process Manager will think
                                that the process has died (because in actuality the process it
                                launched DID in fact die only that it was due to self-termination)
                                This means beware of using this service to startup such applications
                                although it can connect to them to alarm in case of failure, just
                                make sure you don't configure it to restart it or you'll get non
                                stop process creation ;)
        */
        static public int CreateUIProcessForServiceRunningAsLocalSystem(string in_strTarget, string in_strArguments)
        {
            PROCESS_INFORMATION l_oProcessInformation = new PROCESS_INFORMATION();
            SECURITY_ATTRIBUTES l_oSecurityAttributes = new SECURITY_ATTRIBUTES();
            STARTUPINFO l_oStartupInfo = new STARTUPINFO();
            PROFILEINFO l_oProfileInfo = new PROFILEINFO();
            IntPtr l_ptrUserToken = new IntPtr(0);
            uint l_nActiveUserSessionId = 0xFFFFFFFF;
            string l_strActiveUserName = "";
            int l_nProcessID = -1;
            IntPtr l_ptrBuffer = IntPtr.Zero;
            uint l_nBytes = 0;

            try
            {
                //The currently active user is running what session?
                l_nActiveUserSessionId = WTSGetActiveConsoleSessionId();

                if (l_nActiveUserSessionId == 0xFFFFFFFF)
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSGetActiveConsoleSessionId failed,  GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                if (false == WTSQuerySessionInformation(IntPtr.Zero, (int)l_nActiveUserSessionId, WTS_INFO_CLASS.WTSUserName, out l_ptrBuffer, out l_nBytes))
                {
                    int l_nLastError = Marshal.GetLastWin32Error();

                    //On earlier operating systems from Vista, when no one is logged in, you get RPC_S_INVALID_BINDING which is ok, we just won't impersonate
                    if (l_nLastError != RPC_S_INVALID_BINDING)
                    {
                        throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQuerySessionInformation failed,  GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                    }

                    //No one logged in so let's just do this the simple way
                    return SimpleProcessStart(in_strTarget, in_strArguments);
                }

                l_strActiveUserName = Marshal.PtrToStringAnsi(l_ptrBuffer);
                WTSFreeMemory(l_ptrBuffer);

                //We are supposedly running as a service so we're going to be running in session 0 so get a user token from the active user session
                if (false == WTSQueryUserToken((uint)l_nActiveUserSessionId, out l_ptrUserToken))
                {
                    int l_nLastError = Marshal.GetLastWin32Error();

                    //Remember, sometimes nobody is logged in (especially when we're set to Automatically startup) you should get error code 1008 (no user token available)
                    if (ERROR_NO_TOKEN != l_nLastError)
                    {
                        //Ensure we're running under the local system account
                        WindowsIdentity l_oIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();

                        if ("NT AUTHORITY\\SYSTEM" != l_oIdentity.Name)
                        {
                            throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQueryUserToken failed and querying the process' account identity results in an identity which does not match 'NT AUTHORITY\\SYSTEM' but instead returns the name:" + l_oIdentity.Name + "  GetLastError returns: " + l_nLastError.ToString());
                        }

                        throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQueryUserToken failed, GetLastError returns: " + l_nLastError.ToString());
                    }

                    //No one logged in so let's just do this the simple way
                    return SimpleProcessStart(in_strTarget, in_strArguments);
                }

                //Create an appropriate environment block for this user token (if we have one)
                IntPtr l_ptrEnvironment = IntPtr.Zero;

                Debug.Assert(l_ptrUserToken != IntPtr.Zero);

                if (false == CreateEnvironmentBlock(out l_ptrEnvironment, l_ptrUserToken, false))
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to CreateEnvironmentBlock failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                l_oSecurityAttributes.Length = Marshal.SizeOf(l_oSecurityAttributes);
                l_oStartupInfo.cb = Marshal.SizeOf(l_oStartupInfo);

                //DO NOT set this to "winsta0\\default" (even though many online resources say to do so)
                l_oStartupInfo.lpDesktop = String.Empty;
                l_oProfileInfo.dwSize = Marshal.SizeOf(l_oProfileInfo);
                l_oProfileInfo.lpUserName = l_strActiveUserName;

                //Remember, sometimes nobody is logged in (especially when we're set to Automatically startup)
                if (false == LoadUserProfile(l_ptrUserToken, ref l_oProfileInfo))
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to LoadUserProfile failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                if (false == CreateProcessAsUser(l_ptrUserToken, in_strTarget, in_strTarget + " " + in_strArguments, ref l_oSecurityAttributes, ref l_oSecurityAttributes, false, CreationFlags.CREATE_UNICODE_ENVIRONMENT, l_ptrEnvironment, null, ref l_oStartupInfo, ref l_oProcessInformation))
                {
                    //System.Diagnostics.EventLog.WriteEntry( "CreateProcessAsUser FAILED", Marshal.GetLastWin32Error().ToString() );
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to CreateProcessAsUser failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                l_nProcessID = l_oProcessInformation.dwProcessID;
            }
            catch (Exception l_oException)
            {
                throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "An unhandled exception was caught spawning the process, the exception was: " + l_oException.Message);
            }
            finally
            {
                if (l_oProcessInformation.hProcess != IntPtr.Zero)
                {
                    CloseHandle(l_oProcessInformation.hProcess);
                }
                if (l_oProcessInformation.hThread != IntPtr.Zero)
                {
                    CloseHandle(l_oProcessInformation.hThread);
                }
            }

            return l_nProcessID;
        }

        #endregion //Methods
    }
}

这是你必须做的调用:

Common.Utilities.Processes.ProcessUtilities.CreateUIProcessForServiceRunningAsLocalSystem(
            @"C:\Windows\System32\cmd.exe", 
            " /c \"start http://www.google.com\""
);

我已经在我自己的系统上测试了它,它的工作方式就像一个魅力(启用了 UAC 的 Windows 7 x64)

但是我建议创建一个小型存根应用程序,它不会使 cmd 窗口闪烁。并且它将接受 url 作为参数。
另外,您不应该像我在示例中那样使用 cmd.exe 的硬编码路径。不管代码如何工作,我希望其余的应该很清楚:-)

HTH

Fixed code from here: http://18and5.blogspot.com/2008/01/i-hope-my-frustration-can-help-someone.html
I had to fix the parameter handling that the example below will actually work.

using System;
using System.Reflection;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Diagnostics;


namespace Common.Utilities.Processes
{
    public class ProcessUtilities
    {
        /*** Imports ***/
        #region Imports

        [DllImport("advapi32.dll", EntryPoint = "AdjustTokenPrivileges", SetLastError = true)]
        public static extern bool AdjustTokenPrivileges(IntPtr in_hToken, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

        [DllImport("advapi32.dll", EntryPoint = "OpenProcessToken", SetLastError = true)]
        public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

        [DllImport("advapi32.dll", EntryPoint = "LookupPrivilegeValue", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

        [DllImport("userenv.dll", EntryPoint = "CreateEnvironmentBlock", SetLastError = true)]
        public static extern bool CreateEnvironmentBlock(out IntPtr out_ptrEnvironmentBlock, IntPtr in_ptrTokenHandle, bool in_bInheritProcessEnvironment);

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("wtsapi32.dll", EntryPoint = "WTSQueryUserToken", SetLastError = true)]
        public static extern bool WTSQueryUserToken(UInt32 in_nSessionID, out IntPtr out_ptrTokenHandle);

        [DllImport("kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId", SetLastError = true)]
        public static extern uint WTSGetActiveConsoleSessionId();

        [DllImport("Wtsapi32.dll", EntryPoint = "WTSQuerySessionInformation", SetLastError = true)]
        public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out uint pBytesReturned);

        [DllImport("wtsapi32.dll", EntryPoint = "WTSFreeMemory", SetLastError = false)]
        public static extern void WTSFreeMemory(IntPtr memory);

        [DllImport("userenv.dll", EntryPoint = "LoadUserProfile", SetLastError = true)]
        public static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);

        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool CreateProcessAsUser(IntPtr in_ptrUserTokenHandle, string in_strApplicationName, string in_strCommandLine, ref SECURITY_ATTRIBUTES in_oProcessAttributes, ref SECURITY_ATTRIBUTES in_oThreadAttributes, bool in_bInheritHandles, CreationFlags in_eCreationFlags, IntPtr in_ptrEnvironmentBlock, string in_strCurrentDirectory, ref STARTUPINFO in_oStartupInfo, ref PROCESS_INFORMATION in_oProcessInformation);

        #endregion //Imports

        /*** Delegates ***/

        /*** Structs ***/
        #region Structs

        [StructLayout(LayoutKind.Sequential)]
        public struct LUID
        {
            public uint m_nLowPart;
            public uint m_nHighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_PRIVILEGES
        {
            public int m_nPrivilegeCount;
            public LUID m_oLUID;
            public int m_nAttributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROFILEINFO
        {
            public int dwSize;
            public int dwFlags;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpUserName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpProfilePath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpDefaultPath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpServerName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String lpPolicyPath;
            public IntPtr hProfile;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessID;
            public Int32 dwThreadID;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        #endregion //Structs

        /*** Classes ***/

        /*** Enums ***/
        #region Enums

        public enum CreationFlags
        {
            CREATE_SUSPENDED = 0x00000004,
            CREATE_NEW_CONSOLE = 0x00000010,
            CREATE_NEW_PROCESS_GROUP = 0x00000200,
            CREATE_UNICODE_ENVIRONMENT = 0x00000400,
            CREATE_SEPARATE_WOW_VDM = 0x00000800,
            CREATE_DEFAULT_ERROR_MODE = 0x04000000,
        }

        public enum WTS_INFO_CLASS
        {
            WTSInitialProgram,
            WTSApplicationName,
            WTSWorkingDirectory,
            WTSOEMId,
            WTSSessionId,
            WTSUserName,
            WTSWinStationName,
            WTSDomainName,
            WTSConnectState,
            WTSClientBuildNumber,
            WTSClientName,
            WTSClientDirectory,
            WTSClientProductId,
            WTSClientHardwareId,
            WTSClientAddress,
            WTSClientDisplay,
            WTSClientProtocolType
        }

        #endregion //Enums

        /*** Defines ***/
        #region Defines

        private const int TOKEN_QUERY = 0x08;
        private const int TOKEN_ADJUST_PRIVILEGES = 0x20;
        private const int SE_PRIVILEGE_ENABLED = 0x02;

        public const int ERROR_NO_TOKEN = 1008;
        public const int RPC_S_INVALID_BINDING = 1702;

        #endregion //Defines

        /*** Methods ***/
        #region Methods

        /*
             If you need to give yourself permissions to inspect processes for their modules,
             and create tokens without worrying about what account you're running under,
             this is the method for you :) (such as the token privilege "SeDebugPrivilege")
         */
        static public bool AdjustProcessTokenPrivileges(IntPtr in_ptrProcessHandle, string in_strTokenToEnable)
        {
            IntPtr l_hProcess = IntPtr.Zero;
            IntPtr l_hToken = IntPtr.Zero;
            LUID l_oRestoreLUID;
            TOKEN_PRIVILEGES l_oTokenPrivileges;

            Debug.Assert(in_ptrProcessHandle != IntPtr.Zero);

            //Get the process security token
            if (false == OpenProcessToken(in_ptrProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out l_hToken))
            {
                return false;
            }

            //Lookup the LUID for the privilege we need
            if (false == LookupPrivilegeValue(String.Empty, in_strTokenToEnable, out l_oRestoreLUID))
            {
                return false;
            }

            //Adjust the privileges of the current process to include the new privilege
            l_oTokenPrivileges.m_nPrivilegeCount = 1;
            l_oTokenPrivileges.m_oLUID = l_oRestoreLUID;
            l_oTokenPrivileges.m_nAttributes = SE_PRIVILEGE_ENABLED;

            if (false == AdjustTokenPrivileges(l_hToken, false, ref l_oTokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
            {
                return false;
            }

            return true;
        }

        /*
            Start a process the simplest way you can imagine
        */
        static public int SimpleProcessStart(string in_strTarget, string in_strArguments)
        {
            Process l_oProcess = new Process();
            Debug.Assert(l_oProcess != null);

            l_oProcess.StartInfo.FileName = in_strTarget;
            l_oProcess.StartInfo.Arguments = in_strArguments;

            if (true == l_oProcess.Start())
            {
                return l_oProcess.Id;
            }

            return -1;
        }

        /*
            All the magic is in the call to WTSQueryUserToken, it saves you changing DACLs,
            process tokens, pulling the SID, manipulating the Windows Station and Desktop
            (and its DACLs) - if you don't know what those things are, you're lucky and should
            be on your knees thanking God at this moment.

            DEV NOTE:  This method currently ASSumes that it should impersonate the user
                              who is logged into session 1 (if more than one user is logged in, each
                              user will have a session of their own which means that if user switching
                              is going on, this method could start a process whose UI shows up in
                              the session of the user who is not actually using the machine at this
                              moment.)

            DEV NOTE 2:    If the process being started is a binary which decides, based upon
                                the user whose session it is being created in, to relaunch with a
                                different integrity level (such as Internet Explorer), the process
                                id will change immediately and the Process Manager will think
                                that the process has died (because in actuality the process it
                                launched DID in fact die only that it was due to self-termination)
                                This means beware of using this service to startup such applications
                                although it can connect to them to alarm in case of failure, just
                                make sure you don't configure it to restart it or you'll get non
                                stop process creation ;)
        */
        static public int CreateUIProcessForServiceRunningAsLocalSystem(string in_strTarget, string in_strArguments)
        {
            PROCESS_INFORMATION l_oProcessInformation = new PROCESS_INFORMATION();
            SECURITY_ATTRIBUTES l_oSecurityAttributes = new SECURITY_ATTRIBUTES();
            STARTUPINFO l_oStartupInfo = new STARTUPINFO();
            PROFILEINFO l_oProfileInfo = new PROFILEINFO();
            IntPtr l_ptrUserToken = new IntPtr(0);
            uint l_nActiveUserSessionId = 0xFFFFFFFF;
            string l_strActiveUserName = "";
            int l_nProcessID = -1;
            IntPtr l_ptrBuffer = IntPtr.Zero;
            uint l_nBytes = 0;

            try
            {
                //The currently active user is running what session?
                l_nActiveUserSessionId = WTSGetActiveConsoleSessionId();

                if (l_nActiveUserSessionId == 0xFFFFFFFF)
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSGetActiveConsoleSessionId failed,  GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                if (false == WTSQuerySessionInformation(IntPtr.Zero, (int)l_nActiveUserSessionId, WTS_INFO_CLASS.WTSUserName, out l_ptrBuffer, out l_nBytes))
                {
                    int l_nLastError = Marshal.GetLastWin32Error();

                    //On earlier operating systems from Vista, when no one is logged in, you get RPC_S_INVALID_BINDING which is ok, we just won't impersonate
                    if (l_nLastError != RPC_S_INVALID_BINDING)
                    {
                        throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQuerySessionInformation failed,  GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                    }

                    //No one logged in so let's just do this the simple way
                    return SimpleProcessStart(in_strTarget, in_strArguments);
                }

                l_strActiveUserName = Marshal.PtrToStringAnsi(l_ptrBuffer);
                WTSFreeMemory(l_ptrBuffer);

                //We are supposedly running as a service so we're going to be running in session 0 so get a user token from the active user session
                if (false == WTSQueryUserToken((uint)l_nActiveUserSessionId, out l_ptrUserToken))
                {
                    int l_nLastError = Marshal.GetLastWin32Error();

                    //Remember, sometimes nobody is logged in (especially when we're set to Automatically startup) you should get error code 1008 (no user token available)
                    if (ERROR_NO_TOKEN != l_nLastError)
                    {
                        //Ensure we're running under the local system account
                        WindowsIdentity l_oIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();

                        if ("NT AUTHORITY\\SYSTEM" != l_oIdentity.Name)
                        {
                            throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQueryUserToken failed and querying the process' account identity results in an identity which does not match 'NT AUTHORITY\\SYSTEM' but instead returns the name:" + l_oIdentity.Name + "  GetLastError returns: " + l_nLastError.ToString());
                        }

                        throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to WTSQueryUserToken failed, GetLastError returns: " + l_nLastError.ToString());
                    }

                    //No one logged in so let's just do this the simple way
                    return SimpleProcessStart(in_strTarget, in_strArguments);
                }

                //Create an appropriate environment block for this user token (if we have one)
                IntPtr l_ptrEnvironment = IntPtr.Zero;

                Debug.Assert(l_ptrUserToken != IntPtr.Zero);

                if (false == CreateEnvironmentBlock(out l_ptrEnvironment, l_ptrUserToken, false))
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to CreateEnvironmentBlock failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                l_oSecurityAttributes.Length = Marshal.SizeOf(l_oSecurityAttributes);
                l_oStartupInfo.cb = Marshal.SizeOf(l_oStartupInfo);

                //DO NOT set this to "winsta0\\default" (even though many online resources say to do so)
                l_oStartupInfo.lpDesktop = String.Empty;
                l_oProfileInfo.dwSize = Marshal.SizeOf(l_oProfileInfo);
                l_oProfileInfo.lpUserName = l_strActiveUserName;

                //Remember, sometimes nobody is logged in (especially when we're set to Automatically startup)
                if (false == LoadUserProfile(l_ptrUserToken, ref l_oProfileInfo))
                {
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to LoadUserProfile failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                if (false == CreateProcessAsUser(l_ptrUserToken, in_strTarget, in_strTarget + " " + in_strArguments, ref l_oSecurityAttributes, ref l_oSecurityAttributes, false, CreationFlags.CREATE_UNICODE_ENVIRONMENT, l_ptrEnvironment, null, ref l_oStartupInfo, ref l_oProcessInformation))
                {
                    //System.Diagnostics.EventLog.WriteEntry( "CreateProcessAsUser FAILED", Marshal.GetLastWin32Error().ToString() );
                    throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "The call to CreateProcessAsUser failed, GetLastError returns: " + Marshal.GetLastWin32Error().ToString());
                }

                l_nProcessID = l_oProcessInformation.dwProcessID;
            }
            catch (Exception l_oException)
            {
                throw new Exception("ProcessUtilities" + "->" + MethodInfo.GetCurrentMethod().Name + "->" + "An unhandled exception was caught spawning the process, the exception was: " + l_oException.Message);
            }
            finally
            {
                if (l_oProcessInformation.hProcess != IntPtr.Zero)
                {
                    CloseHandle(l_oProcessInformation.hProcess);
                }
                if (l_oProcessInformation.hThread != IntPtr.Zero)
                {
                    CloseHandle(l_oProcessInformation.hThread);
                }
            }

            return l_nProcessID;
        }

        #endregion //Methods
    }
}

And this is the call you have to do:

Common.Utilities.Processes.ProcessUtilities.CreateUIProcessForServiceRunningAsLocalSystem(
            @"C:\Windows\System32\cmd.exe", 
            " /c \"start http://www.google.com\""
);

I have tested it on my own system and it worked like a charm (Windows 7 x64 with enabled UAC)

However I recommend to create a tiny stub application which will not make the cmd window flash. And which will accept the url as parameter.
Plus you should not use the hardcoded path to cmd.exe like I did in the example. However the code works, the rest should be clear I hope :-)

HTH

千鲤 2024-10-02 05:19:10

如果服务必须与用户交互(例如,服务在登录之前启动),则这是一个设计问题。

我通常通过制作一个从用户会话启动的小程序来解决这个问题。如果我必须与服务中的用户交互,它将首先查看用户级程序是否正在运行,如果是,它将向其发送命令。

It is a design problem if a service has to interact with the user (as example, services are started before logon).

I usally solve this problem by making a small program that starts with the user's session. If I have to interact with the user in the service, it will first look if the user level program is running and if it is, it will send commands to it.

殊姿 2024-10-02 05:19:10

如果您对响应感兴趣,请尝试:

string url = @"http://www.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream);

If you are interested in the response try:

string url = @"http://www.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream);
不忘初心 2024-10-02 05:19:10

为什么不尝试模仿在计算机上具有登录权限的用户,仅使用 System.Diagnostics.Process.Start 启动 Web 浏览器的特定代码段。
网页加载后,您到底打算用浏览器做什么?

Why not try to Impersonate a user who has logon privilages on the machine only for that particular piece of code that starts the web browser using System.Diagnostics.Process.Start.
What exactly do you intend to do with browser once the web page gets loaded?

等风来 2024-10-02 05:19:10

您可以尝试设置“与桌面交互”来运行您的服务。这应该可以解决您的问题,但可能会导致其他问题,例如当有人注销主控制台时您的服务将停止。

You can try to run your service with "interact with desktop" set. This should fix your problem, but may cause other issues like your service will stop when someone logs out of the main console.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文