【问题标题】:open website without Process.start打开没有 Process.start 的网站
【发布时间】:2010-09-27 09:55:34
【问题描述】:

如何在浏览器没有Process.start(...)打开网站网址:

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

我不能在 Windows 服务中使用Process.Start(),我不知道为什么。

【问题讨论】:

  • 这种限制有什么原因吗?
  • 描述确切您想要实现的目标,而不是告诉我们您想要的原因也会有所帮助。

标签: c# process external-process


【解决方案1】:

查看问题“How can a Windows service execute a GUI application?”的答案:

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

还要注意你不应该这样做的观点:)

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

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

如果要禁止显示短暂显示的命令提示符窗口,可以将STARTUPINFO structurewShowWindow 字段设置为SW_HIDE,或在.NET 中将ProcessStartInfo.WindowStyle 属性设置为ProcessWindowStyle.Hidden

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 是的,除了在服务上启动浏览器没有意义的部分,在某些情况下它可能有意义,比如测试网站的特定行为。但是你不能期望看到浏览器打开。
    • @Gabriel:不过,在这种情况下,您不需要浏览器 - 只需要来自网站的信息。
    • @Reed Copsey,但如果您需要在浏览器上自动测试网站的功能行为。您应该使用浏览器进行测试。
    • @Gabriel:啊,但是对于功能测试,我不推荐服务作为测试主机......
    • 当我的孩子找不到明显可见的东西时,我对他们使用的标准线:“如果它有牙齿,它会咬你!”
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 有一些方法可以在当前登录用户的上下文中创建一个进程。看我的回答
      • 也许你知道如何从服务访问 waveIn/waveOut 设备。相同的方案就足够了吗?
      【解决方案5】:

      此处的固定代码: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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          如果您对回复感兴趣,请尝试:

          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);
          

          【讨论】:

          • 他想在用户的默认浏览器中打开
          【解决方案8】:

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

          【讨论】:

          • 没必要看我的例子。
          【解决方案9】:

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

          【讨论】:

          • "interact with desktop"不支持从Vista开始,所以建议不要使用它。
          • 猜猜我没有看到他们正在运行 vista。哦等等,问题中没有这样说。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-07-21
          • 1970-01-01
          • 2015-04-02
          • 2018-02-24
          • 1970-01-01
          • 1970-01-01
          • 2016-03-16
          相关资源
          最近更新 更多