Visual Basic 6 - Any libraries exist to allow implementation of multithreading? - vb6

Are there any libraries that I can import into a VB6 project to enable multi threaded support? I know that there are examples using the Windows API but I was wondering if there are any libraries that already exist that I can just import into my project to allow this functionality in VB6. I have inherited a very complex application in VB6 that one of its tasks is to control a multi-million dollar robotic arm. I need to take this application and make some changes that would benefit greatly if I can add multithreaded support. Converting this application to .NET would be an enormous undertaking (to us a good analogy would be the equivalent of a manned mission to Mars next year). The application includes several custom libraries that perform complex scientific calculations and data analysis. The code has been customized to deliver an impressive processing speed (this is VB6). It would take an enormous amount of resources to migrate to a .NET platform. Executive management indicates it could easily be 8 years before the system is upgraded. I would appreciate any responses.
Note: I did a search before submitting this question and I did see a similar question being asked but the answer to the question directs to the Windows API directly. My question is a bit different. I am asking about libraries that already include this functionality that I can use in this project. That is, libraries that have already done all this work of using the API.

There's no library for multithreading that I know of. But asynchronous processing does not necessarily require threads. Desaware have StateCoder, a library for state machines which helps with multi-tasking without multi-threading. A bit like the Aysnc CTP.
Alternatively here is a pretty standard scheme for asynchronous background processing in VB6. (For instance it's in Dan Appleman's book and Microsoft's VB6 samples.) You create a separate ActiveX EXE to do the work: that way the work is automatically on another thread, in a separate process (which means you don't have to worry about variables being trampled).
The VB6 ActiveX EXE object should expose an event CheckQuitDoStuff(). This takes a ByRef Boolean called Quit.
The client calls StartDoStuff in the ActiveX EXE object. This routine starts a Timer on a hidden form and immediately returns. This unblocks the calling thread. The Timer interval is very short so the Timer event fires quickly.
The Timer event handler disables the Timer, and then calls back into the ActiveX object DoStuff method. This begins the lengthy processing.
Periodically the DoStuff method raises the CheckQuitDoStuff event. The client's event handler checks the special flag and sets Quit True if it's necessary to abort. Then DoStuff aborts the calculation and returns early if Quit is True.
This scheme means that the client doesn't actually need to be multi-threaded, since the calling thread doesn't block while "DoStuff" is happening. The tricky part is making sure that DoStuff raises the events at appropriate intervals - too long, and you can't quit when you want to: too short, and you are slowing down DoStuff unecessarily. Also, when DoStuff exits, it must unload the hidden form.
If DoStuff does actually manage to get all the stuff done before being aborted, you can raise a different event to tell the client that the job is finished.
Disclaimer: direct copy of my answer on another question

You can certainly call the windows API to implement multi threading, and its not actually that complicated. However, the simplest solution would be to expose a .net Com object and implement multi threading through .net. For complicated already existing functionality you can break the vb6 app into com libraries that can be called by by the multi threaded .net controller.
[Gui] ┬> [ .net Com Mulit thread controller] -> [Com exposed VB 6 utility]
|
└> [Com exposed VB 6 utility]

See the article Using Background Threads with Visual Basic 6 for an elegant answer to the problem.

There was a threading library made by elitevb in 2002 which is unfortunately closed now.
But there is an archive of elitevb articles on xtremevbtalk.
In the section System and devices there is a post at the bottom with the threading.dll and sample source code attached, which makes it very easy to implement threading. As far as I remember, there was the problem, that testing it in the IDE crashed the program, but there were no problems running the compiled program.
With the library, you could create threads pretty straitforward:
Dim ReadValuesThread As Thread
Private Sub Form_Load()
Set ReadValuesThread = New Thread
ReadValuesThread.StartThread "ReadValues", "None", Me
' Public Function StartThread(FunctionName As String, _
' FunctionParameter As Variant, _
' ParentObject As Object) As Long
End Sub
Private Sub ReadValues()
' do things
End Sub
Private Sub Form_Unload(Cancel As Integer)
' kill thread
ReadValuesThread.EndThread
End Sub

Related

Questions about SetWindowsHookEx() and hooking

