Difference between revisions of "Windows API examples"

From Lazarus wiki
Jump to navigationJump to search
Line 432: Line 432:
  
 
=Post 5=
 
=Post 5=
 +
This post includes examples for the following APIs :
 +
 +
'''WinAPI - GetFileSize'''
 +
 +
'''WinAPI - GetFileSizeEx'''
 +
 +
These 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.
 +
 +
 +
'''WinAPI - GetSystemInfo'''
 +
 +
'''WinAPI - GetNativeSystemInfo'''
 +
 +
These two functions are very similar, almost identical.  The critical difference is in how the Processor Architecture is reported.
 +
 +
Presuming 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.
 +
 +
In spite of that, MS documentation claims that using GetNativeSystemInfo is _not_ a reliable method to determine the O/S bitness.
 +
 +
 +
'''WinAPI - GetVersion'''    (deprecated)
 +
 +
'''WinAPI - GetVersionEx''' (deprecated)
 +
 +
'''WinAPI - RtlGetNtVersionNumbers''' (undocumented)
 +
 +
Both 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.
 +
 +
On the other hand, RtlGetNtVersionNumbers reports the _true_ and accurate Windows version.
 +
 +
The function (in NTDLL) is as simple as can, and should be:
 +
<syntaxhighlight lang="asm">
 +
                            public RtlGetNtVersionNumbers
 +
    RtlGetNtVersionNumbers proc near
 +
   
 +
                    test    rcx, rcx            ; ensure pointer not nil
 +
                    jz      short loc_78E9517B
 +
                    mov    dword ptr [rcx], 6
 +
   
 +
    loc_78E9517B:
 +
                    test    rdx, rdx            ; ensure pointer not nil
 +
                    jz      short loc_78E95186
 +
                    mov    dword ptr [rdx], 1
 +
   
 +
    loc_78E95186:
 +
                    test    r8, r8              ; ensure pointer not nil
 +
                    jnz    loc_78EB7C9D        ; see below
 +
                    rep retn
 +
    RtlGetNtVersionNumbers endp
 +
   
 +
    loc_78EB7C9D:
 +
                    ; F000 -> free build
 +
                    ; 1DB1 -> build number
 +
   
 +
                    mov    dword ptr [r8], 0F0001DB1h
 +
                    retn
 +
</syntaxhighlight>
 +
 +
Before 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.
 +
 +
The 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.
 +
 +
As 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.
 +
 +
The 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.
 +
 +
For 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.
 +
 +
 +
The 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.
 +
 +
If 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.
 +
 +
If 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
 +
 
=Post 6=
 
=Post 6=
 
=Post 7=
 
=Post 7=

Revision as of 12:19, 13 March 2021

This is a collection of Lazarus forum posts from user 440bx, about Windows API examples. Source codes were collected in GutHub.

Post 1

Attached 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.

The 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.

I 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:

ExpandEnvironmentStrings

This 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.

Roughly (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.)


SetConsoleWindowInfo

The 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.)

The 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.

Roughly, 10% of the examples fall in that category (API operation visualization).

I won't go into a detailed explanation at this time. It will be included along with the program source when it is its turn.


ExtTextOut (two (2) examples)

InvalidateRect

SelectClipRgn

These 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.

The 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.


ExtTextOut (v1)

That 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.


ExtTextOut (v0)

This 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.


SelectClipRgn

This 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".


Window Bitness

Consider a program, let's call it program "A" that can be compiled as a 32 bit and 64 bit versions.

The 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.

The 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.

MS 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.

There 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.

Using 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.

Using 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.

All 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!

That's what the program below does (full source in the attachment)

On 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.

On 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. :)


My 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.

NOTE: There is an example dedicated to LoadLibraryEx by itself which shows how it behaves under various circumstances.


About 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.

I 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.

Post 2

The 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.

Amond the APIs that should be avoided and preferably never used are:

  • GlobalCompact
  • GlobalFix
  • GlobalFlags
  • GlobalHandle
  • GlobalLock
  • GlobalReAlloc
  • GlobalSize
  • GlobalUnfix
  • GlobalUnlock
  • GlobalUnWire
  • GlobalWire
  • LocalAlloc
  • LocalCompact
  • LocalFlags
  • LocalFree
  • LocalHandle
  • LocalLock
  • LocalReAlloc
  • LocalShrink
  • LocalSize
  • LocalUnlock

All 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.

