OK Here is an Asynchronous way to do it. I did not put the Class in a DLL or EXE. Just inside the project which may be the best option for you after all
I wrote alot of this stuff years ago, but I just tested the project and it all works. Add the MODs, CLS, and the BAS files to your project.
VB Code:
Option Explicit
'
' frm_COM_Test.frm
'
Private WithEvents m_COMPort As cls_COMPort
'
Private Sub Form_Load()
Set m_COMPort = New cls_COMPort
End Sub
Private Sub cmd_OpenPort_Click()
If (Not m_COMPort.OpenPort) Then
Me.Caption = "Open Port Unsuccessful"
Else
Me.Caption = "Open Port Successful"
End If
End Sub
Private Sub cmd_ClosePort_Click()
'
Dim intReturn As Integer
intReturn = m_COMPort.ClosePort
'
If Not (intReturn = 0) Then
Me.Caption = "Can't Close Port: " & intReturn
Else
Me.Caption = "Close Port Successful"
End If
'
End Sub
Private Sub cmd_OpenAsync_Click()
m_COMPort.OpenPort_Async
End Sub
Private Sub cmd_CloseAsync_Click()
m_COMPort.ClosePort_Async
End Sub
Private Sub m_COMPort_PortCloseDone(intSuccessful As Integer)
'
If Not (intSuccessful = 0) Then
Me.Caption = "Can't Close Port: " & intSuccessful
Else
Me.Caption = "Close Port Successful"
End If
'
End Sub
Private Sub m_COMPort_PortOpenDone(blnSuccessful As Boolean)
If (Not blnSuccessful) Then
Me.Caption = "Open Port Unsuccessful"
Else
Me.Caption = "Open Port Successful"
End If
End Sub
VB Code:
Option Explicit
'
' VB Module: mod_ComTest.bas
'
Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" _
(ByVal lpFileName As String, ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
'
'// This is the declaration to close the COM port
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
'
VB Code:
Option Explicit
'
' VB Module: modTimers.bas
'
Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
'
Private mcolItems As Collection
'
Public Sub AddTimer(ByRef pobjTimer As APITimer, ByVal plngInterval As Long)
I've just noticed that if I click the "Quote" button it displays all the text in the box without the line numbers - I suppose it would be easy enough to pull the text out that way - on the other hand, isn't there an 'official' way to copy code samples from the forum ?
By the way, the first line is going to give me an error isn't it ?
Private WithEvents m_COMPort As cls_COMPort
Only allowed in an Object Module
I'm doing this in a standard exe project - should I be doing it in an activex exe project ?
Standard EXE project is perfect. I always use a Main Form to host all the main WithEvents Objects. The Quote trick is the official way to do it LOLz...
Last edited by Dave Sell; Mar 19th, 2010 at 08:48 AM.
Nobody knows what software they want until after you've delivered what they originally asked for.
Don't solve problems which don't exist.
"If I had eight hours to cut down a tree, I'd spend six hours sharpening my axe." --- Abraham Lincoln (1809-1865)
Hi Dave - I just downloaded that and took a quick look at the code.
I haven't actually tested it yet with a slow port but would you just clarify for me - what you're saying is that (when I click the "Open Async" button) if the API CreateFile function hangs (ie does not return for any length of time) then the application will still remain responsive and can periodically check to see if the port opened yet.
So, in theory, one could use that sample code to do practically 'anything' on an async basis.
You said you wrote much of that code some time ago - was that its purpose ? to do api tasks asynchronously ? Where is that code, I couldn't see it in the codebank.
Hi Dave - I just downloaded that and took a quick look at the code.
I haven't actually tested it yet with a slow port but would you just clarify for me - what you're saying is that (when I click the "Open Async" button) if the API CreateFile function hangs (ie does not return for any length of time) then the application will still remain responsive and can periodically check to see if the port opened yet.
Short answer is yes, but I would not "check periodically". I would set up my own timer on the form that alerts me that n seconds have gone by and I should try again later, etc. However I don't think there is much you can do besides wait for the API call to return. Like I don't think you can cancel that call or anything, you just gotta let it run its course.
Originally Posted by IanS
So, in theory, one could use that sample code to do practically 'anything' on an async basis.
Yes!
Originally Posted by IanS
You said you wrote much of that code some time ago - was that its purpose ? to do api tasks asynchronously ? Where is that code, I couldn't see it in the codebank.
I made 2 Classes many years ago that used bits of that code; One was the CountDownTimer to be used as a non-graphical Windows Timer, and the other is clsPerformanceCounter, which can be used to measure the time it takes code to execute, and is accurate down to 300 nanoseconds.
Nobody knows what software they want until after you've delivered what they originally asked for.
Don't solve problems which don't exist.
"If I had eight hours to cut down a tree, I'd spend six hours sharpening my axe." --- Abraham Lincoln (1809-1865)
Originally Posted by IanS
So, in theory, one could use that sample code to do practically 'anything' on an async basis.
Originally Posted by Dave Sell
Yes!
This sounds interesting. I don't have a slow port ready to test so I thought of some 'other' api call I could test this with. Sleep() should do it. So I added the declaration for the API Sleep() function.
So, calling Sleep(10000) instead of CreateFile would make the application hang for 10 seconds - but, according to you, the main form should remain responsive while we're waiting for the API call to return.
But it doesn't, the call makes everything hang for 10 seconds.
Maybe Sleep isn't a good one to test this with - can you think of some other slow api call we could use to test this ?
Option Explicit
'
' VB Module: mod_ComTest.bas
'
Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" _
(ByVal lpFileName As String, ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
'
'// This is the declaration to close the COM port
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
and commented out the call to CreateFile and called Sleep() instead because we know that, in this case, the api call with take 10 seconds to return
Code:
Public Function OpenPort() As Boolean
' Instead of CreateFile, lets call Sleep(10000)
' Obviously this isn't going to open a port but we can be sure that this api call will take 10 seconds to return
Sleep (10000)
'
'
' m_lngFileHandle = CreateFile("\\.\COM1", GENERIC_READ Or GENERIC_WRITE, 0, ByVal 0&, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)
'
' If m_lngFileHandle > 0 Then
' OpenPort = True
' End If
End Function
If I click the OpenAsync button on your sample form that eventually calls OpenPort - I assumed you did that because that's the only place anywhere in your code that CreateFile() appears.
According to MSDN: here, Sleep() kills the entire thread, parental ownership not excluded.
Ergo, what you are witnessing is by design and should not in any way correlate to the CreateFile API. Sleep is intended to kill the entire thread.
I suggest you ignore this test, even though I thought it was a pretty good shot at simulating the hung-up nature of the CreateFile() API call.
I believe you cannot have a true test of the true behavior until you run it against a problematic COM port, as I originally suggested.
I will try to simulate this using CreateFile against a broken mapped network drive and see what happens.
Mentioning the word 'Thread' - I think you hit the nail on the head.
The ability to run a 'slow-to-return' CreateFile function AND remain responsive within the application while you're waiting would, in my mind, require two threads.
If we were doing this in C we'd call the CreateThread API to do the async task. Actually, in VB5 you could also call CreateThread and pass it AddressOf a function in a module to do the async task - basically running two threads.
VB6 is more thread-safe so that crashes if you try (although there are ways around it but it requires a bit more code but still possible - I may look at that route)
Without multithreading I really can't see how we can be running code in the API CreateFile function AND running code in the VB form at the same time with just one thread.
I really do appreciate your input on this - but please don't spend too much time on it because I kinda feel we're going down the wrong route.
Using Sleep for more than a few mS is an awful idea. Take a look at the WaitMessage API used in conjunction with a Timer or use this Pause sub, which is a modified of version of what's found in your library. This sub will not freeze your app. You can click buttons etc. while Pause is active.
Code:
Private Sub Pause(ByVal Delay As Single)
Delay = Timer + Delay
If Delay > 86400 Then 'more than number of seconds in a day
Delay = Delay - 86400
Do
DoEvents ' to process events.
Sleep 1 ' to not eat cpu
Loop Until Timer < 1
End If
Do
DoEvents ' to process events.
Sleep 1 ' to not eat cpu
Loop While Delay > Timer
End Sub
Private Sub Form_Load()
Pause 10 ' pause for 10 seconds
MsgBox "Time Out"
End Sub
<--- Did someone help you? Please rate their post. The little green squares make us feel really smart!
If topic has been resolved, please pull down the Thread Tools & mark it Resolved.
Is VB consuming your life, and is that a bad thing??
Using Sleep for more than a few mS is an awful idea. Take a look at the WaitMessage API used in conjunction with a Timer or use this Pause sub, which is a modified of version of what's found in your library. This sub will not freeze your app. You can click buttons etc. while Pause is active.
Ya actually IanS was intentionally freezing the app to run a test, or prove a point. I am not convinced the test is a valid comparison to the behavior of a hung-up API call to CreateFile(). I have not tried it yet.
Nobody knows what software they want until after you've delivered what they originally asked for.
Don't solve problems which don't exist.
"If I had eight hours to cut down a tree, I'd spend six hours sharpening my axe." --- Abraham Lincoln (1809-1865)
Using Sleep for more than a few mS is an awful idea.....
LOL yeah we know - you'd need to read the whole thread from the start to see what we're talking about.
Dave is trying to put some code together that would allow a VB6 app to call a 'blocking function' while still remaining responsive. By 'Blocking Function' I mean calling some API function that might take a very (very) long time to return without blocking the calling application. To test Dave's code I thought we could use the Sleep function (that will block for as long as we tell it to)
I've come to the conclusion it can only be done by actually creating another thread to make the api call - Dave is doing it in just one thread.
Well, we know that VB6 can't multi-thread but I did mention the WaitMessage API in that post. Admittedly, I'm not sure if it's applicable here.
Quote: From the KPD API Guide:
"The WaitMessage function yields control to other threads when a thread has no other messages in its message queue. The WaitMessage function suspends the thread and does not return until a new message is placed in the thread’s message queue".
<--- Did someone help you? Please rate their post. The little green squares make us feel really smart!
If topic has been resolved, please pull down the Thread Tools & mark it Resolved.
Is VB consuming your life, and is that a bad thing??
Actually it can - You can call the CreateThread API function passing it AddressOf a sub or function in a module.
That worked quite well under VB5 but crashes when you try it with VB6 - interesting thing is it's the new thread that crashes while the calling thread remains responsive That kinda feels strange seeing the error messages pop open but still have full control of your application - LOL
Anyway, it is still possible with vb6 to call the CreateThread api function. It just needs a bit more code to set it up before calling it.
Actually it can - You can call the CreateThread API function passing it AddressOf a sub or function in a module.
That worked quite well under VB5 but crashes when you try it with VB6
That's unfortunate, because if it worked under VB6 it could be used to generate DTFM codes and a host of other applications.
<--- Did someone help you? Please rate their post. The little green squares make us feel really smart!
If topic has been resolved, please pull down the Thread Tools & mark it Resolved.
Is VB consuming your life, and is that a bad thing??