使HTA的setTimeout或setInterval在VBScript类中调用Sub / Function

时间:2013-09-14 17:40:22

标签: class vbscript settimeout setinterval hta

嗯,标题说明了一切。我有一个带有VBS类的HTA,我试图用另一个类sub作为其“函数”参数调用setInterval,但是我得到一个“类型不匹配”错误。 这可以通过任何直接或解决方式的黑客形式完成吗?我唯一能想到的就是在类之外使用“参数”函数,但这首先击败了类的目的...... 救命啊!

编辑(示例代码):

Class My_Class
    Private TimerID

    Public Sub Sub1(param)
        Dim x
        x = DoSomeCalculations(param)
        TimerID = window.setInterval("Sub2(x)", 1000, "VBScript")
    End Sub

    Private Sub Sub2(param)
        Dim y
        y = DoSomeMoreCalculations
        If param = y Then window.clearInterval(TimerID)
    End Sub
End Class

3 个答案:

答案 0 :(得分:1)

迟到的答案,但......应该有效:

Class My_Class
    Private TimerID

    Public Sub Sub1(param)
        Dim x
        x = DoSomeCalculations(param)
        Dim cb
        Set cb = New MyCallback
        Set cb.Target = Me
        cb.ParamValue = x
        TimerID = window.setInterval(cb , 1000, "VBScript")
    End Sub

    ' must be Public
    Public Sub Sub2(param)
        Dim y
        y = DoSomeMoreCalculations
        If param = y Then window.clearInterval(TimerID)
    End Sub
End Class

Class MyCallback

    Public Target
    Public ParamValue

    ' must be Public and Default
    Public Default Sub DoCall

        Target.Sub2 ParamValue
    End Sub
End Class

window.setTimeout和window.setInterval将调用Default Sub或Default Function。不幸的是,它不能用在事件处理程序中。

答案 1 :(得分:0)

根据this article想法,setInterval会将'function'字符串传递给GetRef(),但它似乎更像是eval:

<html>
 <head>
  <Title>SetIntervalDemo</Title>
  <hta:application id="SetIntervalDemo" scroll = "no">
  <script type="text/vbscript">
   Dim g_sp0
   Sub sp0()
     MsgBox "sp0 called"
     ClearInterval g_sp0
   End Sub
   Sub sisp0()
     g_sp0 = SetInterval(GetRef("sp0"), 1000)
'    g_sp0 = SetInterval("mp0", 1000) <---- Type Mismatch
   End Sub
   Dim g_sp1
   Sub sp1(x)
     MsgBox "sp1 called: " & x
     ClearInterval g_sp1
   End Sub
   Sub sisp1()
     g_sp1 = SetInterval("sp1(4711 + g_sp1)", 1000)
   End Sub
   Dim g_mp0_a
   Dim g_o_a
   Dim g_mp0_b
   Dim g_o_b
   Sub simp0()
     Set g_o_a = New cC : g_o_a.m_sName = "Alpha"
     Set g_o_b = New cC : g_o_b.m_sName = "Beta"
     g_mp0_a = SetInterval("g_o_a.mp0", 1000)
     g_mp0_b = SetInterval("g_o_b.mp0", 1000)
   End Sub
   Class cC
     Public m_sName
     Public Sub mp0()
       MsgBox m_sName & ".mp0 called"
       ClearInterval g_mp0_a
       ClearInterval g_mp0_b
     End Sub
   End Class
  </script>
 </head>
 <body>
  <input type="button" value="sp0" onclick="sisp0" />
  <input type="button" value="sp1" onclick="sisp1" />
  <input type="button" value="mp0" onclick="simp0" />
 </body>
</html>

因此,使用Subs,一些全局变量和方法调用("g_o_a.mp0")可能是定期调用方法的方法。 (在生产代码中使用之前,请仔细测试。)

答案 2 :(得分:0)

我认为不可能在实例本身中调用一个方法(即通过Me),因为在调用计时器时上下文会丢失。

你可以使用全局变量(尽管很讨厌)。问题在于,如果您有多个同一个类的实例,则每个实例都需要一个全局变量,并且代码需要知道要调用的变量。

但是,上面有一个解决方法 - 使用数组或字典作为单个全局对象,然后使用名称或ID来标识该集合中的实例。

现在,当您使用计时器时,调用类外部的方法,将实例的标识符作为值传递。您调用的方法可以在集合中查找此id,返回您的实例,然后可以在该实例上调用相关方法。

有点破解,但它有效 - 可以在下面使用演示hta应用程序看看它的实际效果。