Here is a little background information. I'm working on replacing a dll that has been used in a dll injection technique via the AppInit_DLLs registry entry. Its purpose was to be present in every process and set hooks into the GDI32.dll to gather information about printing. This is kind of a funky way to get what we want. The .dll itself is over 10 years old (written in Visual Studio 97) and we'd like to replace it with something a little less invasive than an injected dll.
It appears SetWindowsHookEx() maybe what we are looking for. I've been having some trouble with it, but I've also had some discussions with co-workers about whether or not this tree is worth barking up. Here are some questions that we could not determine:
When we hook a routine out of a dll, for example StartDoc() from GDI32.dll, do we really get a notification every time any other process uses that rotuine out of that dll? This is kind of the functionality we were getting with our injected .dll and we need the same functionality going forward.
When the hook is triggered, does the hook handling procedure run in the process space of the process that initiated the actual call, or in the process space of the process that set up the hook? My opinion is that it has to run in the process space of the process that called the routine. For example, if a program calls StartDoc() from GDI32.dll, it will have the hook handling procedure code "injected" into its space and executed. Otherwise, there would have to be some inter-process communication that automatically gets set up between the calling process and the process that set up the hook, and I just don't see that as being the case. Also, its kind of necessary that this hook handling routine run in the process space of the calling process since one of the things it needs to know is the name of that calling process, and I'm not sure on how to get that information if it wasn't actually running in that process.
If the hook handling routine is written using the .NET managed environment, will it break when getting hooked into a process not utilizing the .NET managed environment? We'd really like to move away from C++ here and use C#, but what would happen if we our hook gets called from a process that isn't managed? As stated before, I think that our hook handling procedure will run in the process that originally called the routine that was hooked. But if this is true, than I would think that we'd run into trouble if this process was not using the .NET run time environment, but the incoming hooked handling code is.
Yes.
Generally, it's the former: it executes in the context of the process whose event it is hooking.
After a successful call to SetWindowsHookEx, the operating system automatically injects the hook DLL (the one that contains the callback function) into the address space of all target processes that meet the requirements for the specified hook type. (Of course, the hooking code is not necessarily injected immediately.)
The exception to this general rule are the low-level keyboard and mouse hooks (WH_LL_KEYBOARD and WH_LL_MOUSE). Since those hook types are not injected into the client processes, the callback is called in the same thread that originally called SetWindowsHookEx.
That last point is important to keep in mind to answer your third question. Because the low-level keyboard and mouse hooks are the only two global hooks that do not require DLL injection, they are also the only two types of hooks that can be written in managed .NET code.
For the other hook types, your concerns expressed in the question are precisely correct. You would need to write these hook DLLs in C or C++. Of course, the rest of your application's pieces could still be written in a managed language. The only thing that matters is the hook DLL.
You might consider looking into either Microsoft Detours or EasyHook.

Why call CoFreeUnusedLibraries() before OleUninitialize()?

While trying to resolve process hanging on CoUninitialize() I came upon a piece of code shared by many of our projects. When a program is going to quit it first calls CoFreeUnusedLibraries(), then immediately OleUninitialize().
While the effect of OleUninitialize() is quite clear I can't find why one would want to call CoFreeUnusedLibraries() before calling OleUnitialize(). What might be the use of this call at this specific point?
CoFreeUnusedLibraries() will trigger a call to the DllCanUnloadNow for each in-process COM DLL that exports this function. Not sure about threading issues or out-of-process COM components as it relates to this API.
Presumably, someone who wrote the code that calls DllCanUnloadNow before OleUnitialize was attempting to reduce working set and ensure cleanup.
I don't think there's much value in calling CoFreeUnusedLibraries right before application shutdown (the DLLs will get unloaded anyway).
My experience is that calling CoFreeUnusedLibraries results in crashes and hangs in 3rd party COM DLLs that never had their DllCallUnloadNow implementation tested before release. (Because not too many apps call this function).
You didn't provide a call stack or hint as to where the hang was occurring (did you break into a debugger to see what DLL is at the top of the stack?). My guess is that you can likely take this call out if you can't fix the offending DLL.
Docs indicate that
This function is provided for
compatibility with 16-bit Windows.
Hmmm...
Have you seen this problem report? This call seems redundant to me - maybe this leaves one or more DLLs in a state where OleUninitialize does not work properly - waiting for some state change due to the earlier call. However this does allude to the need to wait a while between calls...
CoFreeUnusedLibraries does not
immediately release DLLs that have no
active object. There is a ten minute
delay for multithreaded apartments
(MTAs) and neutral apartments (NAs).
For single-threaded apartments (STAs),
there is no delay. The ten minute
delay for CoFreeUnusedLibraries is to
avoid multithread race conditions
caused by unloading a component DLL.
There are also comments elsewhere re a 6 -minute closedown timeout when using DCOM - is that applicable to you?

How to avoid the White Blank Screen?

