前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# Windows服务开发

C# Windows服务开发

作者头像
码客说
发布2022-05-23 09:37:46
1.2K0
发布2022-05-23 09:37:46
举报
文章被收录于专栏:码客

前言

我要开发一个系统服务,服务的作用是定时检测并关闭其他应用的弹窗,但是开发后却发现,服务在运行是压根获取不到任何窗口。

原因在于

Windows服务一般在Session0里,EXE应用一般在Session1里,Win7及以后的系统将服务与应用程序进行了Session隔离,不允许其进行UI交互,可以考虑穿透Session隔离来启动。

这样就想到另一个方法,把业务写成一个控制台程序,在服务中调用,但是依旧不行

服务中启动的其他应用依旧会在Session0中。

那我们就要想个方法能在Session1中运行的方法。

总的来说有以下注意点

  • 服务的Account属性设置为LocalSystem,安装服务后的登录身份则为本地系统账户
  • 不要把Windows服务的程序放在C:\Users\Administrator\目录下运行,不然启动服务的时候会遇到权限问题
  • 程序要在Session1中运行

创建Windows服务

创建后在Service1.cs的设计试图上右键 添加安装程序

之后会出现以下两个

点击1 对应的属性 设置 Account 设置为 LocalSystem

点击2

其中

  • ServiceName 就是安装后显示的服务名称 支持中文
  • StartType是启动的方式 默认是手动 这里设置为自动(Automatic), 但是注意就算设置为自动在安装服务后也不会自动启动,只有下次重启电脑时才会自动启动。

如图

服务代码

代码语言:javascript
复制
public partial class Service1 : ServiceBase
{
  public Service1()
  {
    InitializeComponent();
  }

  protected override void OnStart(string[] args)
  {
    log("服务-启动");
    closePopWin();
  }

  protected override void OnStop()
  {
    log("服务-停止");
  }

  public void closePopWin()
  {
    new Thread(o =>
               {
                 while (true)
                 {
                   try
                   {
                     string exeFile = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "CloseWPSPopWin.exe");
                     WinAPIInterop.CreateProcess(exeFile);

                     closeWpsWins();
                   }
                   catch (Exception ex)
                   {
                     log(ex.Message);
                   }

                   Thread.Sleep(1000);
                 }
               })
    {
      IsBackground = true
    }.Start();
  }

  public static void closeWpsWins()
  {
    try
    {
      var windows = WindowEnumerator.FindAll();

      for (int i = 0; i < windows.Count; i++)
      {
        var win = windows[i];
        if (win.Title.Contains("WPS") && !win.IsTop)
        {
          WindowEnumerator.CloseWin(win.Hwnd);
        }
      }
    }
    catch (Exception)
    {
    }
  }

  private static void log(string content)
  {
    if (string.IsNullOrEmpty(content))
    {
      return;
    }

    try
    {
      string logFile = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, string.Format("service.{0:yyyyMMdd}.log", DateTime.Now));
      using (FileStream fileStream = new FileStream(logFile, FileMode.Append, FileAccess.Write))
      {
        using (StreamWriter streamWriter = new StreamWriter(fileStream))
        {
          string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
          streamWriter.WriteLine(time + " " + content);
        }
      }
    }
    catch { }
  }
}

服务安装工具

界面效果

服务安装后我们可以通过下面的命令打开服务面板

代码语言:javascript
复制
services.msc

窗口对应的代码

