Archived Forum Post

Index of archived forum posts

Question:

VB6 Async Callbacks?

Dec 06 '16 at 09:56

I’m in the process of writing an application using VB6 implementing the Socket object via ActiveX using WithEvents and hope you can point me in the right direction.

First up is whether there’s an event on a socket that has been created from a listening socket using AcceptNextConnectionAsync, which fires when a client disconnects. At the moment I’m polling the sockets but if there’s a way to get that via an event that would seem to be more efficient. Looking at the documentation it says that KeepAlive is enabled by default, so would this indicate that there is some mechanism within the Socket already to determine if the connection is closed and so fire an event? If there isn’t already an event to do this, could it be considered for a future version?

The other issue I’m having is that I’m trying to build both server and client applications using sockets, and using async methods as neither side can be blocking. I’m occassionally getting crashes – oddly not in the VB6 IDE but in the compiled applications. I’m making sure I’m not doing any UI updates in the async code and am instead setting properties on classes that are shared between the UI and the socket handling code. The examples that you have available for VB6 on example-code.com are all based on a single chunk of code that is run sequentially, do you have any examples that show how to use event driven asynchronous calls in VB6?


Answer

The short answer is that because async methods run in a background thread, it's not possible for them to make a callback into VB6 code because it's from the background thread, and because of the architecture of COM/ActiveX + VB6. It may be possible in other COM/ActiveX environments (which remains to be seen), so the TaskCompleted method exists, but it can never be used in VB6. Therefore, the only solution is to setup a timer event that periodically checks for task completion. This is demonstrated in this project here on GitHub -- https://github.com/chilkatsoft/VB6-Accept-Connection-Async

Here are some drawings to understand a synchronous and asynchronous calls better:

A Synchronous Method Call w/ Callback

Synchronous Method Call with Callback

A VB6 program is event driven. When it begins, there is a main UI thread that runs an event loop, which is not your application code. When something happens, it makes a call to a subroutine or function in your VB6 app. I'll walk through the steps in a synchronous method call with a callback:

  1. Something occurred, and the UI thread makes a call to a subroutine/function in your VB6 app.
  2. Your subroutine makes a call to a Chilkat method. The thread of execution is now within native code inside the Chilkat DLL.
  3. Chilkat makes a callback into your VB6 callback function. The thread of execution is now inside your VB6 code (in your callback function).
  4. Your VB6 callback function returns. The thread of execution continues on within the Chilkat native code.
  5. Eventually, and possibly after many callbacks into your VB6 code, the Chilkat method returns. The thread of execution is now at the statement in your VB6 code following the call to the Chilkat method, and it continues onward.
  6. Your VB6 subroutine returns, and the thread of execution is back inside the UI loop. (The UI loop handles UI updates. Thus... if your VB6 subroutine takes a long time to return control back to the UI loop, your UI freezes.)

An Asychronous Method Call

Asynchronous Method Call

Here's what happens in an asynchronous method call:

  1. Something happened, so the UI thread calls your VB6 subroutine/function.
  2. Your VB6 function calls an asynchronous Chilkat method, you get a ChilkatTask object, and then you call task.Run().
  3. The task.Run method starts a new thread of execution. This is the background thread, and it will make the call to the desired Chilkat method with the arguments originally passed to it and packaged up in the Task object.
  4. The Chilkat method is called on the background thread (not the UI thread).
  5. Return immediately after starting the background thread.
  6. Return control to the VB6 app. The UI thread of execution is now at the statement following the call to Task.Run.
  7. The VB6 function returns control back to the UI main event loop. Note: The UI thread of execution is 1 --> 2 --> 3 --> 5 --> 6 --> 7.
  8. Eventually the Chilkat method running in the background thread returns, and the background thread exits. The code in the background thread, which is native code contained in the DLL, cannot make a call back to VB6 code because of COM/VB6 architectural requirements (remember, VB6 was released back in 1998, almost 20 years ago..) A callback that crosses a COM boundary, coming from native code, can only be on the UI (main) thread. It cannot be from some other mysterious background thread running native code. That's the reason for the crash.