| RTL referenceGlossaryTips/Tricks|FREE App/VCL|Best'O'Net|Books|Link To |
| GDI Graphics In Delphi | ||||||||||||||||||||||||
| Page 9: API Drawings | ||||||||||||||||||||||||
From the Top Things You Need To Know Next, there is a message loop. Windows works by events. When something happens, for example a mouse movement or key press, Windows sends a message to every application. If you want to respond to the event then you are responsible for handling it (though usually we ignore most messages, only handling a small subset). Remember: you program sits around waiting for messages, not being proactive. It says "is there a message yet? How about now? Still no message? Is there a message? Where's the message?" (repeat until Windows gets a message for the program to deal with).
The message loop is an infinite loop. Because you don't know when your program will end you must loop indefinitely. In each loop of your message loop you use PeekMessage or GetMessage to take a message off of the event queue. Messages are put there by Windows as and when needed. You process that message using TranslateMessage and DispatchMessage. This process continues until you receive a WM_QUIT message, at which point you break out of your message loop.
Next up is the message handler. This is a special call-back function. What's a call-back function? Well, you specify a function as a field of the "window class" record. Windows takes it and calls it when necessary. That's right: you don't call the function, but let Windows do it instead, as needed. In practise, this function gets called via a complex chain after DispatchMessage in the message loop.
So what does the message handler do? Well, it handles messages (duh ;). One of its parameters is the type of message. You use a case statement on this, using pre-defined constants such as WM_PAINT, WM_DESTROY, and so on. When you are interested in a message you write code to handle it. However, you may not be interested in handling every message so at the end the DefWndProc is called, which is the default handler. Windows has default responses to messages, and by calling DefWndProc you are saying, "I don't care about this message, do with it what you want."
There are two very cryptic parameters to the message handler function: "wParam" and "lParam". What are they? Well, it depends on the message. They are both integers, and Windows uses them for whatever information is relevant to the message. For example, with WM_SIZE (called when your window gets resized) the lParam stores the new width and height information. Windows can also be cunning and pack several parts of information into a single number, which is why you might need to use LOWORD and HIWORD. These separate out the information. You'll be told in the help files when you need to use HIWORD and LOWORD, so don't worry too much.
One other final note: the code for an API program goes in your .DPR file... you know, the one that you hardly ever touch? Well, you do now. Remember to remove the form from a new project with "remove file" because we don't want any VCL (that's the whole point of this tutorial part!).
The Process Eeeeeeeeeeeek! Don't worry, we'll go through it step by step. Once you have template code it's just a copy and paste job. Luckily, I'm giving you such template code. Let's look at each bit now, because it sounds pretty scary without code (and is almost as scary with).
1) Declare a TWndClass:
2) Fill its details out (it's a record):
The notes:
Note 1: These two mean to redraw the screen if anything happens to the width or height (movement or resize). You can also investigate CS_OWNDC. Although this structure was huge, don't be scared. Most of the stuff is copy and paste.
3) Register a new "window class", passing in your TWndClass
This one's easy:
4) and 5) Create a new window with CreateWindow (or CreateWindowEx) and STORE THE HANDLE YOU GET FROM CREATEWINDOW (important!)
This is another of those scary functions Windows does so well ;). CreateWindow allows you to create a window based on any previously registered "Window Class". That's why it was important for you to go through the nonsense above.
g_Handle here is a global variable I declared at the top of the program, of type HWND. Remember that handles are used in Windows to identify everything. Therefore, it's vital we store this because we'll be using it a lot.
The first parameter is that of your "window class" lpszClassName (that's a long pointer to a zero-terminated string, Hungarian notation fans). The next is the window caption. Then it's the screen x and y positions, and the window width and height. The others from that point on are default values... again, copy and paste without fear!
6) Show the window with ShowWindow
This displays your window. CmdShow is supplied by Delphi. It's for the "start minimised", "start normal", "start maximised" options for programs.
7) Update the window with UpdateWindow (optional)
This sends a WM_PAINT message. Note how much we're the g_Handle already.
8) Go into an infinite loop (your message loop)
The above keeps on processing messages until a WM_QUIT message happens, which is Windows telling us to get outta town.
PeekMessage checks the message queue for a message. If it finds it, it removes it from the queue (with PM_REMOVE specified, anyway). We then process it with TranslateMessage and DispatchMessage, which will call our lpfnWndProc function (our message handler). That's where the main action happens. You can use GetMessage instead of PeekMessage, although that blocks (sits there waiting for a message). If there isn't a message with PeekMessage it immediately continues. This makes it suitable for real-time effects, games, animations, etc.
9) Write a window function to handle message events
This should actually go above the previous code, but you'll be downloading the sample code and using that, so you should know that by now. Anyway...
We want to handle the Windows message WM_PAINT. This gets sent whenever Windows wants to update your window (little 'w'). You can capture it and do your drawing here. So what does that mean?
Add a case for WM_PAINT in your message handler. The message handler is a call-back function. That means you supply it to Windows when you register your "window class" and Windows calls it whenever a new message arrives (your "Window Class" had a field for this, remember?):
The above code gets called in response to Windows receiving a message. Messages handlers check the "Msg" parameter to see what the message type is. If it's one we care about, we add a line or two in our case to handle the message (like here, with WM_PAINT). If we haven't handled the message we call the default message handler, DefWindowProc with the same parameters. This lets Windows do its standard handling, and means no messages are skipped.
First of all, remember that WM_DESTROY handler. Without it, your program will hang around in the background after you close it. That is, if you click the close button on the window top-right and press CTRL-ALT-DEL, you'll still see it in the task manager list. Bleh!
The second part to note is that we call BeginPaint. This function lets Windows do some preparation so we can draw in the window. The first parameter is a handle to the window. This lets Windows identify what needs drawn, whether the background needs erased, etc. The second parameter is a TPaintStruct. This contains information that's quite useful, but that we don't need here.
Having called BeginPaint, we need a HDC (handle to device context, remember) to draw with. This is the return value of BeginPaint, so we store it.
After that, it's a case of drawing our stuff with the newly acquired HDC and then finishing up. Once we've done our drawing, we call EndPaint. This is pretty obvious: we let Windows know we've finished and it says, "thanks!"
Well, that's us got our HDC, which fills in the blank from the very first example: "This code, of course, assumes you have already nabbed a DC".
Let's add in an example function that draws lots of lines, for the sheer hell of it:
Why Did We Do All That? Remember that you don't need to memorise the above code. Just create a template API project (with a .dpr and .res file only! - don't include anything else like .dofs or .cfgs, which will mess up things if you copy and paste). Et voila! You've got a one-way ticket to smaller exes.
That's all folks!
Question, Suggestions... |
||||||||||||||||||||||||
All graphics (if any) in this feature created by Zarko Gajic.
| More Delphi |
|
· Learn another routine every day - RTL Quick Reference. · Download free source code applications and components. · Talk about Delphi Programming, real time. · Link to the Delphi Programming site from your Web pages. · Tutorials, articles, tech. tips by date: 2001|2000|1999|1998 or by TOPIC. |
|
· NEXT ARTICLE:
Articles. More Delphi articles |
| Stay informed with all new and interesting things about Delphi (for free). |
|
|
| Got some code to share? Got a question? Need some help? |