代码语言:javascript
复制
public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();

    selectFileBtn.Click += SelectFileBtn_Click;
    installBtn.Click += InstallBtn_Click;
    unInstallBtn.Click += UnInstallBtn_Click;
    startBtn.Click += StartBtn_Click;
    stopBtn.Click += StopBtn_Click;
  }

  private void StartBtn_Click(object sender, RoutedEventArgs e)
  {
    if (serviceNameTb.Text != "")
    {
      if (ServiceUtil.IsServiceExisted(serviceNameTb.Text))
      {
        ServiceUtil.ServiceStart(serviceNameTb.Text);
      }
    }
  }

  private void StopBtn_Click(object sender, RoutedEventArgs e)
  {
    if (serviceNameTb.Text != "")
    {
      if (ServiceUtil.IsServiceExisted(serviceNameTb.Text))
      {
        ServiceUtil.ServiceStop(serviceNameTb.Text);
      }
    }
  }

  private void UnInstallBtn_Click(object sender, RoutedEventArgs e)
  {
    if (filePathTb.Text != "")
    {
      ServiceUtil.UninstallService(filePathTb.Text);
    }
  }

  private void InstallBtn_Click(object sender, RoutedEventArgs e)
  {
    if (filePathTb.Text != "")
    {
      ServiceUtil.InstallService(filePathTb.Text);
    }
  }

  private void SelectFileBtn_Click(object sender, RoutedEventArgs e)
  {
    Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
    ofd.Filter = "可执行程序|*.exe";
    //ofd.InitialDirectory = desktopPath;
    ofd.Multiselect = false;
    ofd.AddExtension = true;
    ofd.DereferenceLinks = true;
    if (ofd.ShowDialog() == true)
    {
      filePathTb.Text = ofd.FileName;
    }
  }
}

服务操作工具类

代码语言:javascript
复制
using System;
using System.Collections;
using System.Configuration.Install;
using System.ServiceProcess;

namespace WPSServiceClient.Utils
{
  internal class ServiceUtil
  {
    //判断服务是否存在
    public static bool IsServiceExisted(string serviceName)
    {
      ServiceController[] services = ServiceController.GetServices();
      foreach (ServiceController sc in services)
      {
        if (sc.ServiceName.ToLower() == serviceName.ToLower())
        {
          return true;
        }
      }
      return false;
    }

    //安装服务
    public static void InstallService(string serviceFilePath)
    {
      using (AssemblyInstaller installer = new AssemblyInstaller())
      {
        installer.UseNewContext = true;
        installer.Path = serviceFilePath;
        IDictionary savedState = new Hashtable();
        installer.Install(savedState);
        installer.Commit(savedState);
      }
    }

    //卸载服务
    public static bool UninstallService(string serviceFilePath)
    {
      using (AssemblyInstaller installer = new AssemblyInstaller())
      {
        try
        {
          installer.UseNewContext = true;
          installer.Path = serviceFilePath;
          installer.Uninstall(null);
          return true;
        }
        catch (Exception) {
          return false;
        }
      }
    }
    //启动服务
    public static void ServiceStart(string serviceName)
    {
      using (ServiceController control = new ServiceController(serviceName))
      {
        if (control.Status == ServiceControllerStatus.Stopped)
        {
          control.Start();
        }
      }
    }

    //停止服务
    public static void ServiceStop(string serviceName)
    {
      using (ServiceController control = new ServiceController(serviceName))
      {
        if (control.Status == ServiceControllerStatus.Running)
        {
          control.Stop();
        }
      }
    }
  }
}

Session1中运行

我们之前运行是这样的

但是这种方法会在所在的Session中运行,所以这里是不行的。

代码语言:javascript
复制
string exeFile = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "CloseWPSPopWin.exe");

ProcessStartInfo start = new ProcessStartInfo();
start.FileName = exeFile;
start.Arguments = "";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
log("执行 " + exeFile);

//启动调用
using (Process process = Process.Start(start))
{
  //接收控制台输出的消息
  process.OutputDataReceived += (s1, e1) =>
  {
    if (e1 != null && e1.Data != null && e1.Data.Equals("Update"))
    {
      Process[] p = System.Diagnostics.Process.GetProcessesByName("Starter");
      p[0].Kill();
    }
  };
  process.BeginOutputReadLine();
  //等待退出
  process.WaitForExit();
}

方法1

调用方式

代码语言:javascript
复制
WinAPIInterop.CreateProcess(exeFile);

工具类

代码语言:javascript
复制
public class WinAPIInterop
{
  public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