As mentioned previously, no example for any of the above APIs will be given.

GlobalAlloc

GlobalFree

These two (2) APIs are still commonly found in C programs but, are not as flexible as HeapAlloc() and HeapFree() which should be preferred.

No example of GlobalAlloc and GlobalFree will be given. An example of HeapAlloc and HeapFree will be given in the future.


GlobalMemoryStatus

GlobalMemoryStatus returns incorrect information on systems with more than 4GB of memory. Use GlobalMemoryStatusEx instead.

An example for both will be given in the future.


GetTextExtentPoint

Use GetTextExtentPoint32 instead which, according to MS, is more accurate.


WinAPI - GetTextExtentPoint (gdi)

WinAPI - GetTextExtentPoint32 (gdi)

Both of these APIs return the width and height in pixels of a specified string


WinAPI - SetBkMode (gdi)

Sets the background color to opaque or transparent. When the background is opaque then whatever color was chosen as background will be visible.


WinAPI - SetTextAlign (gdi)

Sets the text alignment. This example visually demonstrates the effects of some of the SetTextAlign options. See MSDN for all available options.


WinAPI - TextOut (gdi)

Outputs a string at the specified location, using the currently selected font, background color, and text color.


WinAPI - GetClientRect (user)

Retrieves 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.

NOTE: this example does not include code to eliminate or minimize flicker,which would not be hard to implement (left for future example.)


WinAPI - Windows Bitness (technique)

This 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.

recommendation : replace the previous version with this version.


What follows is the code for the GetClientRect example (the source for the remaining examples is present in the zip file attached to this post.)

Post 3

Here are some examples of simple, yet often very useful API functions.

WinAPI - DebugBreak

This example demonstrates using DebugBreak() to transfer control to a debugger. The example should be executed both, under a debugger and directly.

This example also serves to demonstrate how a program can detect if it is running under a debugger without using the IsDebuggerPresent() API.

Use the "DebugBreak()" menu item to cause the program execution to fall under debugger control. Use F7/F8/F9 to control execution.

DebugBreak() is very useful to create conditional breakpoints with very complex conditions that are beyond the capabilities of an application debuggers.

Also, 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.


In 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.

DebugBreak() 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.

(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.

To 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. :)

This 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.)


WinAPI - IsDebuggerPresent

This 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.

It 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().

When 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.

IsDebuggerPresent() 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.)


WinAPI - QueryPerformanceCounter

WinAPI - QueryPerformanceFrequency

QueryPerformanceCounter along with QueryPerformanceFrequency can be used to measure elapsed time very accurately. That's the combined purpose of these two APIs.

To determine elapsed time in seconds, simply subtract the first query from the second query and divide the result by the frequency.

The 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)


WinAPI - SetLastError

SetLastError 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.)

Also, 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.

Fun experiment :

1. Start the SetLastError example

2. Select "LoadLibrary failure" from the "SetLastError" menu

3. Notice the value of the error (should be $A1)

4. Select "About SetLastError" from the "Help" menu

5. Click the OK button on the About box

6. Notice the value of the error (should still be $A1

Now repeat step 1 through 4 above

5. Move the About box vertically a couple of pixels up and click the OK button

6. Notice the value of the error (should be $0 instead of $A1)

7. Why did moving the About box cause the value of SetLastError to change ?


WinAPI - Sleep

The MSDN documentation states quite a few things about the Sleep API. This test program demonstrates what _actually_ happens when the sleep interval changes.

RECOMMENDATION : _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.

To 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.

Post 4

This post includes examples for the following APIs :

WinApi - CreateStatusWindow (example is named StatusBar)

This 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.

The 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.

To 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.

In 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.

Under 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.


WinAPI - GetEnvironmentStrings

This 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.

NOTE: 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.

Future examples will show how to calculate the horizontal extent properly.

Also, 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.

This 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.

Also, 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.


WinAPI - FreeEnvironmentStrings

This example calls the GetEnvironmentStrings API immediately followed by a call to FreeEnvironmentStrings.

In 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.

In other versions of Windows, FreeEnvironmentStrings does free a memory block that contains the environment strings.


WinAPI - ExpandEnvironmentStrings

This example shows how to retrieve the value of a known environment string.


WinAPI - GetModuleHandle

This example shows how to call GetModuleHandle to retrieve the load address of modules in the process including the .exe itself.

It 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.

It 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.

Note 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.)


WinAPI - GetModuleHandleEx

