如何通过CallNtPowerInformation获取LastSleepTime?代码示例求助
使用CallNtPowerInformation获取LastSleepTime的完整示例
嘿,我看到你在调用CallNtPowerInformation接口获取上次睡眠时间时遇到了问题,我来帮你梳理下正确的实现方式,附上可运行的完整代码:
首先,你原来的DllImport声明有个小问题:lpOutputBuffer不需要用ref修饰,我们需要手动分配内存缓冲区传递给API,调用完成后再从缓冲区读取数据。另外,要明确LastSleepTime对应的POWER_INFORMATION_LEVEL枚举值是27,返回的是一个64位的FILETIME格式时间值。
完整代码实现
using System; using System.Runtime.InteropServices; public class PowerInfoHelper { // 定义POWER_INFORMATION_LEVEL枚举,这里只列出我们需要的LastSleepTime private enum POWER_INFORMATION_LEVEL : uint { LastSleepTime = 27 } // 修正后的DllImport声明 [DllImport("PowrProf.dll", SetLastError = true)] private static extern uint CallNtPowerInformation( POWER_INFORMATION_LEVEL InformationLevel, IntPtr lpInputBuffer, uint nInputBufferSize, IntPtr lpOutputBuffer, uint nOutputBufferSize ); public DateTime? GetLastSleepTime() { // LastSleepTime返回的是8字节的LARGE_INTEGER(对应.NET的long) const int bufferSize = 8; IntPtr outputBuffer = IntPtr.Zero; try { // 分配非托管内存缓冲区 outputBuffer = Marshal.AllocHGlobal(bufferSize); // 调用API获取LastSleepTime uint result = CallNtPowerInformation( POWER_INFORMATION_LEVEL.LastSleepTime, IntPtr.Zero, // 不需要输入缓冲区 0, outputBuffer, (uint)bufferSize ); // 检查调用是否成功(返回0表示成功) if (result != 0) { int errorCode = Marshal.GetLastWin32Error(); throw new System.ComponentModel.Win32Exception(errorCode); } // 从缓冲区读取64位整数(FILETIME格式) long fileTime = Marshal.ReadInt64(outputBuffer); // 将FILETIME转换为DateTime(FILETIME是从1601年1月1日开始的100纳秒间隔) if (fileTime == 0) { // 如果返回0,表示系统从未进入过睡眠状态 return null; } return DateTime.FromFileTime(fileTime); } finally { // 释放非托管内存,避免泄漏 if (outputBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(outputBuffer); } } } // 调用示例 public static void Main() { PowerInfoHelper helper = new PowerInfoHelper(); try { DateTime? lastSleep = helper.GetLastSleepTime(); if (lastSleep.HasValue) { Console.WriteLine($"上次睡眠时间:{lastSleep.Value:yyyy-MM-dd HH:mm:ss}"); } else { Console.WriteLine("系统从未进入过睡眠状态"); } } catch (Exception ex) { Console.WriteLine($"获取失败:{ex.Message}"); } } }
关键说明
- 缓冲区管理:必须手动分配和释放非托管内存,因为
CallNtPowerInformation不会自动管理这些内存,忘记释放会导致内存泄漏。 - 错误处理:调用失败时通过
Marshal.GetLastWin32Error()获取具体的Windows错误码,方便排查问题。 - 时间转换:返回的
FILETIME值需要用DateTime.FromFileTime()转换为.NET的DateTime类型,如果返回0则表示系统从未睡眠过。 - 参数修正:把
nInputBufferSize和nOutputBufferSize改为uint类型,更贴合原生API的参数定义,避免类型转换问题。
你可以直接运行这段代码,测试获取上次睡眠时间的功能,如果还有问题,可以查看控制台输出的错误信息来进一步排查。
内容的提问来源于stack exchange,提问作者user3525444
相关产品推荐
相关产品推荐