  /// <summary>
  /// 服务程序执行消息提示,前台MessageBox.Show
  /// </summary>
  /// <param name="message">消息内容</param>
  /// <param name="title">标题</param>
  public static void ShowServiceMessage(string message, string title)
  {
    int resp = 0;
    WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTSGetActiveConsoleSessionId(), title, title.Length, message, message.Length, 0, 0, out resp, false);
  }

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

  [DllImport("wtsapi32.dll", SetLastError = true)]
  public static extern bool WTSSendMessage(IntPtr hServer, int SessionId, String pTitle, int TitleLength, String pMessage, int MessageLength, int Style, int Timeout, out int pResponse, bool bWait);

  #region P/Invoke WTS APIs

    private enum WTS_CONNECTSTATE_CLASS
  {
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
  }

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  private struct WTS_SESSION_INFO
  {
    public UInt32 SessionID;
    public string pWinStationName;
    public WTS_CONNECTSTATE_CLASS State;
  }

  [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern bool WTSEnumerateSessions(
    IntPtr hServer,
    [MarshalAs(UnmanagedType.U4)] UInt32 Reserved,
    [MarshalAs(UnmanagedType.U4)] UInt32 Version,
    ref IntPtr ppSessionInfo,
    [MarshalAs(UnmanagedType.U4)] ref UInt32 pSessionInfoCount
  );

  [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern void WTSFreeMemory(IntPtr pMemory);

  [DllImport("WTSAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);

  #endregion P/Invoke WTS APIs

    #region P/Invoke CreateProcessAsUser

    /// <summary>
    /// Struct, Enum and P/Invoke Declarations for CreateProcessAsUser.
    /// </summary>
    ///

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private 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 dwYSize;
      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, CharSet = CharSet.Auto)]
  private struct PROCESS_INFORMATION
  {
    public IntPtr hProcess;
    public IntPtr hThread;
    public int dwProcessId;
    public int dwThreadId;
  }

  /// <summary>
  /// 以当前登录的windows用户(角色权限)运行指定程序进程
  /// </summary>
  /// <param name="hToken"></param>
  /// <param name="lpApplicationName">指定程序(全路径)</param>
  /// <param name="lpCommandLine">参数</param>
  /// <param name="lpProcessAttributes">进程属性</param>
  /// <param name="lpThreadAttributes">线程属性</param>
  /// <param name="bInheritHandles"></param>
  /// <param name="dwCreationFlags"></param>
  /// <param name="lpEnvironment"></param>
  /// <param name="lpCurrentDirectory"></param>
  /// <param name="lpStartupInfo">程序启动属性</param>
  /// <param name="lpProcessInformation">最后返回的进程信息</param>
  /// <returns>是否调用成功</returns>
  [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
                                                 bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory,
                                                 ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

  [DllImport("KERNEL32.DLL", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern bool CloseHandle(IntPtr hHandle);

  #endregion P/Invoke CreateProcessAsUser

    /// <summary>
    /// 以当前登录系统的用户角色权限启动指定的进程
    /// </summary>
    /// <param name="ChildProcName">指定的进程(全路径)</param>
    public static void CreateProcess(string ChildProcName)
  {
    IntPtr ppSessionInfo = IntPtr.Zero;
    UInt32 SessionCount = 0;
    if (WTSEnumerateSessions(
      (IntPtr)WTS_CURRENT_SERVER_HANDLE,  // Current RD Session Host Server handle would be zero.
      0,  // This reserved parameter must be zero.
      1,  // The version of the enumeration request must be 1.
      ref ppSessionInfo, // This would point to an array of session info.
      ref SessionCount  // This would indicate the length of the above array.
    ))
    {
      for (int nCount = 0; nCount < SessionCount; nCount++)
      {
        WTS_SESSION_INFO tSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(ppSessionInfo + nCount * Marshal.SizeOf(typeof(WTS_SESSION_INFO)), typeof(WTS_SESSION_INFO));
        if (WTS_CONNECTSTATE_CLASS.WTSActive == tSessionInfo.State)
        {
          IntPtr hToken = IntPtr.Zero;
          if (WTSQueryUserToken(tSessionInfo.SessionID, out hToken))
          {
            PROCESS_INFORMATION tProcessInfo;
            STARTUPINFO tStartUpInfo = new STARTUPINFO();
            tStartUpInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
            bool ChildProcStarted = CreateProcessAsUser(
              hToken,             // Token of the logged-on user.
              ChildProcName,      // Name of the process to be started.
              null,               // Any command line arguments to be passed.
              IntPtr.Zero,        // Default Process' attributes.
              IntPtr.Zero,        // Default Thread's attributes.
              false,              // Does NOT inherit parent's handles.
              0,                  // No any specific creation flag.
              null,               // Default environment path.
              null,               // Default current directory.
              ref tStartUpInfo,   // Process Startup Info.
              out tProcessInfo    // Process information to be returned.
            );
            if (ChildProcStarted)
            {
              CloseHandle(tProcessInfo.hThread);
              CloseHandle(tProcessInfo.hProcess);
            }
            else
            {
              ShowServiceMessage("CreateProcessAsUser失败", "CreateProcess");
            }
            CloseHandle(hToken);
            break;
          }
        }
      }
      WTSFreeMemory(ppSessionInfo);
    }
  }
}

方法2

调用方式

代码语言:javascript
复制
ApplicationLoader.PROCESS_INFORMATION procInfo;
ApplicationLoader.StartProcessAndBypassUAC(exeFile, out procInfo);

工具类

代码语言:javascript
复制
/// <summary>
/// Class that allows running applications with full admin rights. In
/// addition the application launched will bypass the Vista UAC prompt.
/// </summary>
public class ApplicationLoader
{
  #region Structrures

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

  [StructLayout(LayoutKind.Sequential)]
  public struct STARTUPINFO
  {
    public int cb;
    public String lpReserved;
    public String lpDesktop;
    public String lpTitle;
    public uint dwX;
    public uint dwY;
    public uint dwXSize;
    public uint dwYSize;
    public uint dwXCountChars;
    public uint dwYCountChars;
    public uint dwFillAttribute;
    public uint dwFlags;
    public short wShowWindow;
    public short 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 uint dwProcessId;
    public uint dwThreadId;
  }

  #endregion Structrures

    #region Enumberation

    private enum TOKEN_TYPE : int
    {
      TokenPrimary = 1,
      TokenImpersonation = 2
    }

  private enum SECURITY_IMPERSONATION_LEVEL : int
  {
    SecurityAnonymous = 0,
    SecurityIdentification = 1,
    SecurityImpersonation = 2,
    SecurityDelegation = 3,
  }

  #endregion Enumberation

    #region Constants

    public const int TOKEN_DUPLICATE = 0x0002;
  public const uint MAXIMUM_ALLOWED = 0x2000000;
  public const int CREATE_NEW_CONSOLE = 0x00000010;

  public const int IDLE_PRIORITY_CLASS = 0x40;
  public const int NORMAL_PRIORITY_CLASS = 0x20;
  public const int HIGH_PRIORITY_CLASS = 0x80;
  public const int REALTIME_PRIORITY_CLASS = 0x100;

  #endregion Constants

    #region Win32 API Imports

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hSnapshot);

  [DllImport("kernel32.dll")]
  private static extern uint WTSGetActiveConsoleSessionId();

  [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
                                                ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
                                                String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

  [DllImport("kernel32.dll")]
  private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

  [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
  public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
                                             ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
                                             int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

  [DllImport("kernel32.dll")]
  private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

  [DllImport("advapi32.dll", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
  private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

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

  #endregion Win32 API Imports

    /// <summary>
    /// Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
    /// </summary>
    /// <param name="applicationName">The name of the application to launch</param>
    /// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
    /// <returns></returns>
    public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo)
  {
    uint winlogonPid = 0;
    IntPtr hUserTokenDup = IntPtr.Zero,
    hPToken = IntPtr.Zero,
    hProcess = IntPtr.Zero;
    procInfo = new PROCESS_INFORMATION();

    // obtain the currently active session id; every logged on user in the system has a unique session id
    TSControl.WTS_SESSION_INFO[] pSessionInfo = TSControl.SessionEnumeration();
    uint dwSessionId = 100;
    for (int i = 0; i < pSessionInfo.Length; i++)
    {
      if (pSessionInfo[i].SessionID != 0)
      {
        try
        {
          int count = 0;
          IntPtr buffer = IntPtr.Zero;
          StringBuilder sb = new StringBuilder();

          bool bsuccess = TSControl.WTSQuerySessionInformation(
            IntPtr.Zero, pSessionInfo[i].SessionID,
            TSControl.WTSInfoClass.WTSUserName, out sb, out count);

          if (bsuccess)
          {
            if (sb.ToString().Trim() == "Administrator")//Administrator
            {
              dwSessionId = (uint)pSessionInfo[i].SessionID;
            }
          }
        }
        catch (Exception ex)
        {
          
        }
      }
    }

    // obtain the process id of the winlogon process that is running within the currently active session
    Process[] processes = Process.GetProcessesByName("explorer");
    foreach (Process p in processes)
    {
      if ((uint)p.SessionId == dwSessionId)
      {
        winlogonPid = (uint)p.Id;
      }
    }

    // obtain a handle to the winlogon process
    hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

    // obtain a handle to the access token of the winlogon process
    if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
    {
      CloseHandle(hProcess);
      return false;
    }

    // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
    // I would prefer to not have to use a security attribute variable and to just
    // simply pass null and inherit (by default) the security attributes
    // of the existing token. However, in C# structures are value types and therefore
    // cannot be assigned the null value.
    SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
    sa.Length = Marshal.SizeOf(sa);

    // copy the access token of the winlogon process; the newly created token will be a primary token
    if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

    // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
    // the window station has a desktop that is invisible and the process is incapable of receiving
    // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
    // interaction with the new process.
    STARTUPINFO si = new STARTUPINFO();
    si.cb = (int)Marshal.SizeOf(si);
    si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

    // flags that specify the priority and creation method of the process
    int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

    // create a new process in the current user's logon session
    bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                      null,                   // file to execute
                                      applicationName,        // command line
                                      ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                      ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                      false,                  // handles are not inheritable
                                      dwCreationFlags,        // creation flags
                                      IntPtr.Zero,            // pointer to new environment block
                                      null,                   // name of current directory
                                      ref si,                 // pointer to STARTUPINFO structure
                                      out procInfo            // receives information about new process
                                     );

    // invalidate the handles
    CloseHandle(hProcess);
    CloseHandle(hPToken);
    CloseHandle(hUserTokenDup);
    //LoaderService.WriteLog("launch Task", "Monitor");

    return result; // return the result
  }
}

public class TSControl
{
  /**/

  /// <summary>
  /// Terminal Services API Functions,The WTSEnumerateSessions function retrieves a list of sessions on a specified terminal server,
  /// </summary>
  /// <param name="hServer">[in] Handle to a terminal server. Specify a handle opened by the WTSOpenServer function, or specify WTS_CURRENT_SERVER_HANDLE to indicate the terminal server on which your application is running</param>
  /// <param name="Reserved">Reserved; must be zero</param>
  /// <param name="Version">[in] Specifies the version of the enumeration request. Must be 1. </param>
  /// <param name="ppSessionInfo">[out] Pointer to a variable that receives a pointer to an array of WTS_SESSION_INFO structures. Each structure in the array contains information about a session on the specified terminal server. To free the returned buffer, call the WTSFreeMemory function.
  /// To be able to enumerate a session, you need to have the Query Information permission.</param>
  /// <param name="pCount">[out] Pointer to the variable that receives the number of WTS_SESSION_INFO structures returned in the ppSessionInfo buffer. </param>
  /// <returns>If the function succeeds, the return value is a nonzero value. If the function fails, the return value is zero</returns>
  [DllImport("wtsapi32", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern bool WTSEnumerateSessions(int hServer, int Reserved, int Version, ref long ppSessionInfo, ref int pCount);

  /**/

  /// <summary>
  /// Terminal Services API Functions,The WTSFreeMemory function frees memory allocated by a Terminal Services function.
  /// </summary>
  /// <param name="pMemory">[in] Pointer to the memory to free</param>
  [DllImport("wtsapi32.dll")]
  public static extern void WTSFreeMemory(System.IntPtr pMemory);

  /**/

  /// <summary>
  /// Terminal Services API Functions,The WTSLogoffSession function logs off a specified Terminal Services session.
  /// </summary>
  /// <param name="hServer">[in] Handle to a terminal server. Specify a handle opened by the WTSOpenServer function, or specify WTS_CURRENT_SERVER_HANDLE to indicate the terminal server on which your application is running. </param>
  /// <param name="SessionId">[in] A Terminal Services session identifier. To indicate the current session, specify WTS_CURRENT_SESSION. You can use the WTSEnumerateSessions function to retrieve the identifiers of all sessions on a specified terminal server.
  /// To be able to log off another user's session, you need to have the Reset permission </param>
  /// <param name="bWait">[in] Indicates whether the operation is synchronous.
  /// If bWait is TRUE, the function returns when the session is logged off.
  /// If bWait is FALSE, the function returns immediately.</param>
  /// <returns>If the function succeeds, the return value is a nonzero value.
  /// If the function fails, the return value is zero.</returns>
  [DllImport("wtsapi32.dll")]
  public static extern bool WTSLogoffSession(int hServer, long SessionId, bool bWait);

  [DllImport("Wtsapi32.dll")]
  public static extern bool WTSQuerySessionInformation(
    System.IntPtr hServer,
    int sessionId,
    WTSInfoClass wtsInfoClass,
    out StringBuilder ppBuffer,
    out int pBytesReturned
  );

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

  /**/

  /// <summary>
  /// The WTS_CONNECTSTATE_CLASS enumeration type contains INT values that indicate the connection state of a Terminal Services session.
  /// </summary>
  public enum WTS_CONNECTSTATE_CLASS
  {
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit,
  }

  /**/

  /// <summary>
  /// The WTS_SESSION_INFO structure contains information about a client session on a terminal server.
  /// if the WTS_SESSION_INFO.SessionID==0, it means that the SESSION is the local logon user's session.
  /// </summary>
  public struct WTS_SESSION_INFO
  {
    public int SessionID;

    [MarshalAs(UnmanagedType.LPTStr)]
    public string pWinStationName;

    public WTS_CONNECTSTATE_CLASS state;
  }

  /**/

  /// <summary>
  /// The SessionEnumeration function retrieves a list of
  ///WTS_SESSION_INFO on a current terminal server.
  /// </summary>
  /// <returns>a list of WTS_SESSION_INFO on a current terminal server</returns>
  public static WTS_SESSION_INFO[] SessionEnumeration()
  {
    //Set handle of terminal server as the current terminal server
    int hServer = 0;
    bool RetVal;
    long lpBuffer = 0;
    int Count = 0;
    long p;
    WTS_SESSION_INFO Session_Info = new WTS_SESSION_INFO();
    WTS_SESSION_INFO[] arrSessionInfo;
    RetVal = WTSEnumerateSessions(hServer, 0, 1, ref lpBuffer, ref Count);
    arrSessionInfo = new WTS_SESSION_INFO[0];
    if (RetVal)
    {
      arrSessionInfo = new WTS_SESSION_INFO[Count];
      int i;
      p = lpBuffer;
      for (i = 0; i < Count; i++)
      {
        arrSessionInfo[i] =
          (WTS_SESSION_INFO)Marshal.PtrToStructure(new IntPtr(p),
                                                   Session_Info.GetType());
        p += Marshal.SizeOf(Session_Info.GetType());
      }
      WTSFreeMemory(new IntPtr(lpBuffer));
    }
    else
    {
      //Insert Error Reaction Here
    }
    return arrSessionInfo;
  }

  public TSControl()
  {
  }
}

控制台程序禁止弹窗

把输出类型 改为 Windows应用程序

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 创建Windows服务
  • 服务安装工具
  • Session1中运行
    • 方法1
      • 方法2
      • 控制台程序禁止弹窗
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档