This demonstrates, how one can implement robust, asynchronous Workers in VB6 -
by using a (cross-process) SharedMemory-approach (which is one of the common IPC-mechanisms).

For convenient usage, the more complex stuff (the MemoryMapping- as well as the CreateProcess-APIs),
are encapsulated in two generic "Drop-In"-Classes (cIPCMaster and cIPCWorker).

Those Classes can be used either "split-up" (in separate VB6-Projects for Master and Worker) -
but also "all-in-one" (when the same Project - or Process - is used to act as Master and Worker both).

The latter case (using an "all-in-one"-Project), is the most convenient for developing/testing.
Here the Worker-Process is "shelled" against the same ExeName as the Main- (or Master-) Project,
using a simple "forking" in Sub Main() ... (as shown below from the content of Demo1 modMain.bas):
Option Explicit

Sub Main() 'Process-Startup-Forking
  If Len(Trim$(Command$)) = 0 Then 'no Cmd-Arg was passed (it was started as the Master-Process)
    fMaster.Show '... so we simply startup the Main-Form
  Else 'it was started as a WorkerProcess (pass the CommandLine-Arg into the WorkerRoutine)
    EnterWorkerLoop New cIPCWorker, Trim$(Command$)
  End If
End Sub
Above, the blue-colored call represents the "master-fork-path" to your normal "GUI-Project-Code"
(entered when no Commandline-Param was passed to your Executable).

And as the magenta-colored routine-name (in the "worker-fork-path") suggests, the upstarting worker is not fired up
like in an old "classic WebServer-CGI-call" (where the Process exits, after only a single Job was performed).

Instead the current mechanism is implemented in a way, that the WorkerProcess is fired up once -
and then enters an "IDLE-loop" (waiting for Jobs, provided by the Master-Process later).
This way one of the disadvantages of MultiProcessing (the higher Startup-Costs, compared to MultiThreading) is avoided.

The advantages of doing MultiProcessing instead of threading are:
- no typelibs, no extra-Dlls are needed
- in the IDE (after compiling the same Project), the asynchronous workers will behave the same way as in the compiled binary
- a hard terminate of a worker is possible in a stable and "residue-free" manner (though graceful termination-support is of course built-in)
- the communication between Master and Worker(s) happens in an absolute "non-blocking" way

To explain the last point above a bit more... "non-blocking" means, that neither Post- or SendMessage-calls are involved
(as in VB6-OleBased-Communications between "threaded Apartments", where Events, raised from the Workers will block the Main-Thread) -
nor are there other mechanisms in play like Mutexes or CriticalSections, which are normally used in conjunction with shared memory...

Instead the Demo shows, how "state-machine-based" communication (using the shared mem-area) can be implemented.

The approach is extremely robust, completely IDE- and crash-safe, and "cleans up after itself" under any circumstances:
- upstarted Worker-Processes will automatically close, when the Master-Class goes out of scope
- you can even use the IDE-stop-button, whilst asynchronous workers are "deep within a Job" (worker-processes will autoclose nevertheless)

There is also not a single thing, which is "forbidden to use" in the workers (like in many of the threading-approaches for VB6)...

The Zip below comes with 3 Demo-Folders (a simple one to get up to speed - and two "medium-difficult" ones for MandelBrot-rendering).

Ok, here is, what the MandelBrot-Demos will produce (using two Workers, independent from the Main-App):

And here's the Demo-Zip:

Have fun...