This 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.

This ability can often be useful to debuggers.

This example also demonstrates how to "pin" a dll in memory, that is, ensuring that the dll is not unloaded at an undesirable time.

There are, at least, two cases when that capability is useful/required.

The 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.

The 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.

Pinning down a dll prevents this very undesirable situation from taking place.

To 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 :

1. After starting the GetModuleHandleEx example, start the Module32First program.

2. Search and select "GetModuleHandleEx.exe" on the left hand side pane of Module32First

3. Note the values of "USG" and "usg" for the "GetModuleHandleExDll" (they should both be two (2)

4. Select "Pin and attempt to unload the dll" from the "GetModuleHandleEx" menu.

5. Select "Update snapshot" in the Module32First program.

6. Repeat steps 2 and 3 above. The values under "USG" and "usg" should read 65535 indicating that the dll is pinned.

Every 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.

The interesting code portions of this program are:

      case Msg of
        WM_CREATE:
        begin
          { load the supporting dll                                               }
     
          ModuleAddressA := LoadLibrary(DllToLoad);
     
          FunctionAddress := GetProcAddress(ModuleAddressA,
                                            pchar(DLL_FUNCTION_INDEX));
     
          { get the module address again but this time using the function address }
     
          if not GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                                   FunctionAddress,
                                   ModuleAddressB) then
          begin
            LastError := GetLastError();
     
            MessageBox(Wnd,
                      'GetModuleHandleEx call failed in WM_CREATE',
                      'Main Window',
                       MB_ICONERROR or MB_OK);
     
            WndProc := -1;
     
            exit;
          end;
     
          { format and save the values obtained                                   }
     
          StrFmt(Values[1], ' %p', [FunctionAddress]);
     
          { the next two addresses should be the same                             }
     
          StrFmt(Values[2], ' %p', [pointer(ModuleAddressA)]);
          StrFmt(Values[3], ' %p', [pointer(ModuleAddressB)]);
     
          exit;
        end;


On 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)

The other interesting part is :

          case LOWORD(wParam) of
            IDM_BYADDRESSANDPIN:
            begin
              if not GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN,
                                       DllToLoad,
                                       ModuleAddressB) then
              begin
                LastError := GetLastError();
     
                MessageBox(Wnd,
                          'GetModuleHandleEx call failed in IDM_BYADDRESSANDPIN',
                          'Main Window',
                           MB_ICONERROR or MB_OK);
     
                WndProc := -1;
     
                exit;
              end;
     
              for i := 1 to 100 do FreeLibrary(ModuleAddressB);
     
            end;


On 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.


WinAPI - Module32First

For the time being, this example is only included to help visualize what GetModuleHandleEx does. Its source code will be included in a future example.

Post 5

This post includes examples for the following APIs :

WinAPI - GetFileSize

WinAPI - GetFileSizeEx

These 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.


WinAPI - GetSystemInfo

WinAPI - GetNativeSystemInfo

These two functions are very similar, almost identical. The critical difference is in how the Processor Architecture is reported.

Presuming 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.

In spite of that, MS documentation claims that using GetNativeSystemInfo is _not_ a reliable method to determine the O/S bitness.


WinAPI - GetVersion (deprecated)

WinAPI - GetVersionEx (deprecated)

WinAPI - RtlGetNtVersionNumbers (undocumented)

Both 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.

On the other hand, RtlGetNtVersionNumbers reports the _true_ and accurate Windows version.

The function (in NTDLL) is as simple as can, and should be:

                             public RtlGetNtVersionNumbers
    RtlGetNtVersionNumbers proc near
     
                    test    rcx, rcx            ; ensure pointer not nil
                    jz      short loc_78E9517B
                    mov     dword ptr [rcx], 6
     
    loc_78E9517B:
                    test    rdx, rdx            ; ensure pointer not nil
                    jz      short loc_78E95186
                    mov     dword ptr [rdx], 1
     
    loc_78E95186:
                    test    r8, r8              ; ensure pointer not nil
                    jnz     loc_78EB7C9D        ; see below
                    rep retn
    RtlGetNtVersionNumbers endp
     
    loc_78EB7C9D:
                    ; F000 -> free build
                    ; 1DB1 -> build number
     
                    mov     dword ptr [r8], 0F0001DB1h
                    retn

Before 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.

The 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.

As 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.

The 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.

For 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.


The 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.

If 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.

If 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

Post 6

Post 7