GetLastInputInfo 是特定于用户的 - 是否有类似的东西可以提供机器范围内的最后输入时间

问题描述 投票:0回答:3

根据 MSDN,http://msdn.microsoft.com/en-us/library/ms646302%28VS.85%29.aspx

GetLastInputInfo 不提供 系统范围内的用户输入信息 跨所有正在运行的会话。相当, GetLastInputInfo 提供 特定于会话的用户输入 仅适用于该会话的信息 调用该函数。

是否有类似的东西可以提供系统范围内的最后用户输入信息?

c# .net input idle-processing
3个回答
2
投票

我相信做到这一点的唯一方法是通过挂钩到 shell。

(显然)这是必须小心完成的事情,并且在操作系统完全支持之前(直到 Windows 7 之前)在托管代码中是不可行的,因此您将不得不使用一些非托管代码来实现这一点,也许是更新一些可从托管代码查询的全局状态。其 API 是 SetWindowsHookEx

随着 Vista 的会话隔离,您将需要更高的权限才能对其他会话执行此操作。在这方面,从键盘记录器的工作方式中获取线索可能会有所帮助,但我没有现成的链接到某些来源。

首先,这里是来自 condor 的 Windows 端口的源代码,用于发现键盘活动:

/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/


#include <windows.h>

// Shared DATA
// put in here data that is needed globally
#pragma data_seg(".SHARDATA")
HHOOK hHook = NULL;
LONG KBkeyhitflag = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARDATA,RWS")

__declspec(dllexport) LRESULT CALLBACK KBHook(int nCode, WPARAM wParam,
LPARAM lParam)
{
    InterlockedExchange(&KBkeyhitflag,1);

    return CallNextHookEx(hHook,nCode,wParam,lParam);
}

HINSTANCE g_hinstDLL = NULL;

#if defined(__cplusplus)
extern "C" {
#endif //__cplusplus


int __declspec( dllexport) WINAPI KBInitialize(void)
{
    hHook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KBHook,g_hinstDLL,0);
    return hHook ? 1 : 0;
}

int __declspec( dllexport) WINAPI KBShutdown(void)
{
    if ( UnhookWindowsHookEx(hHook) )
        return 1;   // success
    else
        return 0;   // failure
}

int __declspec( dllexport)  WINAPI KBQuery(void)
{
    if ( InterlockedExchange(&KBkeyhitflag,0) )
        return 1;   // a key has been hit since last query
    else
        return 0;   // no keys hit since asked last
}

#if defined(__cplusplus)
} // extern "C"
#endif //defined(__cplusplus)

BOOL WINAPI DllMain(HANDLE hInstDLL, ULONG fdwReason, LPVOID lpReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            g_hinstDLL = (HINSTANCE)hInstDLL;
            DisableThreadLibraryCalls(g_hinstDLL);
            break;
    }
    return 1;
}

0
投票

我最近一直在做一些工作。多个用户可以使用

WTSServer
获取他们的
WTSInfo
,里面包含了很多用户的信息,希望对你有帮助。

typedef struct _WTSINFOA {
  WTS_CONNECTSTATE_CLASS State;
  DWORD                  SessionId;
  DWORD                  IncomingBytes;
  DWORD                  OutgoingBytes;
  DWORD                  IncomingFrames;
  DWORD                  OutgoingFrames;
  DWORD                  IncomingCompressedBytes;
  DWORD                  OutgoingCompressedBy;
  CHAR                   WinStationName[WINSTATIONNAME_LENGTH];
  CHAR                   Domain[DOMAIN_LENGTH];
  CHAR                   UserName[USERNAME_LENGTH + 1];
  LARGE_INTEGER          ConnectTime;
  LARGE_INTEGER          DisconnectTime;
  LARGE_INTEGER          LastInputTime;
  LARGE_INTEGER          LogonTime;
  LARGE_INTEGER          CurrentTime;
} WTSINFOA, *PWTSINFOA;

0
投票

您可以使用间接方法,并使用 csrss.exe 进程中的 ReadOperationCount 来获取 会话 id
csrss 将通过 键盘或鼠标输入
更改其 IO 读取 我创建了示例代码,您可以在其中测量所有会话在两个特定时间点的 ReadOperationCount,然后比较两个对象
如果发生了变化,至少有一个会话没有闲置。
如果没有任何变化,所有会话都处于空闲状态。

您必须拥有管理权限

function Get-SessionIDReadIOCounts {
    # Query Win32_Process to get all processes and their session IDs
    $processes = Get-CimInstance -ClassName Win32_Process
    
    # Select and display unique session IDs
    $sessionIds = $processes | Select-Object -Unique SessionId
    $sessionIds = $sessionIds | Where-Object SessionId -ne 0 # Exclude Session 0
    
    $sessionIds | ForEach-Object {
        $sessionId = $_.SessionId
        # Query Win32_Process for the process named "csrss.exe" with the specified sessionId
        # source idea: https://www.autoitscript.com/forum/topic/160918-how-to-detect-user-idle-from-system-process-or-service/
        $colItems = Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name='csrss.exe' AND SessionId=$sessionId"
    
        # Iterate through each item and return ReadOperationCount
        ForEach ($objItem In $colItems) {
            $myReadIOcount = $objItem.ReadOperationCount
        }
        return [psCustomObject]@{
            sessionID   = $sessionId
            ReadIOCount = $myReadIOcount
        }
    }
}
    
    
$timespan = 5
$firstCheck = Get-SessionIDReadIOCounts
Start-Sleep -Seconds $timespan
$secondCheck = Get-SessionIDReadIOCounts
$comparision = Compare-Object -ReferenceObject $firstCheck -DifferenceObject $secondCheck -Property sessionID, ReadIOCount
if (-not $comparision) {
    Write-Host("No User Interaction in all Sessions for {0} seconds" -f $timespan) -foregroundcolor Red
    # now do your evil stuff and shut down or restart the computer.
}
© www.soinside.com 2019 - 2024. All rights reserved.