列出当前活动的 CreateWaitableTimer 事件

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

尝试查找任何当前处于活动状态但仍未决的计时器,这些计时器可能会导致计算机唤醒。创建计时器时,会指定一个名称。所有命名计时器的列表将是理想的,而不仅仅是指定的名称。

这里是创建命名定时器的代码:

[DllImport("kernel32.dll")]
private static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, String lpTimerName);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);

private static int SetWaitForWakeUpTime(DateTime wakeTime) {
    long waketime = wakeTime.ToFileTime();

    String timerName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name.ToString() + "WakeUpTimer";
    using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, timerName)) {
        if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true)) {
            using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset)) {
                wh.SafeWaitHandle = handle;
                wh.WaitOne();
            }
        }
        else {
            return Marshal.GetLastWin32Error();
        }
    }
    return 0;
}

我尝试过使用

NtQuerySystemInformation
方法,但没有成功。我什至不确定该功能是否是正确使用的功能。

[DllImport("ntdll.dll", SetLastError=true)]
private static extern NtStatus NtQuerySystemInformation(uint infoClass, IntPtr info, uint size, out uint length);

我也尝试过使用命令行

powercfg /waketimers
,但是这似乎只列出了
Windows Task Scheduler
选中了
Wake the computer to run this task
选项的任务。

c# pinvoke dllimport
1个回答
0
投票

我可以解释如何做到这一点。我知道您在 C# 中寻求答案;我将解释如何用 C 或 C++ 来实现(尽管只是给出高级步骤而不是实际代码);我对 C# 的了解相当有限,所以我将把这些指令翻译成 C# 作为练习留给读者。

您需要调用

PowerInformationWithPrivileges
导出的未记录的
powrprof.dll
函数。虽然没有记录,但它的原型是相同的
CallNtPowerInformation

NTSTATUS PowerInformationWithPrivileges(
  [in]  POWER_INFORMATION_LEVEL InformationLevel,
  [in]  PVOID                   InputBuffer,
  [in]  ULONG                   InputBufferLength,
  [out] PVOID                   OutputBuffer,
  [in]  ULONG                   OutputBufferLength
);

它不在导入库中,因此在 C 或 C++ 中,您需要使用

GetProcAddress
动态加载它。 (我想这在 C# 中不是问题,
DllImport
属性将为您处理。)您将像这样调用它:

NTSTATUS status = PowerInformationWithPrivileges(WakeTimerList,NULL,0,buf,bufLength);

bufLength
需要至少 240 字节,但实际所需的大小取决于您有多少个唤醒计时器。通常的故事:检查返回的 NTSTATUS 是否为
STATUS_BUFFER_TOO_SMALL
(
0xC0000023
),如果是,则分配一个更大的缓冲区(例如,将其大小加倍)并重试。继续在循环中增加缓冲区,直到调用成功或失败并出现不同的错误。

虽然返回缓冲区的结构没有记录,但它实际上在 Windows Driver Kit 内核模式标头中给出,请查看文件

km/ntoapi.h
(例如参见copy someone put on GitHub——这是标头的一个版本来自几年前,尽管我不相信这些定义最近发生了变化。)请注意,尝试将内核模式标头导入用户模式 C/C++ 应用程序是行不通的,因为 SDK 和 WDK/DDK 标头不兼容彼此之间产生无法解决的矛盾。相反,您可以复制粘贴定义
ntpoapi.h
:

typedef struct _WAKE_TIMER_INFO {
    SIZE_T OffsetToNext;
    ULARGE_INTEGER DueTime;
    ULONG Period;
    DIAGNOSTIC_BUFFER ReasonContext;
} WAKE_TIMER_INFO, * PWAKE_TIMER_INFO;

请注意,您还需要复制

DIAGNOSTIC_BUFFER
REASON_BUFFER
REQUESTER_TYPE
的定义。

结构本质上是一个链表。如果

OffsetToNext
为零,则没有下一个条目;如果它是非零的,那就是在这个条目之后要跳过的字节数以找到下一个。
OffsetToNext
相对于该条目的开头。根据我的经验,它通常是 240 字节,但不要依赖它。

我相信

DIAGNOSTIC_BUFFER
REASON_BUFFER
中的偏移量也与相应结构的开始有关。

还要注意

UserSharedServiceRequester
,您需要使用
ServiceTag
导出的未记录的
DIAGNOSTIC_BUFFER
函数解码
I_QueryTagInformation
中的
advapi32.dll
。很容易找到这样做的示例代码,请参阅此 C# 代码此 C 代码

关于

Flags
中的
REASON_BUFFER
是什么,请参阅前面
#define
头文件中的这个
ntpoapi.h

#define DIAGNOSTIC_REASON_SIMPLE_STRING             0x00000001
#define DIAGNOSTIC_REASON_DETAILED_STRING           0x00000002

您可能会注意到

REASON_BUFFER
和那些标志与公开记录的
REASON_CONTEXT
结构
非常相似——不同之处在于缺少初始
Version
字段,并且还使用缓冲区偏移量而不是指针。

查找计时器的名称

WakeTimerList
不会给你定时器的名字。它确实告诉您哪个进程/服务器/驱动程序创建了计时器,这可能足以确定它是哪个计时器。

如果您使用未记录的

NtQueryDirectoryObject
/etc API 搜索 NT 对象管理器命名空间,您可以在其中找到命名计时器。或者您可以只使用 WinObj(或类似工具)。给出它的名字,你可以打开它,然后有一个未记录的
NtQueryTimer
API,它可以告诉你一些关于计时器的基本信息(它是否触发了,还有多少时间直到它触发)。不幸的是,似乎没有任何用户模式 API 可以从定时器句柄中找出它是否是唤醒定时器。

© www.soinside.com 2019 - 2024. All rights reserved.