Quantcast

Jump to content


Photo

[VB.Net] SecondsToWait


  • Please log in to reply
5 replies to this topic

#1 Eggy

Eggy
  • Banned from trading - Do not trade with this user.

  • 1783 posts


Users Awards

Posted 04 June 2011 - 03:20 PM

There seems to be a strange problem with the SecondsToWait function I use in my Autobuyer. Basically, the problem is that even SecondsToWait(1) basically never exits, causing an infinite loop somewhere. Strangely, this problem seemed to pop up overnight, the function seemed to work before, which is weird.

I feel like there must be some kind of built in "wait x milliseconds" function built into VB.net, but I can't find anything online. I feel like I am stuck here haha. Any help?

Module SleepModule
	
	Private Structure FILETIME
		Dim dwLowDateTime As Integer
		Dim dwHighDateTime As Integer
	End Structure
	
	Private Const WAIT_ABANDONED As Integer = &H80
	Private Const WAIT_ABANDONED_0 As Integer = &H80
	Private Const WAIT_FAILED As Integer = -1
	Private Const WAIT_IO_COMPLETION As Integer = &HC0
	Private Const WAIT_OBJECT_0 As Integer = 0
	Private Const WAIT_OBJECT_1 As Integer = 1
	Private Const WAIT_TIMEOUT As Integer = &H102
	
	Private Const INFINITE As Short = &HFFFFs
	Private Const ERROR_ALREADY_EXISTS As Short = 183
	
	Private Const QS_HOTKEY As Integer = &H80s
	Private Const QS_KEY As Integer = &H1s
	Private Const QS_MOUSEBUTTON As Integer = &H4s
	Private Const QS_MOUSEMOVE As Integer = &H2s
	Private Const QS_PAINT As Integer = &H20s
	Private Const QS_POSTMESSAGE As Integer = &H8s
	Private Const QS_SENDMESSAGE As Integer = &H40s
	Private Const QS_TIMER As Integer = &H10s
	Private Const QS_MOUSE As Integer = (QS_MOUSEMOVE Or QS_MOUSEBUTTON)
	Private Const QS_INPUT As Integer = (QS_MOUSE Or QS_KEY)
	Private Const QS_ALLEVENTS As Integer = (QS_INPUT Or QS_POSTMESSAGE Or QS_TIMER Or QS_PAINT Or QS_HOTKEY)
	Private Const QS_ALLINPUT As Integer = (QS_SENDMESSAGE Or QS_PAINT Or QS_TIMER Or QS_POSTMESSAGE Or QS_MOUSEBUTTON Or QS_MOUSEMOVE Or QS_HOTKEY Or QS_KEY)
	
	Private Declare Function CreateWaitableTimer Lib "kernel32"  Alias "CreateWaitableTimerA"(ByVal lpSemaphoreAttributes As Integer, ByVal bManualReset As Integer, ByVal lpName As String) As Integer
	
	Private Declare Function OpenWaitableTimer Lib "kernel32"  Alias "OpenWaitableTimerA"(ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Integer, ByVal lpName As String) As Integer
	
	'UPGRADE_WARNING: Structure FILETIME may require marshalling attributes to be passed as an argument in this Declare statement. Click for more: 'ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?keyword="C429C3A5-5D47-4CD9-8F51-74A1616405DC"'
	Private Declare Function SetWaitableTimer Lib "kernel32" (ByVal hTimer As Integer, ByRef lpDueTime As FILETIME, ByVal lPeriod As Integer, ByVal pfnCompletionRoutine As Integer, ByVal lpArgToCompletionRoutine As Integer, ByVal fResume As Integer) As Integer
	
	Private Declare Function CancelWaitableTimer Lib "kernel32" (ByVal hTimer As Integer) As Object
	
	Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer
	
	Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Integer, ByVal dwMilliseconds As Integer) As Integer
	
	Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Integer, ByRef pHandles As Integer, ByVal fWaitAll As Integer, ByVal dwMilliseconds As Integer, ByVal dwWakeMask As Integer) As Integer
	
	Public Sub SecondsToWait(ByRef lNumberOfSeconds As Integer)
		Dim ft As FILETIME
		Dim lBusy As Integer
		Dim lRet As Integer
		Dim dblDelay As Double
		Dim dblDelayLow As Double
		Dim dblUnits As Double
		Dim hTimer As Integer
		
		'UPGRADE_WARNING: App property App.EXEName has a new behavior. Click for more: 'ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?keyword="6BA9B8D2-2A32-4B6E-8D36-44949974A5B4"'
		hTimer = CreateWaitableTimer(0, True, My.Application.Info.AssemblyName & "Timer")
		
		If Err.LastDllError = ERROR_ALREADY_EXISTS Then
			' If the timer already exists, it does not hurt to open it
			' as long as the person who is trying to open it has the
			' proper access rights.
		Else
			ft.dwLowDateTime = -1
			ft.dwHighDateTime = -1
			lRet = SetWaitableTimer(hTimer, ft, 0, 0, 0, 0)
		End If
		
		' Convert the Units to nanoseconds.
		dblUnits = CDbl(&H10000) * CDbl(&H10000)
		If lNumberOfSeconds > 0 Then
			dblDelay = CDbl(lNumberOfSeconds) * 1000 * 10000
		Else
			dblDelay = 100000
		End If
		
		' By setting the high/low time to a negative number, it tells
		' the Wait (in SetWaitableTimer) to use an offset time as
		' opposed to a hardcoded time. If it were positive, it would
		' try to convert the value to GMT.
		ft.dwHighDateTime = -CInt(dblDelay / dblUnits) - 1
		dblDelayLow = -dblUnits * (dblDelay / dblUnits - Fix(dblDelay / dblUnits))
		
		If dblDelayLow < CDbl(&H80000000) Then
			' &H80000000 is MAX_LONG, so you are just making sure
			' that you don't overflow when you try to stick it into
			' the FILETIME structure.
			dblDelayLow = dblUnits + dblDelayLow
			ft.dwHighDateTime = ft.dwHighDateTime + 1
		End If
		
		ft.dwLowDateTime = CInt(dblDelayLow)
		lRet = SetWaitableTimer(hTimer, ft, 0, 0, 0, False)
		
		Do 
			' QS_ALLINPUT means that MsgWaitForMultipleObjects will
			' return every time the thread in which it is running gets
			' a message. If you wanted to handle messages in here you could,
			' but by calling Doevents you are letting DefWindowProc
			' do its normal windows message handling---Like DDE, etc.
			lBusy = MsgWaitForMultipleObjects(1, hTimer, False, INFINITE, QS_ALLINPUT)
			System.Windows.Forms.Application.DoEvents()
		Loop Until lBusy = WAIT_OBJECT_0
		
		' Close the handles when you are done with them.
		CloseHandle(hTimer)
		
	End Sub
