怎么设置啊
还有设置一个时钟,一秒中时间变化一次
retval = SetTimer(Me.hwnd, 1, 500, AddressOf TimerProc)
为什么这个程序中设置的不是1000微秒而是500微秒呢?

解决方案 »

  1.   

    AddressOf TeimerProc是时钟触发的时候调用的程序的地址啊,最好放在模块里。
    retval = SetTimer(Me.hwnd, 1, 500, AddressOf TimerProc)表示没0.5秒调用一次TimerProc
      

  2.   

    将函数指针传递到 DLL 和类型库
    熟悉 C 语言的程序员一定会熟悉函数指针的概念。对于不熟悉 C 语言的读者,有必要对此进行一番解释。函数指针是一种约定,程序员可以用它将一个自定义的函数的地址作为参数传递到另一个函数。后面一个函数可以不是自己编写的,但是已经进行了声明,所以可以在应用程序中使用。利用函数指针,可以调用 EnumWindows 等函数列出系统中打开的窗口,利用 EnumFontFamilies 列出所有的当前字体。利用函数指针还可以访问 Win32 API 中的其它许多函数,早期的 Visual Basic 没有提供对它们的支持。在 Visual Basic 5.0 中,使用函数指针时仍然存在若干限制。详细信息,请参阅本帮助主题后面的“函数指针的局限与风险”。有关函数指针的知识
    使用例子可以很好地说明函数指针的用法。首先,看一看 Win32 API 中的 EnumWindows 函数:Declare Function EnumWindows lib "user32" _
    (ByVal lpEnumFunc as Long, _
    ByVal lParam as Long ) As LongEnumWindows 是一个枚举函数,它能够列出系统中每一个打开的窗口的句柄。EnumWindows 的工作方式是重复地调用传递给它的第一个参数(lpEnumFunc,函数指针)。每当 EnumWindows 调用函数,EnumWindows 都传递一个打开窗口的句柄。在代码中调用 EnumWindows 时,可以将一个自定义函数作为第一个参数传递给它,用来处理一系列的值。例如,可以编写一个函数将所有的值添加到一个列表框中,将 hWnd 值转换为窗口的名字,以及其它任何操作!为了表明传递的参数是一个自定义函数,在函数名称的前面要加上 AddressOf 关键字。第二个参数可以是合适的任何值。例如,如果要把 MyProc 作为函数参数,可以按下面的方式调用 EnumWindows:x = EnumWindows(AddressOf MyProc, 5)在调用过程时指定的自定义函数被称为回调函数。回调函数(通常简称为“回调”)能够对过程提供的数据执行指定的操作。回调函数的参数集必须具有规定的形式,这是由使用回调函数的 API 决定的。关于需要什么参数,如何调用它们,请参阅 API 文档。使用 AddressOf 关键字
    如果代码要调用 Visual Basic 5.0 的函数指针,则必须将该代码放到标准的 .BAS 模块中,不可以将其放到类模块中,也不能将其附加到窗体上。在使用 AddressOf 关键字声明函数时,必须注意下列事项: AddressOf 只能紧接在是参数列表中的参数前;该参数可以是自定义的过程、函数或者属性的名字。
    写在 AddressOf 后面的过程、函数、属性必须与有关的声明和过程在同一个工程中。
    AddressOf 只能用于自定义的过程、函数和属性,不能将其用于 Declare 语句声明的外部函数,也不能将其用于类型库中的函数。
    在声明的 Sub、Function 或自定义的类型定义中,可以将函数指针传递到 As Any 或 As Long 类型的参数。 
    注意   可创建用 Visual C++ (或类似的工具)编译的 DLL 中的回调函数原型。要使用 AddressOf 时,原型必须使用 __stdcall 调用约定。不能将缺省调用约定与 AddressOf 并用在变量中存储函数指针
    在某些情况下,在将函数指针传递到 DLL 之前需要将其存储在一个中间变量中。如果需要将函数指针从一个 Visual Basic 传递到另一个,这种做法是很有用的。例如,在调用 RegisterClass 之类的函数时就需要用结构 (WndClass) 的成员来传递函数指针。要将一个函数指针赋予结构中的一个成员,需要编写一个包装函数(wrapper)。例如,下面创建的 FnPtrToLong 就是一个包装函数,使用它可以将函数指针放入任何结构中:Function FnPtrToLong (ByVal lngFnPtr As Long) As Long
       FnPtrToLong = lngFnPtr
    End Function要使用该函数,首先需要声明类型,然后再调用 FnPtrToLong。AddressOf 加上回调函数的名字作为函数的参数。Dim mt as MyType
    mt.MyPtr = FnPtrToLong(AddressOf MyCallBackFunction)子类派生
    利用子类派生技术,可以截取发送到控件或窗体的消息。通过截取这些消息,可以编写自己的代码来改变或者扩展对象的行为。类的派生技术比较复杂,对它的全面讨论将超出本书的范围。下例只能提供该技术的大致轮廓。重点   当 Visual Basic 处于中断模式时,不允许调用 vtable 方法或 AddressOf 函数。为了保证安全,Visual Basic 仅仅将 0 返回到 AddressOf 函数的调用者。对于子类派生情况,这意味着 WindowProc 将 0 返回到 Windows。Windows 要求它的许多消息返回非 0 值,因此返回的常数 0 将导致 Windows 与 Visual Basic 之间的死锁,从而迫使进程终止。在下例中,应用程序包括一个简单的窗体,其中只有两个命令按钮。代码的作用是截取发送到窗体的 Windows 消息,并在“立即”窗口中打印出这些消息的值。代码的第一部分是声明部分,包括 API 函数声明,常数声明和变量声明:Declare Function CallWindowProc Lib "user32" Alias _
    "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
       ByVal hwnd As Long, ByVal Msg As Long, _
       ByVal wParam As Long, ByVal lParam As Long) As LongDeclare Function SetWindowLong Lib "user32" Alias _
    "SetWindowLongA" (ByVal hwnd As Long, _
    ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPublic Const GWL_WNDPROC = -4
    Global lpPrevWndProc As Long
    Global gHW As Long下一步,使用两个例程“钩入”消息流。第一个过程 (Hook) 调用了 SetWindowLong 函数,它使用了 GWL_WNDPROC 索引来创建窗口类的子类,窗口类是用来创建窗口的。然后它使用 AddressOf 关键字和回调函数 (WindowProc) 来截取消息并在“立即”窗口中打印消息的值。第二个过程 (Unhook) 关闭了子类,重新使原来的 Windows 过程成为回调函数。Public Sub Hook()
       lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _
       AddressOf WindowProc)
    End SubPublic Sub Unhook()
       Dim temp As Long
       temp = SetWindowLong(gHW, GWL_WNDPROC, _
       lpPrevWndProc)
    End SubFunction WindowProc(ByVal hw As Long, ByVal uMsg As _
    Long, ByVal wParam As Long, ByVal lParam As Long) As _
    Long
       Debug.Print "Message: "; hw, uMsg, wParam, lParam
       WindowProc = CallWindowProc(lpPrevWndProc, hw, _
       uMsg, wParam, lParam)
    End Function最后,窗体的代码设置了 hWnd 的初始值,按钮的代码仅仅调用了上面的两个例程:Private Sub Form_Load()
       gHW = Me.hwnd
    End SubPrivate Sub Command1_Click()
       Hook
    End SubPrivate Sub Command2_Click()
       Unhook
    End Sub使用函数指针的局限与风险
    使用函数指针是有风险的。每当调用 DLL 的时候,就失去了 Visual Basic 开发环境的稳定性,使用函数指针的危险性就更大了,因为它很容易导致应用程序失败并因此而丢失已完成的工作。在工作的时候必须经常地保存和备份工作成果。下面列出了使用函数指针的一些注意事项: 调试。在中断模式下,如果应用程序引发了回调函数,那么回调函数将被正常执行,但是断点和单步设置将被忽略。如果回调函数产生了异常,则可以捕捉到它并返回当前值。在中断模式下,如果堆栈上有回调函数,那么禁止复位。
    形实转换程序。形实转换是 Windows 实现代码重定位的手段。如果在中断模式下删除回调函数,它的形实转换程序将被修改返回 0。该值通常总是正确的,但是也有意外的情况。如果在中断模式下先删除了一个回调函数,然后又再次将其加入,那么有些被调用者可能对新的函数地址一无所知。在 .exe 中不使用形实转换程序,指针被直接传递到入口点。
    被传递的函数的签名有误。如果函数的参数个数与调用者期望的不一致,或者在调用某个参数时错误地使用 ByRef 或 ByVal,则应用程序可能会失败。因此,在传递函数时签名一定要正确。
    将函数传递给不存在的 Windows 过程。在对某个窗口进行子类派生的时候,需要将一个函数指针作为 Windows 过程 (WindowProc) 传递给 Windows。但是,在 IDE 中运行应用程序时,在调用 WindowProc 时下一层函数可能已经被破坏了。这可能导致一般性保护错误,并使 Visual Basic 开发环境遭到破坏。
    不支持“Basic 到 Basic”的函数指针。在 Visual Basic 的内部不能传递指向 Visual Basic 函数的指针。目前,只支持从 Visual Basic 到 DLL 函数的指针。
    回调过程中包含错误。回调过程中的任何错误都不应回传最初调用它的外部过程,这是很重要的。可以通过在回调过程的开始加上 On Error Resume Next 语句来实现。
      

  3.   

    那是timer的回掉函数,传入的是函数指针,这个函数是只能放在模块里。否则用ADDRESSOF 取不到的。
      

  4.   

    那么
    设置一个时钟,一秒中时间变化一次retval = SetTimer(Me.hwnd, 1, 500, AddressOf TimerProc)为什么这个程序中设置的不是1000微秒而是500微秒呢?(ps:这是我从网上下的一个源码里的)
      

  5.   

    “重点   当 Visual Basic 处于中断模式时,不允许调用 vtable 方法或 AddressOf 函数。为了保证安全,Visual Basic 仅仅将 0 返回到 AddressOf 函数的调用者。对于子类派生情况,这意味着 WindowProc 将 0 返回到 Windows。Windows 要求它的许多消息返回非 0 值,因此返回的常数 0 将导致 Windows 与 Visual Basic 之间的死锁,从而迫使进程终止。”我的程序就出现上述问题,强行退出了vb,有什么办法吗?
      

  6.   

    用API进行SetTimer,必然要用到SubClass,子类调试上是有些麻烦,一般都得凭经验分析尝试,若没把握请不在子类相关的过程中设置断点,建议仅用Debug输出中间结果。强行退出了vb是,调试子类处理程序中最常见的现象,没代码很难说清,需要自己分段找出可能出错的部分(为找它你可能需准备意外退出N次),你把相关代码贴出来,别人也可帮你分析。
      

  7.   

    'timeproc函数
    Public Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)    Dim timestr As String * 260
        Dim slength As Long
        Dim retval As Long
        Dim lastTime As SYSTEMTIME
        
        
        On Error Resume Next
        
        GetLocalTime systime
        
        slength = GetTimeFormat(0, 0, systime, CLng(0), timestr, Len(timestr))
        
        lastTime = DateDiff("mm:ss", systime, shut_Time)
        
        Me.Label1.Caption = lastTime
        
        If CDate(lastIime) < CDate("00:00") Then
        retval = KillTimer(shutdown.hwnd, 1)
           ShutdownPC
        End If
        
    End Sub'下面是调用它的函数
    Private Sub Form_Load()
        Dim retval As Long
       AlwaysOnTop Me
       retval = SetTimer(Me.hwnd, 1, 500, AddressOf TimerProc)'调试到这里就强行关闭vb,好像是没调用timerproc
    End Sub