WTSEnumerateSessions 挂起并且永不返回

2024-03-04

我有一个用 C# 编写的 .Net 服务。我正在调用WTSEnumerateSessions。会话检查由计时器每 15 分钟运行一次,它列出所有会话并将用户名/域组合与预定义的用户集进行比较,以确定他们是否登录到服务器。

问题在于几个服务器的调用WTSEnumerateSessions无限期挂起。它工作正常几次,然后突然停止并导致服务变得无响应。

这是我用来枚举会话的代码。

[DllImport("wtsapi32.dll")]
static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);

[DllImport("wtsapi32.dll")]
static extern void WTSCloseServer(IntPtr hServer);

[DllImport("wtsapi32.dll")]
static extern Int32 WTSEnumerateSessions(
       IntPtr hServer,
       [MarshalAs(UnmanagedType.U4)] Int32 Reserved,
       [MarshalAs(UnmanagedType.U4)] Int32 Version,
       ref IntPtr ppSessionInfo,
       [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

 [DllImport("wtsapi32.dll")]
 static extern void WTSFreeMemory(IntPtr pMemory);

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

 [StructLayout(LayoutKind.Sequential)]
 private struct WTS_SESSION_INFO
 {
      public Int32 SessionID;

      [MarshalAs(UnmanagedType.LPStr)]
      public String pWinStationName;

      public WTS_CONNECTSTATE_CLASS State;
 }

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

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

 private void ListUsers_Elapsed()
 {
      IntPtr serverHandle = IntPtr.Zero;
      List<String> resultList = new List<string>();
      serverHandle = WTSOpenServer("");

      try
      {
        IntPtr SessionInfoPtr = IntPtr.Zero;
        IntPtr userPtr = IntPtr.Zero;
        IntPtr domainPtr = IntPtr.Zero;
        Int32 sessionCount = 0;
        Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref                     sessionCount);
        Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
        Int32 currentSession = (int)SessionInfoPtr;
        uint bytes = 0;

        if (retVal != 0)
        {
          for (int i = 0; i < sessionCount; i++)
          {
            WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO));
            currentSession += dataSize;

            WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes);
            WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes);

            //Then I compare the session information against a list of users to determine if they are still active

            WTSFreeMemory(userPtr); 
            WTSFreeMemory(domainPtr);
          }

          WTSFreeMemory(SessionInfoPtr);
        }
      }
      finally
      {
        CloseServer(serverHandle);
      }

    }

我生成了一个用户转储,似乎有一个永远不会超时的 WaitForSingleObject:

