【问题标题】:VBScript - Error 0x80041017 (null)VBScript - 错误 0x80041017(空)
【发布时间】:2011-07-23 05:38:49
【问题描述】:

重要更新:

正如 Cheran S 下文所述,最好避免在此脚本中使用“taskmgr”。我不打算编辑代码,因为我认为最好尽可能多地保留原始问题,因为这样做会使 Cheran 的回答和评论部分无效和混淆。

“taskmgr”的一个很好的替代品是“CharMap”(用于简单快速的测试)。


运行 Windows XP Professional(32 位)并且我得到了抛出此错误的脚本:

Script:   C:\test.vbs
Line:     40
Char:     3
Error:    0x80041017
Code:     80041017
Source:   (null)

代码如下:

Set objWshShell = Wscript.CreateObject("Wscript.Shell")

Set objWshNet = CreateObject("Wscript.Network")
strComputer = objWshNet.ComputerName

Set objWMIService = GetObject("winmgmts:" & _
    "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Dim arrWinTitle(2)
    arrWinTitle(0) = "My Documents"
    arrWinTitle(1) = "Control Panel"

Dim arrProcName(3)
    arrProcName(0) = "'taskmgr.exe'"
    arrProcName(1) = "'calc.exe'"
    arrProcName(2) = "'notepad.exe'"

Sub srBlockWindow(strWinTitle, intWinTitle, strProcName, intProcName)
    i = 0
    Do While i = 0
        If objWshShell.AppActivate(strWinTitle(intWinTitle)) Then
            objWshShell.AppActivate(strWinTitle(intWinTitle))
            Wscript.Sleep 100
            objWshShell.SendKeys "%{F4}"
            Wscript.Sleep 100
        End If

        If intWinTitle = 0 Then
            intWinTitle = intWinTitle + 1
        Else
            intWinTitle = 0
        End If

        Wscript.Sleep 100

        Set colProcesses = objWMIService.ExecQuery _
            ("SELECT * FROM Win32_Process WHERE NAME = " & strProcName(intProcName))

        For Each objProcess In colProcesses
            objProcess.Terminate()
        Next

        If intProcName >= 0 Then
            intProcName = intProcName + 1
        ElseIf intProcName >= 5 Then
            intProcName = 0
        End If

        Set colProcesses = Nothing
    Loop
End Sub

Call srBlockWindow(arrWinTitle, 0, arrProcName, 0)

我已经查看了this,但我相信我的脚本对引号没有任何问题。为了清楚起见,我在“For Each ...”字符串的开头收到了错误。

特别之处在于它第一次运行良好,但一旦循环,那就是我得到错误的时候。因此,它将关闭所有所需的 Windows/应用程序,但是一旦它完成第二次迭代,我就会收到错误消息。我已经插入了“On Error Resume Next”,但这并没有解决它(我稍后会添加它,因为当窗口/进程与脚本进行的关闭/结束/停止尝试同时启动时需要解决冲突)。

我认为这是因为我应该有条件地检查进程是否存在;问题是,我不太确定如何使用这段代码来做到这一点(我对 Collections 从来没有好过)。有人对如何专门使用此代码提出建议吗?


我查看了this 并尝试编写一个快速的替代脚本,但它并没有真正起作用。代码如下:

Set service = GetObject("winmgmts:")

Dim arrProcName(3)
    arrProcName(0) = "'taskmgr.exe'"
    arrProcName(1) = "'calc.exe'"
    arrProcName(2) = "'notepad.exe'"

Sub srTest(strProc, intProc)
    i = 0
    Do While i = 0
        For Each Process In Service.InstancesOf("Win32_Process")
            If Process.Name = strProc(intProc) Then
                Process.Name.Terminate
                Process.Terminate
            End If
        Next

        If intProc = 0 Then
            intProc = intProc + 1
        ElseIf intProc >= 3 Then
            intProc = 0
        End If
    Loop
End Sub

Call srTest(arrProcName, 0)

如您所见,我尝试了“Process.Terminate”和“Process.Name.Terminate”,但都没有产生任何结果(甚至没有错误)。我进一步使用“Wscript.Echo Process.Name”和“Wscript.Echo strProc(intProc)”对其进行了测试,但这些都不起作用。

既然我在这个替代解决方案上失败了,我觉得我在黑暗中疯狂地寻找解决方案,所以我会将这些深奥的挑战推迟到比我优越得多的社区。


这里可能有一些人正在阅读本文并想知道我为什么要针对“我的​​文档”、“控制面板”、“taskmgr.exe”、“calc.exe”和“notepad.exe”。几乎每个阅读本文的人都可能能够自行推断,但我会确保我对那些需要它的人很清楚。我这样做是因为它使测试更容易,因为所有这些都可以通过使用“运行”快捷方式(Windows 键 + R)然后输入以下字符串(当然,一次一个)来访问:

  • 我的文档
  • 控制
  • 任务管理器
  • 计算
  • 记事本

您可能知道关键字,但我只想强调为什么我使用这些特定的关键字(速度和简单性)。


如果 Cheran 将最终代码添加到发布的答案中,我将删除它

最终解决方案:

Set objWshShell = Wscript.CreateObject("Wscript.Shell")

Set objWshNet = CreateObject("Wscript.Network")
strComputer = objWshNet.ComputerName

Set objWMIService = GetObject("winmgmts:" & _
    "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Dim arrWinTitle
    arrWinTitle = Array("My Documents", "Control Panel")

Dim arrProcName
    arrProcName = Array("'charmap.exe'", "'calc.exe'", "'notepad.exe'")

Sub srBlockWindow(strWinTitle, intWinTitle, strProcName, intProcName)
    i = 0
    Do While i = 0
        On Error Resume Next
            ' In the Event of Conflict w/Initiation of Window or Process
        If objWshShell.AppActivate(strWinTitle(intWinTitle)) Then
            objWshShell.AppActivate(strWinTitle(intWinTitle))
            Wscript.Sleep 100
            objWshShell.SendKeys "%{F4}"
            Wscript.Sleep 100
        End If

        intWinTitle = (intWinTitle + 1) Mod (UBound(strWinTitle) + 1)

        Wscript.Sleep 100

        Set colProcesses = objWMIService.ExecQuery _
            ("SELECT * FROM Win32_Process WHERE NAME = " & strProcName(intProcName))

        For Each objProcess In colProcesses
            objProcess.Terminate()
        Next

        intProcName = (intProcName + 1) Mod (UBound(strProcName) + 1)

        Set colProcesses = Nothing
    Loop
End Sub

Call srBlockWindow(arrWinTitle, 0, arrProcName, 0)

这是我用来测试它的快速脚本:

Set objWshShell = Wscript.CreateObject("Wscript.Shell")

i = 0
Do While i = 0
    objWshShell.Run "explorer.exe /e, C:\Documents and Settings\User\My Documents"
    Wscript.Sleep 200
    objWshShell.Run "CharMap.exe"
    Wscript.Sleep 200
    objWshShell.Run "Control.exe"
    Wscript.Sleep 200
    objWshShell.Run "calc.exe"
    Wscript.Sleep 200
    objWshShell.Run "notepad.exe"
    Wscript.Sleep 200
Loop

小心!调整时间,以便您可以结束“Wscript.exe”而不会出现太多问题。最好同时运行两个脚本,看看它是如何工作的。

【问题讨论】:

  • 当您的脚本应该在无限循环中运行时,尤其是在社区站点上发布时,您可能不想使用“taskmgr.exe”作为示例进程名称。如何停止“wscript.exe”进程? (当然,我为自己运行代码而没有先检查它而感到羞耻......)
  • @Cheran S - 大声笑,对不起;我有一个后退。我从来没有考虑过这个问题,原因有两个:(1)我没有工作代码,rofl。 (2) 我在运行对话框中使用这个命令:TaskKill /Im Wscript.exe /F。有一个警告:我正在运行 XP Professional 客户端,而“TaskKill”命令仅适用于 XP Pro。 XP Home 的替代方案是TsKill Wscript。我相信这个原则适用于所有家庭版和专业版(XP/Vista/7)。再次抱歉。

标签: vbscript windows-xp


【解决方案1】:

我发现了两个大问题:

  1. 这里的主要问题在于您定义数组的方式。您在数组声明中指定的数字是最大的数组下标。由于 VBScript 数组总是从 0 开始索引,因此您实际上需要指定比数组中的元素数少 1。

    ' This is wrong:
    Dim arrWinTitle(2)
    Dim arrProcName(3)
    
    ' Should be:
    Dim arrWinTitle(1)
    Dim arrProcName(2)
    

    您还可以使用Array 函数来初始化您的数组,假设您事先知道其中有多少元素。在这种情况下,您只需将 arrWinTitle 声明为 Variant 而不是数组:

    Dim arrWinTitle
    arrWinTitle = Array("My Documents", "Control Panel")
    
  2. 如果您进行了更改并尝试运行脚本,您仍然会收到“下标超出范围”错误。该错误是由该块引起的:

    If intProcName >= 0 Then
        intProcName = intProcName + 1
    ElseIf intProcName >= 5 Then
        intProcName = 0
    End If
    

    首先,strProcName 的最大下标应该是 2,而不是 5。即使这样,这段代码也不起作用。看起来你想要做的是循环遍历数组的元素,然后从 0 开始。这样做更好的是使用 Mod 运算符:

    intProcName = (intProcName + 1) Mod (UBound(strProcName) + 1)
    

    还要注意我如何使用 UBound 函数来避免对数组的实际长度进行硬编码。


我不会花太多时间分析您的第二个示例,因为这只是尝试使第一个示例正常工作。但是,我会注意到,在您的 arrProcName 数组中,进程名称仍然有单引号,这是该脚本也不起作用的原因之一。

【讨论】:

  • 糟糕!对于您的第二点,我完全忘记将 ElseIf 字符串编辑为 2!我正在使用更多项目进行原型设计,并从第二个数组中删除了一些,但我没有清理交互代码块。给我一些时间来消化你的帖子,因为你使用 Array 函数、Mod 运算符和 UBound 函数对我来说都是新的(感谢你记住我是一个完整的新手并添加了这些资源链接!)。在我测试并弄清楚完整脚本的外观后,我会确保为您提供“复选标记”。不过 =D,您不必像上次那样等待。
  • 因为我很笨(实际上,真的很笨),所以我花了一段时间才把它拼凑起来。以防万一有人像我一样愚蠢(这很难完成,所以我假设这可能是一个比我更新 VBS 的人)我已经将最终代码发布到问题中。当然,除非您想将其复制并粘贴到您的答案 Cheran 中?不幸的是,我还没有真正弄清楚 UBound 是如何工作的(“下标”这个词让我回避了);我最终会得到它,但就目前而言,我对工作代码 =D 感到满意。非常感谢 Cheran!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
相关资源
最近更新 更多