End Module


#2 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 04 June 2011 - 06:23 PM

You're right to think that a sleep method is built in, after all it's the new fangled .NET framework! :p

Some googlling turns up a sleep() method in the Threading namespace:

System.Threading.Thread.Sleep(1000) 'sleep 1 seconds


#3 drew010

drew010
  • 7 posts

Posted 04 June 2011 - 10:36 PM

If you're trying to sleep in your main thread and don't want to hang up the ui, you can use this

    ' add to your global module or form code
    Public Sub SafeSleep(ByVal millisecondsTimeout As Integer)
        Dim start As DateTime = DateTime.Now

        Do
            Application.DoEvents()
            System.Threading.Thread.Sleep(5)

            If (DateTime.Now - start).TotalMilliseconds > millisecondsTimeout Then Return
        Loop
    End Sub

    ' example from main thread
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        SafeSleep(10000)
        MessageBox.Show("wake")
    End Sub


or in C#

        public void SafeSleep(int millisecondsTimeout)
        {
            for (DateTime start = DateTime.Now; true; ) {
                Application.DoEvents();
                System.Threading.Thread.Sleep(5);
                if ((DateTime.Now - start).TotalMilliseconds > millisecondsTimeout) return;
            }
        }


#4 Melchoire

Melchoire
  • 5284 posts


Users Awards

Posted 05 June 2011 - 12:50 AM

If you're trying to sleep in your main thread and don't want to hang up the ui, you can use this

    ' add to your global module or form code
    Public Sub SafeSleep(ByVal millisecondsTimeout As Integer)
        Dim start As DateTime = DateTime.Now

        Do
            Application.DoEvents()
            System.Threading.Thread.Sleep(5)

            If (DateTime.Now - start).TotalMilliseconds > millisecondsTimeout Then Return
        Loop
    End Sub

    ' example from main thread
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        SafeSleep(10000)
        MessageBox.Show("wake")
    End Sub


or in C#

        public void SafeSleep(int millisecondsTimeout)
        {
            for (DateTime start = DateTime.Now; true; ) {
                Application.DoEvents();
                System.Threading.Thread.Sleep(5);
                if ((DateTime.Now - start).TotalMilliseconds > millisecondsTimeout) return;
            }
        }


Oh yeah! RegEx(a user here) was having the same problem a day or two ago. His gtk gui would hang during a loop and he had to resort to threads to fix it.

#5 drew010

drew010
  • 7 posts

Posted 05 June 2011 - 10:55 AM

Ah yeah I saw that thread he made. Unfortunately, any type of time consuming operation, even if its less than a second can/will hang up the main thread if that's where its run from. The longer it takes the more noticeable and unresponsive the gui becomes. So for that reason threading is a must, but also adds a lot more complexity.

I don't know if Python (I think that was what he was using) has any sort of Application.DoEvents() functionality, but that basically tells the main loop to process any events (e.g. paint the ui, or respond to user input) that may be queued up.

#6 Zeeknon

Zeeknon
  • 63 posts

Posted 05 July 2011 - 02:37 PM

A better C# version:
public void SafeSleep(int millisecondsTimeout)
{
	for (DateTime start = DateTime.Now; (DateTime.Now - start).TotalMilliseconds <= millisecondsTimeout; )
	{
		Application.DoEvents();
		System.Threading.Thread.Sleep(5);
	}
}



0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users