搜索字符串'!Important以查看代码的关键位。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <script type="text/vbs" language="vbscript">
            option explicit

            dim dummies '!Important - this is the global variable holding all your instances

            sub CreateDummyInstances()
                dim dum
                set dummies = CreateObject("Scripting.Dictionary")  
                set dum = (new Dummy)("Makaveli84", 5000) 
                set dum = (new Dummy)("JohnLBevan", 7000) 
                set dum = (new Dummy)("Ekkehard.Horner", 10000)
                set dum = nothing 
            end sub

            class Dummy

                private m_name
                private m_timeoutMilSec
                private m_timerOn
                private m_timerRunningLock

                private sub Class_Initialize
                    m_timerOn = false
                    m_timerRunningLock = false
                end sub

                public default function Dummy(name, timeoutMilSec)
                    m_name = name
                    m_timeoutMilSec = timeoutMilSec
                    dummies.add name, me '!Important - add this new instance to our collection
                    CreateButton
                    set Dummy = me
                end function

                public property Get Name
                    Name = m_name
                end property

                public property Get IsTimerOn
                    IsTimerOn = m_timerOn
                end property

                public sub BeginTimer()
                    m_timerOn = true
                    if not m_timerRunningLock then 'avoid creating two threads if an off-on occurs within a single timer wait
                        TimerLoop
                    end if
                end sub

                public sub EndTimer()
                    m_timerOn = false
                end sub

                public sub TimerLoop()
                    if m_timerOn then 'get out of jail free (altered by separate thread)
                        m_timerRunningLock = true
                        PerformSomeAction

                        'this doesn't work because Me loses its context when called by the timer
                        'window.setTimeout "Me.TimerLoop", m_timeoutMilSec, "VBScript"

                        'so instead we pass a name / id for this object as a parameter to an external function
                        'and have that lookup this instance and externally call the method we wanted to call
                        window.setTimeout "TheFunkyTrick(""" & m_name & """)", m_timeoutMilSec, "VBScript" '!Important - call the external function

                    else
                        m_timerRunningLock = false
                    end if
                end sub

                private sub CreateButton()
                    dim p
                    dim button
                    set p = document.createElement("p")
                    set button = document.createElement("button")
                    button.id = "btnStart" & m_name 
                    button.innerText = "Start " & m_name
                    AddClickEventHandler button, "StartTimer"
                    p.appendChild button
                    set button = document.createElement("button")
                    button.id = "btnStop" & m_name 
                    button.innerText = "Stop " & m_name
                    AddClickEventHandler button, "StopTimer"
                    p.appendChild button
                    divButtons.appendChild p
                    set button = Nothing
                    set p = Nothing
                end sub

                private sub AddClickEventHandler(objButton, strFunctionName)
                    dim fun
                    set fun = getRef(strFunctionName)
                    call objButton.attachEvent("onclick", fun)
                    set fun = Nothing
                end sub

                sub PerformSomeAction
                    msgbox "Hello from " & m_name & ".  I show up every " & cstr(cint(m_timeoutMilSec/1000)) & " seconds, until stopped."
                end sub

            end class

            function vbInit()
                CreateDummyInstances
            end function

            function GetDummy(name)
                if dummies.exists(name) then
                    set GetDummy = dummies(name) '!Important - get desired instance from the collection (assuming it exists)
                else
                    msgbox "name not found: " & name
                    set GetDummy = nothing 'the way I've coded stuff below this would cause an exception (i.e. since I've not bothered to check if it's nothing) - but as this is a demo that's fine
                end if
            end function

            function GetNameFromButtonEvent(objEvent, boilerplate)
                GetNameFromButtonEvent = Right(objEvent.srcElement.Id, len(objEvent.srcElement.Id) - len(boilerplate))
            end function

            sub StartTimer(objEvent)
                dim name
                name = GetNameFromButtonEvent(objEvent, "btnStart")
                GetDummy(name).BeginTimer
            end sub
            sub StopTimer(objEvent)
                dim name
                name = GetNameFromButtonEvent(objEvent, "btnStop")
                GetDummy(name).EndTimer
            end sub
            sub TheFunkyTrick(name) '!Important - call the method on the relevant instance
                GetDummy(name).TimerLoop
            end sub
        </script>
        <HTA:APPLICATION 
            ApplicationName="Stack Overflow VBScript Class Timer Demo"
            Caption="Yes"
            icon="img/favicon.ico"
            Scroll="no"
            SingleInstance="yes"
            WindowState="normal"
            ShowInTaskbar="Yes"
            SysMenu="Yes"
            MaximizeButton="No"
            ShowInTaskbar="Yes"
            MinimizeButton="No"
            Navigable="No"
            Border="Thin"
            BORDERSTYLE="Complex"
            INNERBORDER="No"
        />
        <title>Stack Overflow VBScript Class Timer Demo</title>
    </head>
    <body onload="vbInit()" language="vbscript">
        <h1>Demo</h1>
        <div id="divButtons"></div>
    </body>
</html>