How to avoid the White Blank Screen? - vb6

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.

Related

What external events trigger TCustomForm.RecreateWnd?

A particular VCL application is crashing, rarely, and apparently spontaneously, and I strongly suspect it is because the main form's window handle is being recreated in response to some external trigger from the operating system.
I do know for certain that if the main form's window handle were to change then trouble would certainly follow (because background threads post messages to this handle).
But I would like to know how to reproduce this bug before I set about making code changes intended to fix it, if only so I can confirm that this is a plausible cause of the crashes, that the symptoms match those reported, and that the code changes do indeed fix it. (Also if there is no way the OS can trigger a call to TCustomForm.RecreateWnd I need to find some other explanation for the crashes. Yes, I could post CM_RECREATEWND to the main form but that just begs the question.)
(I am aware that the correct way to post messages from background threads to the VCL thread is to create a hidden message window purely for this purpose using AllocateHWnd - that is my proposed fix.)
I have seen a similar error with a descendent of TCustomListView where RecreateWnd was being triggered by changing the desktop style from Windows XP to Windows Classic. But I haven't been able yet to find such a method to trigger RecreateWnd on a TCustomForm. (I've spent a considerable amount of time reading through the VCL code and I must be missing something.)
Summary:
Under what stimuli from the operating system does the VCL end up calling RecreateWnd on a TCustomForm? (I'm especially interested in Windows 7 if that makes a difference.)
If there is such a stimulus from the OS, I can prove whether it causes the observed symptoms. If there definitively isn't then I can prove window recreation isn't the cause.
Windows will destroy your form if it is owned or parented to another window that is being destroyed.
That will cause your form's handle to be destroyed.
But since your form is still around, Delphi will re-create the handle the next time.

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 increase the startup speed of the delphi app?

What do you do to increase startup speed (or to decrease startup time) of your Delphi app?
Other than application specific, is there a standard trick that always works?
Note: I'm not talking about fast algorithms or the likes. Only the performance increase at startup, in terms of speed.
In the project options, don't auto-create all of your forms up front. Create and free them as needed.
Try doing as little as possible in your main form's OnCreate event. Rather move some initialization to a different method and do it once the form is shown to the user. An indicator that the app is busy with a busy mouse cursor goes a long way.
Experiments done shows that if you take the exact same application and simply add a startup notification to it, users actually perceive that app as starting up faster!
Other than that you can do the usual things like exclude debug information and enable optimization in the compiler.
On top of that, don't auto create all your forms. Create them dynamically as you need them.
Well, as Argalatyr suggested I change my comment to a separate answer:
As an extension to the "don't auto create forms" answer (which will be quite effective by itself) I suggest to delay opening connections to databases, internet, COM servers and any peripheral device until you need it first.
Three things happen before your form is shown:
All 'initialization' blocks in all units are executed in "first seen" order.
All auto-created forms are created (loaded from DFM files and their OnCreate handler is called)
You main form is displayed (OnShow and OnActivate are called).
As other have pointed out, you should auto-create only small number of forms (especially if they are complicated forms with lots of component) and should not put lengthy processing in OnCreate events of those forms. If, by chance, your main form is very complicated, you should redesign it. One possibility is to split main form into multiple frames which are loaded on demand.
It's also possible that one of the initialization blocks is taking some time to execute. To verify, put a breakpoint on the first line of your program (main 'begin..end' block in the .dpr file) and start the program. All initialization block will be executed and then the breakpoint will stop the execution.
In a similar way you can step (F8) over the main program - you'll see how long it takes for each auto-created form to be created.
Display a splash screen, so people won't notice the long startup times :).
Fastest code - it's the code, that never runs. Quite obvious, really ;)
Deployment of the application can (and usually does!) happen in ways the developer may not have considered. In my experience this generates more performance issues than anyone would want.
A common bottleneck is file access - a configuration file, ini file that is required to launch the application can perform well on a developer machine, but perform abysmally in different deployment situations. Similarly, application logging can impede performance - whether for file access reasons or log file growth.
What I see so often are rich-client applications deployed in a Citrix environment, or on a shared network drive, where the infrastructure team decides that user temporary files or personal files be stored in a location that the application finds issues with, and this leads to performance or stability issues.
Another issue I often see affecting application performance is the method used to import and export data to files. Commonly in Delphi business applications I see export functions that work off DataSets - iterating and writing to file. Consider the method used to write to file, consider memory available, consider that the 'folder' being written to/read from may be local to the machine, or it may be on a remote server.
A developer may argue that these are installation issues, outside the scope of their concern. I usually see many cycles of developer analysis on this sort of issue before it is identified as an 'infrastructure issue'.
First thing to do is to clear auto
created forms list (look for Project
Options). Create forms on the fly
when needed, especially if the
application uses database connection
(datamodule) or forms that include
heavy use of controls.
Consider using form inheritance also
to decrease exe size (resource usage is mimized)
Decrease number of forms and merge similar or related functionality into single form
Put long running tasks (open database connections, connect to app server, etc) that have to be performed on startup in a thread. Any functionality that depends on these tasks are disabled until the thread is done.
It's a bit of a cheat, though. The main form comes up right away, but you're only giving the appearance of faster startup time.
Compress your executable and any dlls using something like ASPack or UPX. Decompression time is more than made up for by faster load time.
UPX was used as an example of how to load FireFox faster.
Note that there are downsides to exe compression.
This is just for the IDE, but Chris Hesick made a blog posting about increasing startup performance under the debugger.

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