00000000`cd78dd78 000007fe`fd6810dc ntdll!NtWaitForSingleObject+0xa
00000000`cd78dd80 000007fe`fce11442 KERNELBASE!WaitForSingleObjectEx+0x79
00000000`cd78de20 000007fe`fce113fd winsta!WaitForLsmStart+0xa3
00000000`cd78de50 000007fe`fce11336 winsta!OpenLocalLSM+0xe0
00000000`cd78deb0 000007fe`fce12669 winsta!CPublicBinding::GetLSMBinding+0x51
00000000`cd78def0 000007fe`fce12576 winsta!_tsrpcEnumerate+0xd3
00000000`cd78df90 000007fe`fb073500 winsta!WinStationEnumerateW+0x22
00000000`cd78dfc0 000007fe`8de35caf wtsapi32!WTSEnumerateSessionsA+0x60
00000000`cd78e030 000007fe`8de351c0 0x000007fe`8de35caf
00000000`cd78e120 000007fe`8de34b61 0x000007fe`8de351c0
00000000`cd78e260 000007fe`8de3849a 0x000007fe`8de34b61
00000000`cd78e2d0 000007fe`eb922c73 0x000007fe`8de3849a
00000000`cd78e370 000007fe`ec2639a5 System_ni+0x922c73
00000000`cd78e3e0 000007fe`ec263719 mscorlib_ni+0x4a39a5
00000000`cd78e540 000007fe`ec30be92 mscorlib_ni+0x4a3719
00000000`cd78e570 000007fe`ec30bc7e mscorlib_ni+0x54be92
00000000`cd78e600 000007fe`ec2c12e4 mscorlib_ni+0x54bc7e
00000000`cd78e670 000007fe`ed45a7f3 mscorlib_ni+0x5012e4
00000000`cd78e710 000007fe`ed45a6de clr!CallDescrWorkerInternal+0x83
00000000`cd78e750 000007fe`ed45ae76 clr!CallDescrWorkerWithHandler+0x4a
00000000`cd78e790 000007fe`ed519033 clr!MethodDescCallSite::CallTargetWorker+0x251
00000000`cd78e940 000007fe`ed45c121 clr!AppDomainTimerCallback_Worker+0x23
00000000`cd78ea30 000007fe`ed45c0a8 clr!ManagedThreadBase_DispatchInner+0x2d
00000000`cd78ea70 000007fe`ed45c019 clr!ManagedThreadBase_DispatchMiddle+0x6c
00000000`cd78eb70 000007fe`ed45c15f clr!ManagedThreadBase_DispatchOuter+0x75
00000000`cd78ec00 000007fe`ed518fc1 clr!ManagedThreadBase_FullTransitionWithAD+0x2f
00000000`cd78ec60 000007fe`ed518f35 clr!AppDomainTimerCallback+0x66
00000000`cd78ecc0 000007fe`ed518b42 clr!ThreadpoolMgr::AsyncTimerCallbackCompletion+0x36
00000000`cd78ed10 000007fe`ed4df046 clr!UnManagedPerAppDomainTPCount::DispatchWorkItem+0x122
00000000`cd78edb0 000007fe`ed4def3a clr!ThreadpoolMgr::ExecuteWorkRequest+0x46
00000000`cd78ede0 000007fe`ed59fcb6 clr!ThreadpoolMgr::WorkerThreadStart+0xf4
00000000`cd78ee70 00000000`775f59ed clr!Thread::intermediateThreadProc+0x7d
00000000`cd78f930 00000000`7772ba01 kernel32!BaseThreadInitThunk+0xd
00000000`cd78f960 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

有人可以解释一下为什么它挂起,以及是否有更好的方法来做到这一点。我依赖计时器而不是注册的原因OnSessionChange事件是,在过去,我发现后者不可靠,因为它有时会触发虚假事件,或者在发生会话更改时根本不会触发。

正如我所说,除了少数服务器之外,这在大多数服务器上都运行良好。即使对于那些受该问题影响的人,它也会在计时器触发前 4 或 5 次已用事件后起作用,然后它只会挂在下一个引发的已用事件上,之后的每个连续已用事件也是如此。

附:受影响的服务器均安装了 Windows Server 2008 R2

EDIT:

好吧,我发现了一些非常奇怪的东西。我设法让 WTSEnumerateSessions 正常工作而不会挂起。问题是我所做的并没有真正意义,在我的生活中我无法弄清楚它在做什么来阻止绞刑。

这就是我所做的,出于沮丧,我正在寻找其他方法来获取会话列表。其中一种方法是使用LsaEnumerateLogonSessions获取会话列表而不是WTSEnumerateSessions。所以我在调用之前调用了以下函数WTSEnumerateSessions.

public static List<string> PopulateSessionsList()
        {
            var outList = new List<string>();
            System.Security.Principal.WindowsIdentity currentUser = System.Security.Principal.WindowsIdentity.GetCurrent();

            DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate

            UInt64 count;
            IntPtr luidPtr = IntPtr.Zero;
            CommonWin32.LsaEnumerateLogonSessions(out count, out luidPtr);  //gets an array of pointers to LUIDs

            IntPtr iter = luidPtr;      //set the pointer to the start of the array

            for (ulong i = 0; i < count; i++)   //for each pointer in the array
            {
                IntPtr sessionData;

                CommonWin32.LsaGetLogonSessionData(iter, out sessionData);
                CommonWin32.SECURITY_LOGON_SESSION_DATA data = (CommonWin32.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(CommonWin32.SECURITY_LOGON_SESSION_DATA));

                //if we have a valid logon
                if (data.PSiD != IntPtr.Zero)
                {
                    //get the security identifier for further use
                    System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD);

                    //extract some useful information from the session data struct
                    var ptrToStringUni = Marshal.PtrToStringUni(data.Username.buffer);
                    if (ptrToStringUni != null)
                    {
                        string username = ptrToStringUni.Trim();          //get the account username
                        var toStringUni = Marshal.PtrToStringUni(data.LoginDomain.buffer);
                        if (toStringUni != null)
                        {
                            string domain = toStringUni.Trim();        //domain for this account  
                            var stringUni = Marshal.PtrToStringUni(data.AuthenticationPackage.buffer);
                            if (stringUni != null)
                            {
                                string authpackage = stringUni.Trim();    //authentication package
                            }
                            string session = data.Session.ToString();
                            CommonWin32.SECURITY_LOGON_TYPE secType = (CommonWin32.SECURITY_LOGON_TYPE)data.LogonType;
                            DateTime time = systime.AddTicks((long)data.LoginTime);                              //get the datetime the session was logged in

                            outList.Add("Session: " + session + " User: " + username + " *** Domain: " + domain + " *** Login Type: (" + data.LogonType + ") " + secType.ToString() + " *** Login Time: " + time.ToLocalTime().ToString());
                        }
                    }
                }
                iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(CommonWin32.LUID)));  //move the pointer forward
                CommonWin32.LsaFreeReturnBuffer(sessionData);   //free the SECURITY_LOGON_SESSION_DATA memory in the struct
            }
            CommonWin32.LsaFreeReturnBuffer(luidPtr);   //free the array of LUIDs

            return outList;
        }

这是我的 pInvoke 代码:

[DllImport("secur32.dll", SetLastError = false)]
public static extern uint LsaFreeReturnBuffer(IntPtr buffer);

[DllImport("Secur32.dll", SetLastError = false)]
public static extern uint LsaEnumerateLogonSessions(out UInt64 LogonSessionCount, out IntPtr LogonSessionList);

[DllImport("Secur32.dll", SetLastError = false)]
public static extern uint LsaGetLogonSessionData(IntPtr luid, out IntPtr ppLogonSessionData);

令我惊讶的是,调用此方法解决了悬而未决的问题。我不知道它为什么这样做。不确定它是否调用某些内部/私有方法。

如果不调用新方法,只需连续调用不到 10 次,罪魁祸首就会挂起,我在多台机器上尝试的每次测试都是这种情况。添加代码后,它就可以工作了。如果你打电话没关系WTSEnumerateSessions就像一分钟 1000 次一样,它从未挂起。一次也没有。

有人能告诉我为什么打电话吗LsaEnumerateLogonSessions and LsaGetLogonSessionData神奇地停止悬挂。有关这些函数内部功能的任何信息吗?


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WTSEnumerateSessions 挂起并且永不返回 的相关文章

  • 当我使用“control-c”关闭发送对等方的套接字时,为什么接收对等方的套接字不断接收“”

    我是套接字编程的新手 我知道使用 control c 关闭套接字是一个坏习惯 但是为什么在我使用 control c 关闭发送进程后 接收方上的套接字不断接收 在 control c 退出进程后 发送方的套接字不应该关闭吗 谢谢 我知道使用
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • 如何在 Windows 命令行中使用参数运行 Python 脚本

    这是我的蟒蛇hello py script def hello a b print hello and that s your sum sum a b print sum import sys if name main hello sys
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 将散列数据作为散列的密钥传递再次返回不正确的结果

    我正在创建一个将使用 GAS 访问 AWS 服务的脚本 我使用实用程序库中的哈希函数来执行创建 v4 签名所需的所有哈希 这些函数似乎能够成功地对数据进行一次哈希处理 但尝试将哈希数据传递到参数中会产生不正确的结果 还有其他人遇到这个问题并
  • 在swift中实现函数

    我是 swift 的新手 试图实现一个简单的函数 该函数将最小和最大数字作为输入 并返回一个包含所有限制数字的数组 我收到错误 错误 对泛型类型 Array 的引用需要 中的参数 我可以知道我错过了什么吗 func serialNumber
  • Scikit-learn、KMeans:如何使用 max_iter

    我想了解类中的参数 max itersklearn cluster KMeans http scikit learn org stable modules generated sklearn cluster KMeans html 根据文档
  • Haskell:如何停止程序打印向左或向右

    我用 haskell 制作了一个计算器 我在 GHCi 中运行它 然而 由于最终的数字可以是整数或双精度数 我已经进行了类型声明 calc String gt Either Integer Double 然而 函数的输出总是在其前面有左或右
  • C++ 判断类是否具有可比性

    我或多或少是Java程序员 所以这可能是一个愚蠢的问题 但我没有找到任何简单的解决方案 我在 C 中有一个这样的类 template
  • JavaScript 排序方法处理大写字母

    注意到 JavaScript 可能有些奇怪的地方sort 方法 给定以下数组 var arr Aaa CUSTREF Copy a template Copy of Statementsmm Copy1 of Default Email T
  • 在 ColdFusion 中维护出站 TCP 连接池

    我希望从 ColdFusion 应用程序中大量使用 RESTful API 我不是 CF 专家 但我预计重复的 cfhttp 调用将成为瓶颈 因为我相信每次调用都会导致建立连接 发送请求 收到响应和断开连接 我很好奇 有没有办法维护一个连接
  • 在批处理文件中定义和使用变量

    我正在尝试在批处理文件中定义和使用变量 看起来应该很简单 echo off set location bob echo We re working with location 我得到的输出如下 We re working with 这里发生
  • jQuery Mobile 视口在 Windows Phone 中无法工作

    我正在 WindowsPhone 中测试 jQueryMobile 但视口无法正常工作 有一个解决方法这一页 http forum jquery com topic problem with virtual viewport size on
  • FireStore Tasks.whenAllComplete 与协程

    我想同步实现这段代码 但job join deferred await和火力基地await 不工作 有谁知道解决方案吗 CoroutineScope Dispatchers Main launch val job launch Tasks
  • 如何使用 System.Net.HttpClient 发布复杂类型?

    我有一个自定义复杂类型 我想使用 Web API 来使用它 public class Widget public int ID get set public string Name get set public decimal Price
  • 如何使用复选框更改 QGraphicsView 背景

    在此代码中 更改了QGraphicsView背景 现在当我检查 true 时我需要更改背景checkBox 当我设置为checkBox去检查true我需要像这段代码一样设置背景 当我设置时checkBox去检查false 我需要设置QGra
  • Tensorflow 2 抛出 ValueError:as_list() 未在未知 TensorShape 上定义

    我正在尝试在 Tensorflow 2 0 中训练 Unet 模型 该模型将图像和分割掩模作为输入 但我得到了ValueError as list is not defined on an unknown TensorShape 堆栈跟踪显
  • 带有 ActionCable 的 Rails 5 应用程序可以部署在 Windows 上吗?

    我有一个 Rails 5 应用程序 我计划将其部署在 Linux 上 但由于我们需要一些访问非常特定的仅限 Windows 的软件 因此我需要将其部署在 Windows Server 2012 R2 上 我的软件堆栈 或组合 应该是 Ngi
  • Scala 中的调试日志不会影响性能

    在 C C Objective C 等语言中 通常使用预处理器宏来定义日志记录机制 这些机制甚至不会为发布的二进制文件进行编译 因此不会造成性能影响 大致如下 ifdef DEBUG printf some event we want to
  • python 中的对象初始值设定项语法(c#)?

    我想知道是否有一种快速的方法来初始化Python中的对象 例如 在 c 中 您可以实例化一个对象并设置字段 属性 例如 SomeClass myObject new SomeClass variableX value variableY 1
  • 从 ASP.NET MVC Lambda 表达式获取价值

    我正在尝试创建自己的 HTML 帮助器 它接受一个表达式 类似于内置的 LabelFor 帮助器 我找到了当表达式与此类似时获取属性值的示例 model gt model Forename 但是 在我的一些模型中 我想获取子元素中的属性 例
  • Python 电子邮件 PDF:某些 PDF 已损坏

    我正在尝试将 PDF 文件附加到电子邮件中 对于一个 PDF 打印为 PDF 的 Word 文档 它可以工作 收件人在 Outlook 中打开它没有问题 然而 对于其他 PDF 除了大了几 KB 之外 它们看起来都一样 它们会被损坏 是一个
  • Android - 解决 ACCESS_CONTENT_PROVIDERS_EXTERNALLY 权限?

    我正在构建一个应用程序 它给出了这个异常 因为我调用了一些内部方法 权限拒绝 没有从 pid 2520 uid 10047 调用 getContentProviderExternal 的权限 需要 android permission AC
  • WTSEnumerateSessions 挂起并且永不返回

    我有一个用 C 编写的 Net 服务 我正在调用WTSEnumerateSessions 会话检查由计时器每 15 分钟运行一次 它列出所有会话并将用户名 域组合与预定义的用户集进行比较 以确定他们是否登录到服务器 问题在于几个服务器的调用