您好,JL。您知道,您的问题中一定要引用“旧”这个词,是吧?就在今天,一位脚本专家收到了“美国退休人士协会”(AARP) 的一封来信。尽管信中承认这位脚本专家还不到 50 岁(因此,还不具备成为其成员的资格),但确实邀请了这位脚本专家预先注册成员身份;毕竟,这封信便已暗示了时间不会很久了,对吧?考虑周到的是,信中还附带了一个成员身份样卡,上面饰有该脚本专家的名字。相信我们:如果之前您从未认为自己变老了,那么您就不会在 AARP 成员身份卡上看到自己的名字。
言归正传;毕竟,您不是在寻找年老的脚本专家,而是要查找事件日志中的旧事件。(您很幸运,看看,这里并没有年老的脚本专家。尽管我们有时对 Jean 有些质疑。)最初看来这个问题似乎有点棘手,原因有二。其一,很难将类似于“找到集合中的最旧项”的想法转换成 WQL 查询;WQL 查询语言不支持 MIN 或 MAX 之类的关键字。当然,您可以始终只获取所有事件,然后再确定哪个事件是最旧的,但这样则会引入另外一个问题:既然不能在 WQL 查询中指定排序顺序,又如何知道哪个事件是最旧的呢?这本来应该是一个很简单的问题,居然会引出这么多问题!
但是,有志者事竟成,是不是?(不,不要有那种想法。我们一直在告诉您,我们并没有那么老!)我们发现,每次向事件日志中写入事件时,都会为该事件分配一个记录号:写入日志的第一个事件被分配编号 1,第二个事件被分配编号 2,依此类推。即使在清除事件日志后,这一规则同样适用。假定事件日志中有 1,000 个事件,然后您清除了该日志。试想一下,对于写入清除后日志的第一个事件,将为其分配哪个记录号?答对了: 1。
我们为什么要关心这个问题呢?因为这意味着事件日志中最旧事件的记录号将始终为 1。也就是说,这意味着只需查找事件 1 便能找到事件日志中的最旧记录。可以使用如下脚本来执行此操作:
strComputer = "."
Set objWMIService = GetObject("winmgmts:{(Security)}//" & strComputer & "/root/cimv2")
Set colEvents = objWMIService.ExecQuery _
("Select * from Win32_NTLogEvent Where Logfile = 'System' " & _
"AND RecordNumber = 1")
For Each objEvent in colEvents
Wscript.Echo "Time Written: " & objEvent.TimeWritten
Next
正如您所看到的那样,这是一个相当普通的 WMI 脚本。我们首先连接到本地计算机上的 WMI 服务,尽管(正如我们不厌其烦地指出的)该脚本也可以在远程计算机上运行。然后,我们使用下面的 WQL 查询从 System 事件日志返回 RecordNumber 等于 1 的所有事件:
Set colEvents = objWMIService.ExecQuery _
("Select * from Win32_NTLogEvent Where Logfile = 'System' " & _
"AND RecordNumber = 1")
因为记录号必须唯一,所以我们知道集合中只有一项。另外,由于写入事件日志的第一个事件的记录号将为 1,因此还知道我们已经找到了最旧的事件。现在,剩下要做的事情就是设置 For Each 循环来遍历这个只有一项的集合,然后回显 TimeWritten 属性的值:
For Each objEvent in colEvents
Wscript.Echo "Time Written: " & objEvent.TimeWritten
Next
需要补充说明的是(不论是好是坏),将以 UTC(通用协调时间)格式返回 TimeWritten。但是没关系:如果您要寻找一种简单的方法将该 UTC 值转换为常规(并可读)的日期时间值,请看一下“脚本中心脚本存储库”中的这个示例脚本。
现在,我们知道您正在想什么:如果可以找到事件日志中的最旧事件,那么这是否意味着也可以找到添加到事件日志中的最新事件呢?那当然。不过,您必须要等到明天才能知道如何去做。毕竟,到了我们这个年龄,每天一个专栏已经是竭尽所能了。