The Price of Simplicity

Top  Previous  Next

What have we got?

The creators of Xbase++ like to stress "simplicity" as the main idea behind the Xbase Part architecture. Let's just take a closer look at what this architecture is like: Xbase Parts are the Xbase++ representations of windows. When you start an Xbase++ program an extra thread is created which is the owner of all windows that correspond to the Xbase++ parts which are created by your program, no matter in which of your application threads they are created. Let's call this thread the Xbase++ GUI thread. On the API level, every window is connected with a thread and the messages sent (from the O/S or another application) to a window are sent to the thread it is connected with. So if you are programming "native Windows", you have to put a message loop (sometimes referred to as a "message pump") in every thread that owns windows. Hence there must be a message handler in the Xbase++ GUI thread. Since the abstract Xbase++ Parts are not created in the GUI thread but in application threads, the GUI thread must forward the messages received in the native window to the application thread that created the corresponding Xbase Part. This forwarding happens asynchronously, that means the message is just put in the Xbase++ message queue of an application thread. An instant response to that message is not possible. Another problem with this message forwarding is filtering: Not all messages are forwarded by the GUI thread message handler. That means not all messages sent by other applications or the O/S arrive in your application threads.

What does that mean?

Sending messages between windows is one of the ways to exchange data between different applications or the O/S and the running applications. An example for data exchange between applications is DDE. But since the messages used for DDE are not forwarded to your application threads, you cannot do DDE when you use the Xbase++ GUI.

 

The O/S sends notifications to all applications, when things happen that might affect them, like changed settings. These notifications never make it to your message handler.

 

Normally you can register for change notifications. That means for example you tell the O/S to send you a notification if a file is modified. With the Xbase++ GUI the sent notification message will be dumped by the GUI thread message handler because it doesn't know it should be forwarded.

 

Some messages sent to windows are actually synchronous calls that must return values. For example the WM_QUERYOPEN message is sent to a minimized window when it is about to be restored. If the window procedure returns TRUE, the window will be restored. If the window procedure returns FALSE, the window will remain minimized. This interaction is not possible with the Xbase++ way of message handling because the message is forwarded asynchronously.

 

Windows' message handling is actually much more complicated than "put every message in the thread's queue and leave the rest to the message handler". The first big problem is the difference between message sending and message posting Windows supports. If you send a message, it is not put in the thread message queue, instead the window procedure is called directly. That cannot be replicated with the Xbase++ way of message handling. Message posting means putting events in the queue. That's what can be approximated with Xbase++ message handling. Unfortunately, some messages receive some special treatment on the Windows side. For example the WM_PAINT message is never put in the queue twice. When it is sent (or better posted) to a Window, the queue is checked if a paint message is already on its way. If one is found it is removed from the queue and a new WM_PAINT message is added at the end of the queue. When a WM_PAINT message has already made it through the O/S thread message queue and is just on its way to the Xbase++ application message handler, it cannot be stopped anymore. That's one of the reasons why the Xbase++ GUI appears so flickery.

 

Actually Windows message handling is even more complex than discussed in the previous paragraph. If you want to know more, turn to Jeffrey Richter's book.

The Bottom line

The basic architecture of the Xbase++ GUI is indeed very simple: All Windows are created in one thread and all messages are forwarded to the application threads through message queues. Unfortunately that comes with side effects that isolate your application from the "talkative" applications around your's. It even deprives you of the chance to receive notifications the O/S wants to send to you or from requests the O/S wants to be answered by your application.

 

The closer you look at Windows and its architecture, the more you get the impression that it's about cooperation and collaboration between applications, processes and services. Windows' message handling system is a lot more advanced than just a simple message queue for every thread. The O/S is taking care that your application will still be responsive, even if it is waiting for responses from other applications or the O/S. All these efforts are in vain when you're using the Xbase++ GUI and its message handling. Since all windows corresponding to your Xbase++ Parts are created in a thread you don't have access to, all these mechanisms are out of your reach.

 

If you choose to continue using the Xbase++ GUI and its Parts, Cockpit will help you with its Xbase++ Part subclassing capability. It will give you (almost) direct access to all the messages sent to your application. See Chapter 5 - Xbase Part Integration for details. Or you can choose to use the Windows way of handling messages throughout your application.

Charles Petzold puts it like this:

"Obviously, there's hardly any one right way to write applications for Windows. More than anything else, the nature of the application itself should probably dictate the tools. But learning the Windows API gives you vital insights into the workings of Windows that are essential regardless of what you end up using to actually do the coding. Windows is a complex system; putting a programming layer on top of the API doesn't eliminate the complexityit merely hides it. Sooner or later that complexity is going to jump out and bite you in the leg. Knowing the API gives you a better chance at recovery."

 

"Any software layer on top of the native Windows API necessarily restricts you to a subset of full functionality. You might find, for example, that Visual Basic is ideal for your application except that it doesn't allow you to do one or two essential chores. In that case, you'll have to use native API calls. The API defines the universe in which we as Windows programmers exist. No approach can be more powerful or versatile than using this API directly."