Using VB6
At the time of executing, it showing white blank screen while it was working with database,
How to avoid the white blank screen in VB6.
WHITE BLANK SCREEN MEANS HANGING (WHEN IT WAS WORKING WITH DATABASE).
How to avoid that?
I assume you mean that the GUI won't redraw itself when executing a long-running operation. (Shouldn't actually be that visible starting with Vista, but I digress).
If your long-running operation is composed of several steps or tight loops, then you can sprinkle a call to DoEvents every once in a while to cause the form to remain somewhat responsive even when doing work.
Another option would be to migrate your long-running work into a separate thread but last I looked this wasn't exactly trivial or easily possible in VB6.
You should work with data base in separate thread, and any time-consuming operation should be run in a separate thread too. In this case your user interface won't be frozen.
I posted this as an answer to another question, but the pattern applies here as well:
VB6, on its own, is single threaded. However, you can make it somewhat multithreaded via the use of ActiveX EXE that run in their own process, yet still are tethered to the original VB6-created EXE.
What I've used in the past is the Timer object in conjunction with an ActiveX EXE. This approach will give you an ability to localize all the downloading logic in one place, control it like you control a regular object and have it run in a separate EXE, thus by default making it multi-threaded.
So the way this works is like so:
You call the LongRunningOperation method on the ActiveX EXE object
In the LongRunningOperation method, you instantiate the Timer and have it kick off almost immediately.
You get out of the LongRunningOperation method, thus giving control back to the entity that called it.
Then you communicate back to the main app via Events (e.g. LongRunningOperationProgress or LongRunningOperationComplete, etc...)
I recommend the Timer object from Karl Petersen.
This is actually the same problem as your "How to exit the program immediately" question. In both cases, the problem is that your database operation is blocking the VB6 program from executing.
The answer you accepted for the other question - use ADO to carry out the operations asynchronously - will also solve this blank screen problem.
Or if you prefer, follow one of my suggestions in my answer to your other question and use a .NET background worker component through Interop like this.
Or follow my other suggestion and delegate the database work to an ActiveX exe like this. EDIT AngryHacker's nice answer to this question uses this method.
Your first instinct should be to put your resource-intensive operations in a separate thread. This is a bit difficult in VB6, although possible (but not recommended) if you invoke the native CreateThread API.
You can also migrate to VB.NET, and use Thread objects natively.

How do I dispose of a VB6 COM object i'm using via an interop assembly from IronPython?

I'm using a third party COM component by means of a .NET interop assembly in IronPython, as seen here: How can I use a VB6 COM 'reference' in IronPython?
My experience in this area has been great, I'm very impressed by the amount of stuff that works seamlessly... except one thing.
The 3rd party COM component uses Microsoft's DAO library (not sure what version) to work with a database created by yet another 3rd party application. The problem is that this database file gets recreated during my program's runtime, and I'd like to 'de-initialize' this COM object. In particular, running my code (which accesses the COM component's attributes, calls it's methods, etc) works fine the first time I run my routine. The next time, however, the third party COM component displays a messagebox saying the database (MSAccess 95 .mdb file) is already in use, and offers an option to retry. Pressing retry works reliably, so the main issue is that this dialog comes up.
So, my theory is the COM component is leaking handles to the db, and I can't find a 'cleanup' method to call. I've tried .Dispose(), but that has not worked.
My last resort is making the code that calls to the COM object a separate IronPython process that interacts with my main process via std in/out as the role the COM object serves is more of a 'give me all this information right now' use case instead of a continually required dependency.
I am hoping to avoid that scenario, and since i'm not to familiar with COM (or really, .NET for that matter) I can only hope i'm missing an obvious .Dispose method or the like.
If there's no clean way, can I forcibly unload the assembly from my process, analogous to repeated FreeLibrary calls in native code? (I guarantee that my code won't be using the object anymore, so I shouldn't need to worry about missing refs on my part leading to a memory protection error)
EDIT:
I wasn't able to solve this, so I went the out of process method and let windows clean up when my child batch process shuts down.
Not sure about IronPython, but when working with COM in C#, you need to call Marshal.ReleaseComObject after use to ensure the reference count is properly decremented.
Otherwise you will leak...Dispose() does not do this for you.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject.aspx

Multithreading in visual basic 6.0

How to implement multi-threading in visual basic 6.0. It would be great if someone can give an example.
VB6 is not a really good environment for multi-threaded applications. There is no out-of-the-box support, you need to delve into standard WinAPI functions. Take a look at this article, which provides quite a comprehensive sample:
http://www.freevbcode.com/ShowCode.Asp?ID=1287
On several projects I have implemented asynchronous processing in VB6 using multiple processes. Basically having a worker thread within an active exe project that is separate from the main process. The worker exe can then be passed whatever data it needs and started, raising back an event to say it's finished or there is data for the main process.
It's a more resource hungry (an extra process rather than a thread) but VB6 is running in a single threaded apartment and doesn't have any built in support for starting new threads.
If you really need to have multiple threads within one process I'd suggest looking at using .net or VC6 rather than VB6.
If the problem that you are trying to solve is a long calculation and you want to keep the UI responsive, then one possibility is to frequently call the DoEvents function within your long calculation. This way, your program can process any Windows messages, and thus the UI will respond to user commands. You can also set up a Cancel button to signal your process that it needs to end.
If you do this, then you will need to be careful to disable any controls that could cause a problem, such as running the long process a second time after it has started.
Create "Active X" controls to manage your code. Each control has its own thread. You can stack multiple controls doing the same thing, or have individual controls doing unique things.
EG, You make one to download a file from the net. Add ten controls and you have ten individual threaded downloads running, independent of the thread which the actual program is running. Essentially, they are all just interactive, windows, controlled by an instanced mini-dll program.
Can't get any easier than that. You can throttle them, turn them on and off, as well as create more, or remove them, as needed. (Indexing just like any other of the "Objects", on a form. Which are all just active-x controls, which are simply managed by the vb-runtime dlls.)
You can use the Interop Forms Toolkit 2.0 for multithreading in VB6. The Toolkit
allows you to take advantage of .NET features without being forced onto an upgrade pat. Thus you can also use .NET User Controls as ActiveX controls in VB6.

Resources