Sign in Join
Wiki copytemporary

Windows API examples: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11*(Windows only)*
22
33This is a collection of Lazarus forum posts from user **440bx**, about Windows API examples. Source codes were [collected in GitHub](https://github.com/Alexey-T/Lazarus-WinAPI-examples).
44
55## Post 1
66
77Attached to this post are six (6) "teaser" API programs that give an idea of what these programs/examples are about. In addition, there is a "Windows Bitness" program that is more an illustration of how to get the Windows bitness than how to use a specific API.
88
99The teaser examples do _not_ include source at this time. I will include the source when the time comes to give an example of the API they demonstrate. The "Windows Bitness" includes full source to give an idea of what the source code for a typical example looks like.
1010
1111I will first give a quick description of the purpose of the "teaser" programs before discussing details of the "Windows Bitness" program. The teaser program/examples are:
1212
1313**ExpandEnvironmentStrings**
1414
1515This is an example of the many trivial API functions in Windows. These are APIs that don't have much to say about them. IOW, you read its documentation in MSDN, code the call and you're done.
1616
1717Roughly (iow, an estimate) 50% of the examples fall in that category. Their main reason to exist was to test their performance and/or behavior in unusual cases. (those were my personal reasons.)
1818
1919**SetConsoleWindowInfo**
2020
2121The description of SetConsoleWindowInfo in MSDN isn't particularly enlightening and the MSDN example's purpose seems to be to confuse the reader (just in case you understood the questionable description that preceded it.)
2222
2323The teaser example is a visual representation of how that API works. It shows the parameters before the call, the parameters after the call and the effects of the call.
2424
2525Roughly, 10% of the examples fall in that category (API operation visualization).
2626
2727I won't go into a detailed explanation at this time. It will be included along with the program source when it is its turn.
2828
2929**ExtTextOut** (two (2) examples)
3030
3131**InvalidateRect**
3232
3333**SelectClipRgn**
3434
3535These examples, in addition to demonstrating what the API does and how it works are related by one common goal which is, reducing or eliminating _flicker_ when redrawing.
3636
3737The InvalidateRect example flickers when the mouse coordinates are updated in the area dedicated to them. Selecting "Invalidate entire area" from the InvalidateRect menu shows how the size of the invalid area makes the flicker more noticeable.
3838
3939**ExtTextOut** (v1)
4040
4141That example is almost identical to the InvalidateRect example but, it demonstrates how to use ExtTextOut to significantly reduce flicker. In the case of the example, to a level that is almost imperceptible.
4242
4343**ExtTextOut** (v0)
4444
4545This example also demonstrates how to significantly reduce flicker but in a larger area. It also demonstrates the clipping abilities of ExtTextOut. The example also shows how to slow a GUI program down by using a console and, how to use the console to help in debugging.
4646
4747**SelectClipRgn**
4848
4949This example shows one the ways available to totally eliminate flicker (zero flicker without using double buffering.) Resize the window horizontally and/or vertically. Control flicker/no flicker with the menu options under "Flicker".
5050
5151**Window Bitness**
5252
5353Consider a program, let's call it program "A" that can be compiled as a 32 bit and 64 bit versions.
5454
5555The 64 bit version of the program has it easy. All it has to do is determine that it is itself a 64 bit program, once it knows that, it must be running on a 64 bit version of Windows (until MS releases a 128 bit version of Windows - won't happen tomorrow.) To determine if it is itself a 64 bit program, all it has to do is check the size of a pointer, i.e, sizeof(pointer) and if the size is 8 then it knows that it is a 64 bit program and since it is running, it must be running under a 64 bit version of Windows. Easy as pie.
5656
5757The 32 bit version cannot use that "trick" since its pointer size will be four (4) whether or not it is running under a 32 bit or 64 bit version of Windows.
5858
5959MS would have the programmer use the IsWowProcess() or IsWowProcess2() API to make the determination but, those two APIs have a few annoyances associated with them. First, they may not be present (IsWowProcess() requires XP SP2 or higher) and, even when present, the user running the program must have some privileges, that while not high, he/she may not have.
6060
6161There is a way to determine the bitness without using either or these APIs that do not require the user to have any "special" privileges. The "trick" resides in two details. The first one is that the Windows server is what determines the Windows bitness, the second one is that LoadLibraryEx will load almost anything you want as long as you don't intend to run it.
6262
6363Using the first observation, if Windows is 32 bit then its csrss.exe (client service runtime subsystem) will also be 32 bit and, it will always be 64 bit in a 64 bit version of Windows.
6464
6565Using the second observation, we can simply ask LoadLibraryEx to load csrss.exe as a datafile and unless the user is totally "privilege destitute", LoadLibraryEx will serve csrss.exe in a sliver platter.
6666
6767All that's left is for the program to rummage in csrss.exe to determine its bitness and whatever its bitness is, that's the Windows bitness too :) Done!
6868
6969That's what the program below does (full source in the attachment)
7070
7171On line 192, the program uses LoadLibraryEx to load csrss.exe. On line 211, it checks the address returned by LoadLibraryEx, which fiddles with the LSB to indicate that it was loaded as a data file and adjusts the address to turn it into the real load address.
7272
7373On line 219, using the real load address, it determines the module bitness (see GetModuleBitness on line 75.) If anything does not go according to plan, the program states it could not determine the Windows bitness, otherwise it outputs it. Easy as 32 bit pie. :)
7474
7575My intention is to start with the simplest most trivial API and build from there. This way those who follow the examples can gain the necessary knowledge to understand more subtle and complex use the API.
7676
7777NOTE: There is an example dedicated to LoadLibraryEx by itself which shows how it behaves under various circumstances.
7878
7979About 1% (maybe slightly more) of the examples are like the one above, where one API along with "peripheral" knowledge can be used to extract information out of Windows.
8080
8181I suggest creating a "WinAPI" directory and under that directory, create directories named "Techniques" (which the above example would go into), ntdll, kernel32, gdi32 and user32. Of course, you're free the arrange the examples however you like but, I have found that way to make the large number of programs easier to manage and find.
8282
8383## Post 2
8484
8585The examples will be presented roughly in order of complexity, that is, from simplest to more complex. The simplest are by far those APIs which should NOT be used since no example will be provided for those.
8686
8787Amond the APIs that should be avoided and preferably never used are:
8888
8989- GlobalCompact
9090- GlobalFix
9191- GlobalFlags
9292- GlobalHandle
9393- GlobalLock
9494- GlobalReAlloc
9595- GlobalSize
9696- GlobalUnfix
9797- GlobalUnlock
9898- GlobalUnWire
9999- GlobalWire
100100- LocalAlloc
101101- LocalCompact
102102- LocalFlags
103103- LocalFree
104104- LocalHandle
105105- LocalLock
106106- LocalReAlloc
107107- LocalShrink
108108- LocalSize
109109- LocalUnlock
110110
111111All of the above are Windows 16 bit memory management functions which are _not_ well suited for a 32 bit O/S. Use the 32 bit memory management functions instead.
112112
113113As mentioned previously, no example for any of the above APIs will be given.
114114
115115**GlobalAlloc**
116116
117117**GlobalFree**
118118
119119These two (2) APIs are still commonly found in C programs but, are not as flexible as HeapAlloc() and HeapFree() which should be preferred.
120120
121121No example of GlobalAlloc and GlobalFree will be given. An example of HeapAlloc and HeapFree will be given in the future.
122122
123123**GlobalMemoryStatus**
124124
125125GlobalMemoryStatus returns incorrect information on systems with more than 4GB of memory. Use GlobalMemoryStatusEx instead.
126126
127127An example for both will be given in the future.
128128
129129**GetTextExtentPoint**
130130
131131Use GetTextExtentPoint32 instead which, according to MS, is more accurate.
132132
133133**WinAPI - GetTextExtentPoint** (gdi)
134134
135135**WinAPI - GetTextExtentPoint32** (gdi)
136136
137137Both of these APIs return the width and height in pixels of a specified string
138138
139139**WinAPI - SetBkMode** (gdi)
140140
141141Sets the background color to opaque or transparent. When the background is opaque then whatever color was chosen as background will be visible.
142142
143143**WinAPI - SetTextAlign** (gdi)
144144
145145Sets the text alignment. This example visually demonstrates the effects of some of the SetTextAlign options. See MSDN for all available options.
146146
147147**WinAPI - TextOut** (gdi)
148148
149149Outputs a string at the specified location, using the currently selected font, background color, and text color.
150150
151151**WinAPI - GetClientRect** (user)
152152
153153Retrieves the coordinates of the client area. This example draws a frame around the client area. It also serves as an example of CreatePen, SetCapture, ReleaseCapture, InvalidateRect, SelectObject, Rectangle, lstrcpy, lstrlen and DeleteObject.
154154
155155NOTE: this example does not include code to eliminate or minimize flicker,which would not be hard to implement (left for future example.)
156156
157157**WinAPI - Windows Bitness** (technique)
158158
159159This version includes minor editing of the comments and loads csrss.exe as an image resource instead of a data file. Since Windows uses the low bits of the load address to indicate the mode the file was loaded in, this version aligns the load address instead of simply subtracting one (1) which is valid only when the module is loaded as a data file. Using alignment works regardless of the way the module is loaded.
160160
161161recommendation : replace the previous version with this version.
162162
163163What follows is the code for the GetClientRect example (the source for the remaining examples is present in the zip file attached to this post.)
164164
165165## Post 3
166166
167167Here are some examples of simple, yet often very useful API functions.
168168
169169**WinAPI - DebugBreak**
170170
171171This example demonstrates using DebugBreak() to transfer control to a debugger. The example should be executed both, under a debugger and directly.
172172
173173This example also serves to demonstrate how a program can detect if it is running under a debugger without using the IsDebuggerPresent() API.
174174
175175Use the "DebugBreak()" menu item to cause the program execution to fall under debugger control. Use F7/F8/F9 to control execution.
176176
177177DebugBreak() is very useful to create conditional breakpoints with very complex conditions that are beyond the capabilities of an application debuggers.
178178
179179Also, DebugBreak() is useful to set breakpoints where it is usually not possible to set them. For instance, during the loading of a program. Windows automatically loads and calls the associated DLLs entry points. A strategically placed DebugBreak instruction allows a debugger to gain control during the DLLs load process. This combined with the use of a system debugger, such as SoftICE, allows debugging almost any code in the executable.
180180
181181In addition to that, combining the use of DebugBreak() with conditional compilation directives allows having the breakpoints, which in a large program may be numerous, in the program itself. This prevents "losing" breakpoints by exceeding the "breakpoint history" capacity in most debuggers and aids in future maintenance as, at least in theory, calls to DebugBreak() should only appear (or be left) in the most important or complex section of the code.
182182
183183DebugBreak() can also be useful to make reverse engineering a program more cumbersome. By littering the executable with a fairly large number of calls to it (say about 60 or more) this can make reverse engineering the application very tedious when it is run under a debugger.
184184
185185(Code skipped...) The above is the "critical" part of the code in the example. By executing DebugBreak() in a try/except, the application can detect whether or not it is running under a debugger. If the code in the exception handler is _not_ executed then, the program is not being debugged because, if it were, the DebugBreak() would have transferred control to the debugger instead of activating the exception handler.
186186
187187To make reverse engineering more cumbersome, a program could place code that _should be_ executed in the exception handler. If the program is running under a debugger, the piece of code that is actually part of the normal operation of the program is not executed thus causing the program to later fail in some way. Put around fifty (50) of those cases around in the program and, that will try the patience of whoever is trying to find out how the code works. :)
188188
189189This works particularly well when present in functions/procedures that are conditionally executed and/or are not executed often (it increases the chances of one or more of them being missed.)
190190
191191**WinAPI - IsDebuggerPresent**
192192
193193This example demonstrates how to use the API IsDebuggerPresent() to determine if a program is running under a debugger or not. If the program is running under a debugger, selecting "Break into the debugger" from the IsDebuggerPresent() menu, will cause a break into the debugger, otherwise the request to break into the debugger will be ignored.
194194
195195It also demonstrates the difference between using DebugBreak() and an inline int 3. When using DebugBreak(), application debuggers will stop at the DebugBreak() line because the _entire_ DebugBreak() procedure has _not_ yet been executed, specifically, it stops at the "ret" that is inside DebugBreak().
196196
197197When using an inline "int 3" the debugger will stop at the next source line because the "int 3" has been fully executed unlike the DebugBreak() procedure.
198198
199199IsDebuggerPresent() is particularly useful to guard calls to DebugBreak(). That way, it is not necessary to remove the calls to DebugBreak() in the "release" executable since they are triggered only when the executable is run under a debugger (normal users rarely run a program under a debugger and, many of them don't even know what a debugger is anyway.)
200200
201201**WinAPI - QueryPerformanceCounter**
202202
203203**WinAPI - QueryPerformanceFrequency**
204204
205205QueryPerformanceCounter along with QueryPerformanceFrequency can be used to measure elapsed time very accurately. That's the combined purpose of these two APIs.
206206
207207To determine elapsed time in seconds, simply subtract the first query from the second query and divide the result by the frequency.
208208
209209The attached examples only show how to call each of these functions. They do not include how to calculate elapsed time (it is reasonable to believe that programmers can subtract one number from another and divide the result by yet another number, i.e (b - a) div c)
210210
211211**WinAPI - SetLastError**
212212
213213SetLastError can be used by an application's procedures to indicate whether or not they successfully performed their task. Note that it is strongly recommended to mark these custom application errors (the example shows how that is done.)
214214
215215Also, it is important NOT to call any Windows API functions after setting the last error as it is very likely that the API function will overwrite the last error set by the application.
216216
217217Fun experiment :
218218
2192191\. Start the SetLastError example
220220
2212212\. Select "LoadLibrary failure" from the "SetLastError" menu
222222
2232233\. Notice the value of the error (should be \$A1)
224224
2252254\. Select "About SetLastError" from the "Help" menu
226226
2272275\. Click the OK button on the About box
228228
2292296\. Notice the value of the error (should still be \$A1
230230
231231Now repeat step 1 through 4 above
232232
2332335\. Move the About box vertically a couple of pixels up and click the OK button
234234
2352356\. Notice the value of the error (should be \$0 instead of \$A1)
236236
2372377\. Why did moving the About box cause the value of SetLastError to change ?
238238
239239**WinAPI - Sleep**
240240
241241The MSDN documentation states quite a few things about the Sleep API. This test program demonstrates what _actually_ happens when the sleep interval changes.
242242
243243RECOMMENDATION : _NEVER_ use an interval of zero (0). An interval of zero (0) causes most versions of Windows (maybe all) to consume significantly more CPU than expected. Run the example while watching the CPU usage in Task Manager and selecting various values from the menu.
244244
245245To eliminate the overhead of repainting the screen, uncomment the NOPAINT define. (painting does not perceptibly alter the cpu consumption, the DEFINE is there to test and prove that is the case.
246246
247247## Post 4
248248
249249This post includes examples for the following APIs :
250250
251251**WinApi - CreateStatusWindow** (example is named StatusBar)
252252
253253This example shows the use of the obsolete CreateStatusWindow API and a "quirk" in the number of characters a status bar displays. CreateWindow or CreateWindowEx should be used instead of CreateStatusWindow. The fact that the API is obsolete and that the example demonstrates a quirk in the status bar is the reason the example is not named after the API.
254254
255255The sample program attempts to display a string that is longer than 137 characters in the status bar. The string to be displayed consists of a sentence that is repeated three times. If the status bar does not display the same sentence three times then truncation occurred.
256256
257257To show where the truncation took place, the text from the status bar is retrieved using SB_GETTEXT and subsequently displayed in the listbox. If the listbox displays the sentence three times (as expected), it indicates that the status bar did save the entire string but displayed only part of it.
258258
259259In spite of the fact that the status bar will only display 137 characters, it does keep a full copy of the string that was sent to it using SB_SETTEXT.
260260
261261Under build 950 of Windows 95 and later versions as well, the status bar control will not display more than 137 characters. It is not known whether this limit is intentional or not but, it does not seem to be documented anywhere.
262262
263263**WinAPI - GetEnvironmentStrings**
264264
265265This example shows how to call the GetEnvironmentStrings to, as its name indicates, obtain the environment strings. It also shows how to parse the block of memory where the strings reside. As each individual string is identified it loads it into a listbox.
266266
267267NOTE: this example takes a "shortcut" when it comes to determining the listbox horizontal extent. In production code, the horizontal extent should be calculated as the width of the longest string added into the listbox. This simple program uses a hard coded estimate which in many cases will result in the "PATH" string not being fully visible.
268268
269269Future examples will show how to calculate the horizontal extent properly.
270270
271271Also, this example doesn't even bother to free the memory block created by GetEnvironmentStrings which should be done to avoid a potential memory leak. See the FreeEnvironmentStrings example below.
272272
273273This example also shows how to create a status bar at the top of a window as well as at its bottom (the more common case.) However, note that the API CreateStatusWindow is now obsolete. CreateWindow or CreateWindowEx should be prefered. Anything CreateStatusWindow can do, those APIs can do just as well.
274274
275275Also, this example does not show how to workaround a bug in the listbox control. The bug _sometimes_ causes the contents of the listbox to be garbled when the window is resized and the horizontal scroll thumb is not at the origin, i.e, horizontal scrolling has taken place. The reason the workaround code is not present is because the window isn't resizable. See the StatusBar example, which uses a resizable window, for the workaround. Note, that the presence of this bug is likely dependent on the version of Windows and isn't triggered consistently.
276276
277277**WinAPI - FreeEnvironmentStrings**
278278
279279This example calls the GetEnvironmentStrings API immediately followed by a call to FreeEnvironmentStrings.
280280
281281In Windows 9x the FreeEnvironmentStrings function does not free the environment strings. It verifies that the parameter passed does indeed point to the process' environment strings. If it does, the function returns TRUE otherwise it returns FALSE but it never frees the block.
282282
283283In other versions of Windows, FreeEnvironmentStrings does free a memory block that contains the environment strings.
284284
285285**WinAPI - ExpandEnvironmentStrings**
286286
287287This example shows how to retrieve the value of a known environment string.
288288
289289**WinAPI - GetModuleHandle**
290290
291291This example shows how to call GetModuleHandle to retrieve the load address of modules in the process including the .exe itself.
292292
293293It also serves as an example for the **WinAPI - ModifyMenu**. The items under the "GetModuleHandle" menu change depending on the bitness of the program. The change is done using ModifyMenu. Notice that the items in the menu are _not_ those declared in the .rc resource file and differ depending on the program's bitness.
294294
295295It can also serve as an example for the **WinAPI - GetMenu** and **WinAPI - GetSubMenu**. In order to modify a specific menu item, first the handle to the menu must be obtained using GetMenu and after that the handle to the popup menu associated with the menu item must be obtained using GetSubMenu. Once the submenu handle is known then ModifyMenu can be called to modify an item in the popup menu.
296296
297297Note unrelated to any Windows API: the width and precision stated in the StrFmt function is ignored by StrFmt if necessary (it is when the program is 64 bit.)
298298
299299**WinAPI - GetModuleHandleEx**
300300
301301This example shows how to call GetModuleHandleEx to determine a module's load address based on any address that resides in the module. For instance, knowing that some dll function resides at a specific address, GetModuleHandleEx when given that address can return the Dll's load address.
302302
303303This ability can often be useful to debuggers.
304304
305305This example also demonstrates how to "pin" a dll in memory, that is, ensuring that the dll is not unloaded at an undesirable time.
306306
307307There are, at least, two cases when that capability is useful/required.
308308
309309The first is in a multi-threaded environment where one thread obtains a dll's module handle while another thread _may_ unload the dll thus invalidating the handle obtained by the first thread. Note, this situation quite likely indicates very poor programming, a module should _not_ be loaded by one thread and unloaded in another. IOW, the thread that loads a dll should be the only thread responsible for unloading it.
310310
311311The second case is not as common as the first one described above. When a program injects a dll into another process and the injected dll in turn traps functions in application specific dlls then, it is not only conceivable but, even likely that the application may decide to unload one of it dlls that have functions trapped by the injected dll. When this happens the injected dll will call into the void since the dll functions it was trapping are gone.
312312
313313Pinning down a dll prevents this very undesirable situation from taking place.
314314
315315To see the effects of pinning down a dll use the "Module32First" example (source for it will be given in a future post) and follow these steps :
316316
3173171\. After starting the GetModuleHandleEx example, start the Module32First program.
318318
3193192\. Search and select "GetModuleHandleEx.exe" on the left hand side pane of Module32First
320320
3213213\. Note the values of "USG" and "usg" for the "GetModuleHandleExDll" (they should both be two (2)
322322
3233234\. Select "Pin and attempt to unload the dll" from the "GetModuleHandleEx" menu.
324324
3253255\. Select "Update snapshot" in the Module32First program.
326326
3273276\. Repeat steps 2 and 3 above. The values under "USG" and "usg" should read 65535 indicating that the dll is pinned.
328328
329329Every time the "Pin and attempt to unload the dll" option is executed, the program attempts to (unsuccessfully) unload the library 100 times. What should be noted is that 100 is _not_ subtracted from the "USG" and "usg" fields. Once those fields are set to high(word) they are no longer reference counted.
330330
331331The interesting code portions of this program are:
332332
333333``` pascal
334334 case Msg of
335335 WM_CREATE:
336336 begin
337337 { load the supporting dll }
338338
339339 ModuleAddressA := LoadLibrary(DllToLoad);
340340
341341 FunctionAddress := GetProcAddress(ModuleAddressA,
342342 pchar(DLL_FUNCTION_INDEX));
343343
344344 { get the module address again but this time using the function address }
345345
346346 if not GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
347347 FunctionAddress,
348348 ModuleAddressB) then
349349 begin
350350 LastError := GetLastError();
351351
352352 MessageBox(Wnd,
353353 'GetModuleHandleEx call failed in WM_CREATE',
354354 'Main Window',
355355 MB_ICONERROR or MB_OK);
356356
357357 WndProc := -1;
358358
359359 exit;
360360 end;
361361
362362 { format and save the values obtained }
363363
364364 StrFmt(Values[1], ' %p', [FunctionAddress]);
365365
366366 { the next two addresses should be the same }
367367
368368 StrFmt(Values[2], ' %p', [pointer(ModuleAddressA)]);
369369 StrFmt(Values[3], ' %p', [pointer(ModuleAddressB)]);
370370
371371 exit;
372372 end;
373373```
374374
375375On line 13, GetModuleHandleEx is used to obtain the dll's load address using the address of one of the functions it contains. During WM_PAINT (not shown here) both addresses, the one obtained this way and the one obtained using GetModuleHandleEx are output, thereby making it easy to verify that both addresses are the same (as they should be)
376376
377377The other interesting part is :
378378
379379``` pascal
380380
381381 case LOWORD(wParam) of
382382 IDM_BYADDRESSANDPIN:
383383 begin
384384 if not GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN,
385385 DllToLoad,
386386 ModuleAddressB) then
387387 begin
388388 LastError := GetLastError();
389389
390390 MessageBox(Wnd,
391391 'GetModuleHandleEx call failed in IDM_BYADDRESSANDPIN',
392392 'Main Window',
393393 MB_ICONERROR or MB_OK);
394394
395395 WndProc := -1;
396396
397397 exit;
398398 end;
399399
400400 for i := 1 to 100 do FreeLibrary(ModuleAddressB);
401401
402402 end;
403403```
404404
405405On line 4, GetModuleHandleEx is used to pin down the dll in memory then in line 20 FreeLibrary is called 100 times to unload the dll. Use Module32First to verify that the dll is not freed/unloaded.
406406
407407**WinAPI - Module32First**
408408
409409For the time being, this example is only included to help visualize what GetModuleHandleEx does. Its source code will be included in a future example.
410410
411411## Post 5
412412
413413This post includes examples for the following APIs :
414414
415415**WinAPI - GetFileSize**
416416
417417**WinAPI - GetFileSizeEx**
418418
419419These two examples show how to get a file's size. GetFileSize should be avoided because the results it returns can be ambiguous for large files. For this reason, it is preferable to always use GetFileSizeEx.
420420
421421**WinAPI - GetSystemInfo**
422422
423423**WinAPI - GetNativeSystemInfo**
424424
425425These two functions are very similar, almost identical. The critical difference is in how the Processor Architecture is reported.
426426
427427Presuming the program is run on a 64 bit intel/AMD CPU, A 32 bit program that uses GetSystemInfo will always see the processor architecture as PROCESSOR_ARCHITECTURE_INTEL, while a 32 bit program using GetNativeSystemInfo will see PROCESSOR_ARCHITECTURE_INTEL when running under a 32 bit version of Windows and see PROCESSOR_ARCHITECTURE_AMD64 when running under a 64 bit version of Windows. The reported processor type will also be different in the above mentioned cases, one case reporting PROCESSOR_INTEL_PENTIUM and the other reporting PROCESSOR_AMD_X8664.
428428
429429In spite of that, MS documentation claims that using GetNativeSystemInfo is _not_ a reliable method to determine the O/S bitness.
430430
431431**WinAPI - GetVersion** (deprecated)
432432
433433**WinAPI - GetVersionEx** (deprecated)
434434
435435**WinAPI - RtlGetNtVersionNumbers** (undocumented)
436436
437437Both GetVersion and GetVersionEx should be avoided not only because they are deprecated but, because these functions (and a few others) don't report the _true_/real Windows version the program is running under.
438438
439439On the other hand, RtlGetNtVersionNumbers reports the _true_ and accurate Windows version.
440440
441441The function (in NTDLL) is as simple as can, and should be:
442442
443443``` asm
444444 public RtlGetNtVersionNumbers
445445 RtlGetNtVersionNumbers proc near
446446
447447 test rcx, rcx ; ensure pointer not nil
448448 jz short loc_78E9517B
449449 mov dword ptr [rcx], 6
450450
451451 loc_78E9517B:
452452 test rdx, rdx ; ensure pointer not nil
453453 jz short loc_78E95186
454454 mov dword ptr [rdx], 1
455455
456456 loc_78E95186:
457457 test r8, r8 ; ensure pointer not nil
458458 jnz loc_78EB7C9D ; see below
459459 rep retn
460460 RtlGetNtVersionNumbers endp
461461
462462 loc_78EB7C9D:
463463 ; F000 -> free build
464464 ; 1DB1 -> build number
465465
466466 mov dword ptr [r8], 0F0001DB1h
467467 retn
468468```
469469
470470Before any other consideration, it is worth pointing out that while this function is undocumented, some important MS code depends on its existence, notably some standard C dlls. Obviously, deciding for or against using undocumented functions is programmer's choice. In the case of this function, it's likely that it's here to stay.
471471
472472The example RtlGetNtVersionNumbers comes in two "flavors", GUI and Console. From a programming viewpoint, there is nothing particularly remarkable in the GUI version. The console version has some remarkable characteristics, not due to the API but, due to how FPC manages consoles and, getting a console application to display a dialog box by selecting a menu item appended to the system menu.
473473
474474As far as how FPC manages consoles, the FPC RTL assumes that there can only be one console during the life of a program. That assumption is incorrect. Consoles, just like any other "resource" can come and go at any time and multiple times.
475475
476476The functions in console.inc are "general" in nature (actually, only somewhat general.) They are meant to be used by a GUI program which creates and deletes multiple consoles during its execution. Something the RtlGetNtVersionNumbers example does _not_ do.
477477
478478For a discussion of how the FPC RTL manages consoles, see the thread : <https://forum.lazarus.freepascal.org/index.php/topic,44828.msg315468.html#msg315468> The last post includes an example program.
479479
480480The other purpose of console.inc is to provide a mechanism for a console application to display a dialog box (usually with author's name, copyright, etc.) This sounds like it would be a trivial thing to do but, I could not find a simple and straightforward solution that worked reliably. The solution in console.inc is pure brute force (but it works :)). It creates a thread specifically with the goal of figuring out if the "About ..." item in the system menu was selected and, if so, presents the dialog box.
481481
482482If someone can come up with a simpler solution that works reliably to display the dialog box, that would be a very welcome and appreciated improvement.
483483
484484If it weren't for the code to manage the creation and destruction of multiple consoles and displaying a dialog box, the console version of this API example would be totally trivial
485485
486486## Post 6
487487
488488This post includes examples for the following APIs :
489489
490490The following two APIs are in one program named GetDesktopWindow
491491
492492**WinAPI - GetDesktopWindow**
493493
494494**WinAPI - GetWindowThreadProcessId**
495495
496496This example gets the handle of the desktop window and the process and thread ids associated with it. It also provides the option to hilight the desktop window in its "GetDesktopWindow" menu.
497497
498498Note that the desktop window and the shell window are different windows in spite of the fact that they cover the same rectangle.
499499
500500**WinAPI - GetShellWindow**
501501
502502This example is almost identical to the GetDesktopWindow except that it shows and highlights the shell window instead of the desktop window.
503503
504504**WinAPI - GetTaskmanWindow** (undocumented)
505505
506506Also very similar to the above mentioned examples but showing the Task manager window.
507507
508508The following two APIs are in one program named GetWindowTextLength
509509
510510**WinAPI - GetWindowText**
511511
512512**WinAPI - GetWindowTextLength**
513513
514514This example shows how to use GetWindowText and GetWindowTextLength as well as drawing a frame around a window, creating a pen, offsetting a rectangle, locking out paint updates on a window, (using GetDCEx), SetROP2, SetWindowPos, GetCapture, ReleaseCapture, GetCursorPos, IsWindow and, LockWindowUpdate among others.
515515
516516The example shows the window text and text length that is associated with the window that resides under the mouse.
517517
518518**WinAPI - IsTopLevelWindow** (undocumented)
519519
520520Functionally similar to the GetWindowTextLength example. It determines if the window that is currently under the cursor is a top level window or not.
521521
522522**WinAPI - GetTopLevelWindow** (undocumented)
523523
524524The same as the IsTopLevelWindow example but displays the top level window that is associated with the window that is currently under the cursor.
525525
526526**WinAPI - SetWindowLong and SetWindowLongPtr**
527527
528528This example uses SetWindowLong (in 32bit) and SetWindowLongPtr (in 64 bit) to change its window frame from that of a typical window to one that has a thin dialog box frame without caption.
529529
530530It also shows how to process the messages WM_NCHITTEST and WM_NCLBUTTONDBLCLK to switch from one frame style to another.
531531
532532It also shows how to use a window's client area to drag it around the screen (feature is purposely active only when the window has no caption.)
533533
534534## Post 7
535535
536536This post includes examples for a number of API functions but because they are also meant to show how to deal with flicker, the main APIs used in each example is listed. The APIs are:
537537
538538- WinAPI - Rectangle (in Rectangle)
539539- WinAPI - CreateSolidBrush
540540- WinAPI - CreatePen
541541- WinAPI - CheckMenuRadioItem
542542- WinAPI - GetStockObject
543543- WinAPI - DeleteObject
544544
545545- WinAPI - CreateWindowEx (in FlickerMax)
546546- WinAPI - GetClassInfoEx
547547- WinAPI - RegisterClassEx
548548- WinAPI - BeginPaint
549549- WinAPI - EndPaint
550550- WinAPI - SelectObject
551551- WinAPI - SetBkColor
552552- WinAPI - GetTextExtentPoint32
553553- WinAPI - SetTextAlign
554554- WinAPI - TextOut
555555- WinAPI - DialogBox
556556- WinAPI - EndDialog
557557- WinAPI - DestroyWindow
558558- WinAPI - PostQuitMessage
559559
560560- WinAPI - CopyRect (in FlickerMin)
561561- WinAPI - Rectangle
562562
563563- WinAPI - GetClassLongPtr (in HREDRAW_VREDRAW)
564564- WinAPI - SetClassLongPtr
565565- WinAPI - GetMenuState
566566- WinAPI - CheckMenuItem
567567
568568- WinAPI - CreateSolidBrush (in Gradient)
569569- WinAPI - DeleteObject
570570- WinAPI - GetDC
571571- WinAPI - ReleaseDC
572572- WinAPI - MulDiv
573573- WinAPI - FillRect
574574- WinAPI - PostMessage
575575- WinAPI - CheckMenuRadioItem
576576- WinAPI - InvalidateRect
577577
578578- WinAPI - GetClipBox (in GetClipBox)
579579- WinAPI - CopyRect
580580- WinAPI - SetRectEmpty
581581- WinAPI - SetTimer
582582- WinAPI - KillTimer
583583- WinAPI - BringWindowToTop
584584- WinAPI - SetForegroundWindow
585585- WinAPI - GetWindow
586586- WinAPI - GetWindowDC
587587- WinAPI - UpdateWindow
588588- WinAPI - Rectangle
589589
590590- WinAPI - GetClipBox (in IsWindowFullyCovered)
591591- WinAPI - SetTimer
592592- WinAPI - KillTimer
593593- WinAPI - BringWindowToTop
594594- WinAPI - SetForegroundWindow
595595- WinAPI - InvalidateRect
596596- WinAPI - UpdateWindow
597597- WinAPI - GetClassLongPtr
598598- WinAPI - CopyRect
599599- WinAPI - Rectangle
600600
601601- WinAPI - GetParent (in GetParent)
602602- WinAPI - GetAncestor
603603- WinAPI - GetConsoleWindow
604604- WinAPI - GetConsoleProcessList
605605- WinAPI - GetDC
606606- WinAPI - ReleaseDC
607607- WinAPI - GetWindowRect
608608- WinAPI - GetTopLevelWindow
609609- WinAPI - IsTopLevelWindow
610610- WinAPI - SetWindowPos
611611- WinAPI - MoveWindow
612612
613613- WinAPI - EnumWindows (in EnumWindows)
614614- WinAPI - GetWindowThreadProcessId
615615- WinAPI - IsWindowVisible
616616- WinAPI - GetWindowText
617617- WinAPI - SendMessage
618618- WinAPI - InitCommonControls
619619- WinAPI - CreateStatusWindow
620620- WinAPI - GetClassLongPtr
621621- WinAPI - SetClassLongPtr
622622- WinAPI - CreateWindowEx
623623- WinAPI - GetDlgItem
624624- WinAPI - MoveWindow
625625- WinAPI - GetScrollPos
626626
627627- WinAPI - EnumChildWindows (in EnumChildWindows)
628628- WinAPI - EnumWindows
629629- WinAPI - GetAncestor
630630- WinAPI - GetDesktopWindow
631631- WinAPI - IsWindowVisible
632632- WinAPI - GetWindowText
633633- WinAPI - GetWindowThreadProcessId
634634
635635in addition to the APIs listed above, this post and the following ones focus on the reduction or elimination of flicker.
636636
637637Flicker is mostly caused by three (3) characteristics, they are: the presence of CS_HREDRAW and CS_VREDRAW in the window class and the processing of the WM_ERASEBKGND (erase background) window message.
638638
639639The CS_HREDRAW and CS_VREDRAW cause the window's entire client area to be invalidated when the window is resized. This in turn causes the entire background of the window to be erased by the default window procedure when a WM_ERASEBKGND message is received. It is the full erasing of the background that is the source of flicker. It causes two different images to appear, the first image is the background (for a very short time) the second one is the image that is supposed to be painted on it.
640640
641641Carefully controlling _what_ has to be erased when a WM_ERASEBKGND message is received can significantly reduce flicker and often eliminate it completely.
642642
643643There are a number of ways to reduce or eliminate flicker, of those, double buffering is likely the most common one due to the fact that it is fairly simple to implement and it completely eliminates flicker. Double buffering consists of painting on a bitmap that is compatible with the output device (usually but not always the screen) then bitblt-ing the fully drawn bitmap onto the output device without having to erase the background first, which is not required because the bitmap is the full and final image that should appear on the output device.
644644
645645Double buffering, while very effective is not without disadvantages, among them, it requires some code to implement it, often specific for each window that uses it and, it can potentially use a fair amount of memory. In addition to that, it has the potential of noticeably lowering the speed at which the windows that use it are repainted. This can be experienced when a program's window feels "elastic" when being resized. The more windows on the user interface, the greater the potential for the window to feel "elastic" when resized.
646646
647647The **FlickerMax** example is what happens when the window class includes CS_HREDRAW and CS_VREDRAW and the default window procedure is allowed to erase the background when processing the WM_ERASEBKGND. The resulting amount of flicker is substantial and visually offensive. It can be seen in its greatest "glory" when the window is resized horizontally. FlickerMax uses the following code when registering the class:
648648
649649``` pascal
650650 if not GetClassInfoEx (hInstance, AppName, cls) then
651651 begin
652652 with cls do
653653 begin
654654 { cbSize has already been initialized as required above }
655655
656656 style := CS_BYTEALIGNCLIENT or CS_VREDRAW or CS_HREDRAW;
657657 lpfnWndProc := @WndProc; { window class handler }
658658 cbClsExtra := 0;
659659 cbWndExtra := 0;
660660```
661661
662662and does not process the WM_ERASEBKGND thus letting the default window procedure erase the entire client area.
663663
664664The **FlickerMin** example is code-wise almost identical to FlickerMax, the difference is that FlickerMin does _not_ allow the default window procedure to erase the background, instead, it erases the rectangles that are above and below the text when processing the WM_PAINT, followed by outputting the line of text. That small difference greatly reduces the amount of flicker. Note that the flicker isn't totally eliminated in this case, resizing the FlickerMin horizontally makes it easy to notice but, for most purposes, it can be considered tolerable.
665665
666666FlickerMin is the reason for the Rectangle example. When using the Rectangle API it is important to keep in mind the type of pen that API uses to draw the edges of the rectangle. When the pen is a NULL pen the rectangle drawn will be one (1) pixel smaller horizontally and vertically than the specified rectangle coordinates. This is because Rectangle uses the pen to draw the outer right and bottom edge of the rectangle and, when the pen is NULL this causes those edges not to be drawn resulting in a rectangle that is one (1) pixel short on the right and bottom edges.
667667
668668This can be seen with the **Rectangle** example by changing the pen used from a null pen to any other colored pen back and forth. A zoomed image of the bottom right of the resulting rectangle is attached where the difference between using a null pen and a colored pen is easier to see.
669669
670670![](/wiki-images/b/ba/WinAPI_rectangle_zoomed.png)
671671
672672When using the Rectangle API to erase parts of the client area background, the simplest method consists of specifying a null pen and increasing the Right and Bottom coordinates of the rectangle. This is what FlickerMin does to erase the part of the client area that is above and below the text without erasing any portion where the new text should appear.
673673
674674The little bit of flicker seen in FlickerMin is due to its reliance on ExtTextOut to clear the narrow and wide rectangle where the text is displayed. It is possible to make the flicker imperceptible by using Rectangle to erase the area on the left and right of where the text will appear (in addition to above and below) and instruct ExtTextOut to confine its output to the rectangle that is snug to the text. (that improvement is left as an exercise for the reader ;) - simply two additional calls to Rectangle and a change in the rectangle used by ExtTextOut)
675675
676676FlickerMin uses the same code as FlickerMax when registering the window class but, when it gets a WM_ERASEBKGND it tells Windows that it took care of erasing the background by using the following code:
677677
678678``` pascal
679679 WM_ERASEBKGND:
680680 begin
681681 { tell Windows we erased the background }
682682
683683 WndProc := 1;
684684
685685 exit;
686686 end;
687687```
688688
689689then when processing the WM_PAINT it uses:
690690
691691``` pascal
692692 {-----------------------------------------------------------------------}
693693 { clear the rectangles above and below the text }
694694
695695 { this could have been done when processing the WM_ERASEBKGND message }
696696 { but it's more convenient to do here. }
697697
698698 CopyRect(ClearRect, ClientRect);
699699
700700 { clear the rectangle above the text }
701701
702702 with ClearRect do
703703 begin
704704 { Left, Right and Top are already set }
705705
706706 Bottom := TextRect.Top;
707707 Rectangle(ps.hdc, Left, Top, Right, Bottom);
708708 end;
709709
710710 { clear the rectangle below the text }
711711
712712 with ClearRect do
713713 begin
714714 { Left and Right are already set }
715715
716716 Top := TextRect.Bottom;
717717 Bottom := ClientRect.Bottom;
718718
719719 Rectangle(ps.hdc, Left, Top, Right, Bottom);
720720 end;
721721```
722722
723723which erases the areas above and below the text. A couple of additional calls to rectangle could be used to erase the areas to the right and left of the text that _will_ be output to make flicker imperceptible.
724724
725725One of the most important decisions is determining if CS_HREDRAW and CS_VREDRAW are necessary when registering the window class. It is, unfortunately, somewhat common to see windows include those two (2) styles in the window class when they aren't really necessary.
726726
727727The HREDRAW_VREDRAW is an example. it does NOT include the CS_HREDRAW and CS_VREDRAW styles, yet it updates the window correctly and without a trace of flicker without using any methods to prevent flicker. The example includes the option of adding CS_HREDRAW and CS_VREDRAW in the class styles, when that is done, there will be a very significant amout of flicker.
728728
729729Again, HREDRAW_VREDRAW does NOT include any code at all to prevent flicker. The exclusion of CS_HREDRAW and CS_VREDRAW is all that is needed to have no flicker whatsoever when resizing the window.
730730
731731The CS_HREDRAW and CS_VREDRAW styles need to be included when what is displayed in the client area changes depending on the size of the window. The Gradient is an example of this. Specifically, the color used at a particular location changes depending on the size of the window, because of this, the entire window needs to be invalidated and repainted whenever its size changes.
732732
733733The **Gradient** program draws everything when processing the WM_ERASEBKGND and tells Windows that it took care of erasing the background. This is just to show that, at least in some cases, it is possible to repaint the entire client area when processing the WM_ERASEBKGND. However, a "normal" program would simply return one (1) when processing WM_ERASEBKGND and redraw the gradient when dealing with WM_PAINT.
734734
735735NOTE: the gradient program uses functions to draw the vertical and horizontal gradients. It would be fairly easy to roll them into a single function that uses an "orientation" parameter to select vertical or horizontal. Gradient uses different functions to use a different number of bands depending on the orientation. The vertical orientation uses fewer bands resulting in noticeable banding while the horizontal orientation uses more bands thereby making the gradient band-less (much smoother.) Also, the difference in smoothness is more apparent when the gradient is blue than when it is grey.
736736
737737Continued on next post ...
738738
739739The **GetClipBox** example is meant to show that the mechanisms Windows uses to paint a window's client area changed after Windows XP. When run on WinXP the example will work as expected but won't on Vista and later versions UNLESS its compatibility setting has "Disable visual themes" checked.
740740
741741if "Disable visual themes" is not checked then GetClipBox will report "COMPLEX_REGION" whenever the window is partially visible and "NULL_REGION" when the window is fully covered by other windows.
742742
743743if "Disable visual themes" is checked then GetClipBox can also report "SIMPLE_REGION" if the window's visible area is a rectangle (which may be the entire client area or a portion of the client area.) NOTE that when "Disable visual themes" is checked the window caption will look like a Win95 caption instead of the stylish captions shown by Windows Vista and later.
744744
745745Given that it is unreasonable to require an application to only operate properly when "Disable visual themes" is checked, the current usefulness of GetClipBox is rather limited.
746746
747747GetClipBox is still useful, regardless of the "Disable visual themes" setting, to determine if a window is fully covered by other windows or not. When the window is fully covered by other windows then GetClipBox will always return "NULL_REGION", this can be useful to applications that update the contents of the client area on a regular basis, using a timer, such as the "performance" tab of the task manager (or the GetClipBox program itself.) If the window is fully covered then there is obviously no need to update the client area thus sparing Windows from wasting clock cycles to determine that whatever the application is painting should be ignored.
748748
749749The GetClipBox example also shows how to eliminate flicker by telling Windows the background has been erased and using the Rectangle API to erase only what is required of the background. The result, unlike in FlickerMin that uses the same method, is totally flicker free.
750750
751751In addition to that, it shows how to use BringWindowToTop and SetForegroundWindow to bring the main window to the foreground when a mouse click occurs in the popup's client area. To see this working, have some window cover the GetClipBox main window then click anywhere in the popup's client area. That will cause the main window to be placed back at the top of the Z order.
752752
753753NOTE: the popup's topmost attribute is set in CreateWindowEx.
754754
755755The **IsWindowFullyCovered** example is an "abbreviated" version of GetClipBox that simply reports if the window is fully covered or not.
756756
757757It is simply the "home" of a function "surprisingly" namedd IsWindowFullyCovered which can be copy/pasted into any other program that needs the functionality.
758758
759759The **GetParent** program is to show how the APIs GetParent, GetAncestor and GetWindow behave depending on the type of window (toplevel, child, popup) where it is being called. The MSDN documentation, while it tries hard, it doesn't make it easy to visualize what the result of those APIs is going to be depending on the type of window using it. The GetParent program _shows_ the result in each case.
760760
761761The GetParent program is actually three (3) programs in one. The first shows the results when the owner of the popup window is zero (0). That occurs when none of the {\$define} in the code are active/set. The second one shows the results when a grandchild window is set as the popup owner, controlled by the {\$define POPUP_OWNER_IS_GRANDCHILD}, the third one shows the results when the owner is set using the TopLevelWindow API, controlled by the {\$define POPUP_OWNER_IS_TOPLEVELWINDOW}.
762762
763763In each case note carefully, the values shown GetParent, GetWindow(..., GW_OWNER) and the values returned by the various GetAncestor options. Particularly when the popup's owner is the grandchild window.
764764
765765The GetParent example also shows how to pass a parameter to the WM_CREATE message using the lParam parameter of CreateWindow/CreateWindowEx (their last parameter.) GetParent uses only one (1) window procedure and only one class for the main window, child window, grandchild window and popup window. This causes Window's default window procedure to call the window procedure recursively for the creation of each window. The recursion is controlled and ended using the lParam which determine the type of window to create (main, child, grandchild or popup.)
766766
767767It also uses the WM_ACTIVATE message to make the three (3) windows to be brought up to the top of the Z order whenever either the main window or the popup window is activated. For this it uses SetWindowPos. It is worth noting that using SetWindowPos always works with the GUI windows but, for some unknown reason, does NOT always work with console windows (it only works _most_ of the time.) It does this using the following code:
768768
769769``` pascal
770770 WM_ACTIVATE:
771771 begin
772772 if LOWORD(wParam) <> 0 then { window is being activated }
773773 begin
774774 if Wnd = MainWnd then
775775 begin
776776 { the main window is being activated, make the popup window visible }
777777
778778 SetWindowPos(PopupWnd,
779779 HWND_TOP,
780780 0,
781781 0,
782782 0,
783783 0,
784784 SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
785785
786786 if ConsoleWnd <> 0 then
787787 begin
788788 { NOTE: with consoles SetWindowPos works only MOST of the time. }
789789 { the reason why it doesn't work all the time is unknown. }
790790
791791 SetWindowPos(ConsoleWnd,
792792 HWND_TOP,
793793 0,
794794 0,
795795 0,
796796 0,
797797 SWP_ASYNCWINDOWPOS or
798798 SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
799799 end;
800800
801801 exit;
802802 end;
803803
804804 if Wnd = PopupWnd then
805805 begin
806806 { the popup window is being activated, make the main window visible }
807807
808808 SetWindowPos(MainWnd,
809809 HWND_TOP,
810810 0,
811811 0,
812812 0,
813813 0,
814814 SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
815815
816816 if ConsoleWnd <> 0 then
817817 begin
818818 { NOTE: with consoles SetWindowPos works only MOST of the time. }
819819 { the reason why it doesn't work all the time is unknown. }
820820
821821 SetWindowPos(ConsoleWnd,
822822 HWND_TOP,
823823 0,
824824 0,
825825 0,
826826 0,
827827 SWP_ASYNCWINDOWPOS or
828828 SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
829829 end;
830830
831831 exit;
832832 end;
833833 end;
834834 end;
835835```
836836
837837Note that SWP_NOACTIVATE is specified in the call to SetWindowPos, failing to specify that flag will cause the main window and popup window to recursively activate each other, leading to an undesirable situation.
838838
839839Another "interesting" feature of GetParent is how it determines whether or not it should move the console window. If GetParent is started from a command line prompt, it will NOT move the console it was started from. However, if it was NOT started from a command line prompt, it creates its own console (automatically done by Windows because in that case GetParent is a console application) and proceeds to move it to uncover it (make it visible.) It makes the determination using the following code:
840840
841841``` pascal
842842 procedure PositionDebugConsole(Wnd : HWND);
843843 { positions the console window to ensure it is not obscured by this }
844844 { program's main window or its popup. NOTE: this procedure is NOT general }
845845 { it uses knowledge of the total height of the text output in the windows. }
846846 var
847847 ProcessCount : DWORD;
848848 ProcessList : array[1..10] of DWORD;
849849
850850 WindowRect : TRECT;
851851 ConsoleRect : TRECT;
852852
853853 begin
854854 { if the main window has not yet been created, there is no need to move the }
855855 { console window. }
856856
857857 if Wnd = 0 then exit;
858858
859859 { the main window exists, figure out if we need to move the console }
860860
861861 if GetConsoleWindow() = 0 then
862862 begin
863863 { there is no console, therefore there is nothing to position }
864864
865865 exit;
866866 end;
867867
868868 ZeroMemory(@ProcessList, sizeof(ProcessList));
869869
870870 ProcessCount := 0;
871871 ProcessCount := GetConsoleProcessList(@ProcessList, high(ProcessList));
872872
873873 if ProcessCount > 1 then
874874 begin
875875 { we were started from a pre-existing console, not from a console we own }
876876 { therefore, don't alter the position of the console, it would not be }
877877 { "polite" to move a pre-existing console. }
878878
879879 exit; { existing console remains whereever it currently is }
880880 end;
881881
882882 { we own the console, move it to be right next to our window and below the }
883883 { popup. }
884884
885885 ConsoleWnd := GetConsoleWindow(); { save the handle to the console }
886886
887887 GetWindowRect(Wnd, WindowRect);
888888 GetWindowRect(ConsoleWnd, ConsoleRect);
889889
890890 with ConsoleRect do
891891 begin
892892 MoveWindow(ConsoleWnd, { our dedicated console window }
893893 WindowRect.Right, { x snug to the main window }
894894 2 * LabelsHeight, { below the popup window }
895895 Right - Left, { no change in width }
896896 Bottom - Top, { no change in height }
897897 TRUE); { repaint it }
898898 end;
899899 end;
900900```
901901
902902It makes the determination using GetConsoleProcessList which will return a value greater than 1 if it was started from an existing console and 1 otherwise (it is a new console.)
903903
904904GetParent has another notable and unexpected "peculiarity". For some reason, the messages (hints) emitted by the compiler (v3.0.4) are off by one. Specifically, the line numbers are one greater than the real line number. This is probably the cause why when, in Lazarus, placing a breakpoint at some line, execution stops one line _after_ the breakpoint instead of the line that contains the breakpoint. Reason for this behavior is unknown but, it might be a bug in FPC v3.0.4.
905905
906906**WinAPI - EnumWindows**
907907
908908**WinAPI - EnumChildWindows**
909909
910910The EnumChildWindows program is _very close_ to the same program/example recently posted in another thread. However, there are two important differences.
911911
912912The first one is that one of the constants used by GetAncestor was misspelled, this didn't cause a problem because it was defined for Delphi v2.0 only and it was not used anyway.
913913
914914The second difference is that the original example did NOT account for Windows ever returning a handle whose value exceeds high(DWORD) (all window handles should fit in a DWORD even in a 64 bit installation because window handles can and are shared among 32bit and 64bit programs.) For some unknown reason, there are times when EnumWindows and EnumChildWindows will return a window handle that has the high DWORD set to \$FFFFFFFF. MS' spy++ also sees the presence of \$FFFFFFFF in the high DWORD thus proving it is not a result of a bug in the example.
915915
916916The presence of \$FFFFFFFF in the high DWORD is a problem in EnumWindows and EnumChildWindows because it messes up the alignment of the output in the listbox.
917917
918918This version of EnumChildWindows unceremoniously sets the high DWORD of a window handle to zeroes when necessary thus ensuring the window handle values never exceed high(DWORD) thus not messing up the alignment in the listbox.
919919
920920**ABOUT HINTS and UNUSED PARAMETERS**
921921
922922This set of programs aim to minimize the number of unwanted hints emitted by FPC, specifically those related to some variable not being initialized because it presumes that "var" parameters should be initialized and, those caused by unused parameters.
923923
924924Removing the unwanted hints about variables not being initialized due to parameters being "var" instead of "out" is solved by correcting the definition. This set of example uses corrected definitions whenever necessary.
925925
926926Removing the unwanted hints about parameters being unused is done using the following code:
927927
928928``` pascal
929929 {$ifdef FPC}
930930 procedure UNUSED_PARAMETER(UNUSED_PARAMETER : ptrint); inline; begin end;
931931 {$endif}
932932```
933933
934934and using that procedure to actually use the otherwise unused parameter, as in:
935935
936936``` pascal
937937 {$ifdef FPC}
938938 UNUSED_PARAMETER(lParam); { "declare" lParam as unused }
939939 {$endif}
940940```
941941
942942that will still cause one (1) hint to be emitted due to the procedure not using the parameter but, that procedure can be used for every unused parameter in the program (in a large program, there can be a good number of them) thus getting only one (1) hint instead of one per unused parameter.
943943
944944Additionally, since the procedure is inline and is empty, it generates no code.
945945
946946Bottom line: it is very similar to using UNREFERENCED_PARAMETER in C.
947947
948948**OTHER CONSIDERATIONS:**
949949
950950There are a good number of ways to avoid and/or minimize flicker. This set of examples will be followed by another set that uses other methods/techniques.
951951
952952## Post 8
953953
954954This post includes examples for a number of API functions and how they can be used to prevent/eliminate flicker. This is the second and last post related to preventing/eliminating flicker. Some of the examples are not directly about flicker, they are included because they are used in other examples that are about reverting/eliminating flicker.
955955
956956The examples are:
957957
958958- WinAPI - ExtTextOut (ExtTextOut in folder Ex1)
959959- WinAPI - CopyRect
960960- WinAPI - Rectangle
961961- WinAPI - WM_GETMINMAXINFO
962962
963963- WinAPI - ExtTextOut (ExtTextOut in folder Ex2)
964964- WinAPI - GetDC
965965- WinAPI - ReleaseDC
966966- WinAPI - OffsetRect
967967- WinAPI - CopyRect
968968- WinAPI - InflateRect
969969- WinAPI - InvalidateRect
970970- WinAPI - HeapAlloc
971971- WinAPI - GetCursorPos
972972- WinAPI - WindowFromPoint
973973- WinAPI - IsWindow
974974- WinAPI - UpdateWindow
975975- WinAPI - SetBkColor
976976
977977- WinAPI - InvalidateRect (in InvalidateRect)
978978- WinAPI - GetWindowRect
979979- WinAPI - OffsetRect
980980- WinAPI - GetDC
981981- WinAPI - GetDCEx
982982- WinAPI - SetROP2
983983- WinAPI - Rectangle
984984- WinAPI - ReleaseDC
985985- WinAPI - SetRectEmpty
986986- WinAPI - HeapAlloc
987987- WinAPI - CreatePen
988988- WinAPI - PostMessage
989989- WinAPI - SendMessage
990990- WinAPI - SetCapture
991991- WinAPI - ReleaseCapture
992992- WinAPI - SetWindowPos
993993- WinAPI - LockWindowUpdate
994994- WinAPI - UpdateWindow
995995- WinAPI - GetCursorPos
996996- WinAPI - IsWindow
997997- WinAPI - GetParent
998998- WinAPI - WindowFromPoint
999999- WinAPI - UpdateWindow
10001000
10011001- WinAPI - GetCharacterPlacement (GetCharacterPlacement in folder Ex1)
10021002- WinAPI - ExtTextOut
10031003- WinAPI - RtlZeroMemory
10041004
10051005- WinAPI - GetCharacterPlacement (GetCharacterPlacement in folder Ex2)
10061006- WinAPI - ExtTextOut
10071007- WinAPI - RtlZeroMemory
10081008
10091009- WinAPI - BitBlt (in BitBlt)
10101010- WinAPI - LoadBitmap
10111011- WinAPI - CreateCompatibleDC
10121012- WinAPI - GetObject
10131013- WinAPI - DeleteDC
10141014
10151015- WinAPI - StretchBlt (in StretchBlt)
10161016- WinAPI - LoadBitmap
10171017- WinAPI - CreateCompatibleDC
10181018- WinAPI - GetObject
10191019- WinAPI - DeleteDC
10201020
10211021- WinAPI - CreateCompatibleBitmap(in CreateCompatibleBitmap)
10221022- WinAPI - CreateCompatibleDC
10231023- WinAPI - DeleteDC
10241024- WinAPI - CreateFont
10251025- WinAPI - MoveWindow
10261026- WinAPI - GetSysColor
10271027- WinAPI - BitBlt
10281028
10291029- WinAPI - SelectClipRgn (in SelectClipRgn)
10301030- WinAPI - CreateFont
10311031- WinAPI - GetDC
10321032- WinAPI - ReleaseDC
10331033- WinAPI - BeginPath
10341034- WinAPI - EndPath
10351035- WinAPI - PathToRegion
10361036- WinAPI - GetRgnBox
10371037- WinAPI - OffsetRgn
10381038- WinAPI - ExtSelectClipRgn
10391039- WinAPI - SelectClipRgn
10401040
10411041- WinAPI - CallWindowProc (in ColorMixer)
10421042- WinAPI - CopyRect
10431043- WinAPI - CreateSolidBrush
10441044- WinAPI - CreateRectRgnIndirect
10451045- WinAPI - ExtSelectClipRgn
10461046- WinAPI - SelectClipRgn
10471047- WinAPI - ExtTextOut
10481048- WinAPI - FrameRect
10491049- WinAPI - GetWindowLongPtr
10501050- WinAPI - InvalidateRect
10511051- WinAPI - MoveWindow
10521052- WinAPI - Rectangle
10531053- WinAPI - SetFocus
10541054- WinAPI - SetScrollInfo
10551055- WinAPI - SetScrollPos
10561056- WinAPI - SetTextColor
10571057- WinAPI - TextOut
10581058- WinAPI - WM_CTLCOLORSCROLLBAR
10591059- WinAPI - WM_GETMINMAXINFO
10601060- WinAPI - WM_VSCROLL
10611061
10621062**WinAPI - ExtTextOut** (example 1 of 2)
10631063
10641064This first example of the ExtTextOut API is similar to the FlickerMin example in post 7 (see post 7 for the source.)
10651065
10661066It demonstrates ExtTextOut's ability to clip text to a rectangle. It clears the background areas as necessary when processing the WM_PAINT message instead of letting the default window procedure clear it when processing the WM_ERASEBKGND. It produces less flicker than FlickerMin because it uses a smaller font. (NOTE: the flicker is easier to see when resizing the window horizontally only.)
10671067
10681068Also note that it does not specify CS_HREDRAW nor CS_VREDRAW in the class styles, instead it invalidates the client area, _without erasing it_ when processing the WM_SIZE message.
10691069
10701070The rectangle coordinates used to erase the background can be output onto a console by recompiling the program with the "DEBUG" define active. This is useful to see/verify the coordinates of the background rectangles that are erased when painting.
10711071
10721072Use the options under the "ExtTextOut" menu to control the clipping of text.
10731073
10741074It also demonstrates how to process the WM_GETMINMAXINFO to control the minimum width and height of the window.
10751075
10761076**WinAPI - InvalidateRect**
10771077
10781078Carefully controlling which parts of the client area are invalidated can significantly reduce flicker.
10791079
10801080This example shows the effect of invalidating the entire client area compared to the effect of invalidating a smaller area that is the one that needs to be updated. When "Invalidate entire area" is selected in the "InvalidateRect" menu, the amount of flicker will be very noticeable. When "Invalidate coordinate area" is selected, the amount of flicker is drastically reduced and confined to a much smaller area.
10811081
10821082The flicker left is due to the use of TextOut and specifying TRUE in the call to InvalidateRect when processing the WM_MOUSEMOVE message:
10831083
1084-`         InvalidateRect(Wnd, @UpdateRect, TRUE);`
1084+```text
1085+ InvalidateRect(Wnd, @UpdateRect, TRUE);
1086+```
10851087
10861088This causes the coordinate area to be erased thus showing a clear background before TextOut outputs the new coordinate values.
10871089
10881090NOTE: All the coordinates shown are relative to the _client area_ of the respective windows.
10891091
10901092**WinAPI - ExtTextOut** (example 2 of 2)
10911093
10921094This example is the InvalidateRect example but with all flicker removed. The absence of flicker is due to the different way the coordinate rectangle is invalidated when processing the WM_MOUSEMOVE message:
10931095
1094-`         InvalidateRect(Wnd, @CoordinateRect, FALSE);  { NO background erase!    }`
1096+```text
1097+ InvalidateRect(Wnd, @CoordinateRect, FALSE); { NO background erase! }
1098+```
10951099
10961100Note, that InvalidateRect is instructed not to require the background to be erased by specifying FALSE (third parameter.) This combined with the fact that ExtTextOut repaints the background and the text in one operation eliminates all flicker in the coordinates area.
10971101
10981102However, it is still possible to see some occasional flicker when first pressing the left mouse button. The default processing of the WM_LBUTTONDOWN message Invalidates the entire client area with the statement:
10991103
1100-`             InvalidateRect(Wnd, nil, TRUE);`
1104+```text
1105+ InvalidateRect(Wnd, nil, TRUE);
1106+```
11011107
11021108This will cause some flicker to be noticeable if the left mouse button is very quickly pressed down and released (about 7 to 10 times per seconds) for a few seconds (5 seconds should normally enough.)
11031109
11041110That occasional flicker can be removed by selecting "Exclude bottom line from invalidated area" which will cause the call to InvalidateRect to be:
11051111
11061112``` Pascal
11071113 MenuItemState := GetMenuState(GetMenu(Wnd), IDM_EXCLUDE, MF_BYCOMMAND);
11081114
11091115 case MenuItemState <> 0 of
11101116 true :
11111117 begin
11121118 { menu item is checked, exclude the bottom of the client area }
11131119
11141120 GetClientRect(Wnd, ClientRect);
11151121
11161122 ClientRect.Bottom := ClientRect.Bottom - Exclude;
11171123 InvalidateRect(Wnd, @ClientRect, TRUE);
11181124 end;
11191125```
11201126
11211127thus excluding the area where the ExtTextOut definition is drawn causing all flicker to be eliminated.
11221128
11231129NOTE: the ExtTextOut attachment contains both examples.
11241130
11251131**WinAPI - GetCharacterPlacement** (example 1 of 2)
11261132
11271133ExtTextOut is a powerful function and when it is combined with GetCharacterPlacement its power surfaces.
11281134
11291135GetCharacterPlacement has a large number of options and the two examples presented here do not do it justice but, will give a _small_ taste of what it enables ExtTextOut to do. For all the available options, please see MSDN.
11301136
11311137This example simply takes a string and uses GetCharacterPlacement to determine how to "spread" it over a specified number of pixels. GetCharacterPlacement returns an array of inter-character distances that are used by ExtTextOut to display the string.
11321138
11331139**WinAPI - GetCharacterPlacement** (example 2 of 2)
11341140
11351141The first example above is a bit deceiving. The same example using a slightly different criteria allows converting a proportionally spaced font into a fixed space font. Normally, such a conversion is undesirable because the additional inter-character whitespace is unsightly but, there are exceptions and, this example shows one of them.
11361142
11371143One such case is when outputting the value of registers in a debugger. Most fixed fonts have digits that are not particularly easy to read whereas proportional fonts usually have aesthetically pleasing and easy to read digits.
11381144
11391145This example turns any hex number consisting of digits 0-F from proportional to fixed and, for comparison, outputs them as they would in their proportional state on the right hand side and their fixed state on the left hand side.
11401146
11411147NOTE: the GetCharacterPlacement attachment contains both examples.
11421148
11431149**WinAPI - BitBlt**
11441150
11451151Likely the most common method to eliminate flicker is to paint on a bitmap then bitblt the fully drawn bitmap onto the client area. This removes all flicker.
11461152
11471153This example is the simplest example of BitBlt-ing meant to serve as a pre-amble to a more sophisticated example that uses it to remove flicker.
11481154
11491155Note that this example does NOT use CS_HREDRAW nor CS_VREDRAW and that it uses a NULL_BRUSH as the background color, making it unnecessary to process the WM_ERASEBKGND to tell Windows the program erased the background when it actually didn't. Windows will erase the background with a NULL_BRUSH which does no erasing at all.
11501156
11511157**WinAPI - StretchBlt**
11521158
11531159StretchBlt is included in this set of examples only because it is very closely related to BitBlt. Since they are close cousins and it is immoral to break a family, they have been kept together as they should be.
11541160
11551161**WinAPI - CreateCompatibleBitmap**
11561162
11571163This example demonstrates double buffering. The top part of the window is drawn with TextOut (and optionally ExtTextOut by re-compiling - see source) thus causing flicker while, the bottom part of the window is first drawn on a bitmap then BitBlt-ed onto the bottom half of the window. The result is that the bottom part of the window is totally flicker free.
11581164
11591165To really appreciate the difference, resize the window. When resizing, the top part of the window will flicker significantly while the bottom part won't flicker at all.
11601166
11611167**WinAPI - SelectClipRgn**
11621168
11631169This example demonstrates how to use regions to eliminate flicker. Note that it does NOT use double buffering, only regions.
11641170
11651171It creates a region that is excluded from the "paintable" region when drawing the background gradient. After the background gradient has been painted then that region is made the "paintable" region and a different gradient is painted in it.
11661172
11671173The inclusion and exclusion of the region is controlled by the options under the "Flicker" menu. When the region is excluded, there will be no flicker and, when it is included, flicker will be noticeable when resizing the window.
11681174
11691175**Utility - ColorMixer**
11701176
11711177This example which doubles as a simple color mixing/selection utility demonstrates how to eliminate flicker using ExtTextOut, Regions and InvalidateRect.
11721178
11731179It uses all the methods to prevent flicker except double buffering.
11741180
11751181In addition to that, it demonstrates how to use scroll bars as controls, how to subclass a window (in this case the scroll bars), how to handle keyboard input to set and select which scroll bar has the focus, as well as, setting the numeric value for each color (use arrow keys, home/end, page up/down), and how to process the WM_GETMINMAXINFO to set the minimum, maximum and maximized sizes of a window.
11761182
11771183Aside from the 3 scroll bars that control the settings of the Red, Green and, blue colors, there is only _one_ window. The right and left hand side are _not_ separate windows.
11781184
11791185The example provides the option of displaying the coordinates of the various rectangles used to eliminate flicker by enabling the SHOW_RECTANGLE_COORDINATES define.
11801186
11811187It also allows controlling whether or not the sample text will flicker by enabling or disabling the define NO_SAMPLE_TEXT_FLICKER.
11821188
11831189The numeric values of Red, Green and Blue shown below the scroll bars are _not_ protected against flicker. Grabbing any of the scroll bar thumbs and moving it up and down quickly for several seconds while paying close attention to the numbers will make a very slight amount of flicker visible. Because the amount of flicker is usually not noticeable, there is no code to prevent it. However, the same method used to ensure the sample text does not flicker can be used with the Red, Green and Blue values for a totally flicker free utility.
11841190
11851191ColorMixer uses its own definitions for most of the APIs it uses. The purpose of the definitions is to minimize the number of useless hints and give better parameter names, which are used by CODETOOLS, thereby providing better guidance when programming.
11861192
11871193![](/wiki-images/7/7b/Winapi_ColorMixer.png)
11881194
11891195**Dead BUG**
11901196
11911197There is a "dead" bug in DrawWindowFrame. That function appears in multiple examples presented in Post 6. The bug is as follows:
11921198
11931199``` Pascal
11941200 SelectObject(dc, OldPen); { restore the original pen }
11951201 ReleaseDC(Wnd, dc);
11961202 DeleteObject(Pen); { get rid of the pen }
11971203
11981204 {---------------------------------------------------------------------------}
11991205 { release the window dc }
12001206
12011207 ReleaseDC(dc, Wnd);
12021208 end;
12031209```
12041210
12051211The last statement "ReleaseDC(dc, Wnd);" is erroneous for two reasons. First, the dc has already been released (just a few lines above) and second, the parameters in that call are reversed. The erroneous statement has no effect on the proper operation of the examples in which it appears but should be removed. Corrected code should read:
12061212
12071213``` Pascal
12081214 SelectObject(dc, OldPen); { restore the original pen }
12091215 ReleaseDC(Wnd, dc);
12101216
12111217 DeleteObject(Pen); { get rid of the pen }
12121218 end;
12131219```
12141220
12151221The examples affected by the above bug are :
12161222
12171223- GetDesktopWindow.lpr
12181224- GetShellWindow.lpr
12191225- GetTaskmanWindow.lpr
12201226- GetTopLevelWindow.lpr
12211227- IsTopLevelWindow.lpr
12221228- GetWindowTextLength.lpr
12231229
12241230Corrected versions of the source files above are attached to this post in the file named "CorrectionsToPost6Examples.7z" (NOTE: only examples presented in Post 6 are affected.)
12251231
12261232## Post 9
12271233
12281234(to be filled later)
12291235
12301236---
12311237*From the Free Pascal wiki, page "Windows API examples", as it stood on 2022-03-20 after a change by Alextp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
12321238Category: Code
12331239Category: Example programs
12341240Category: FPC
12351241Category: Windows

Back to history