Lazarus

Free Pascal => Windows => Topic started by: 440bx on January 08, 2021, 09:53:57 pm

Title: Windows API test programs / Examples
Post by: 440bx on January 08, 2021, 09:53:57 pm
Hello,

During years of programming, I've accumulated a fairly large number of programs whose purpose is to test the operation of specific Windows APIs.  All of the programs are pure Windows API, that is, no objects/classes and independent of Delphi/Lazarus.  IOW, programs along the lines of what is presented in Petzold's Programming the Windows API but, in Pascal.

The great majority of the programs, likely around 95% are very simple, even trivial, programs meant to show how to correctly build the API call and to occasionally be used as the foundation for further tests (such as performance.)   About 5% of the programs are more sophisticated and, are not API specific but goal/technique specific instead.  Such as, how to avoid/eliminate flicker using various methods, implementing the functionality of undocumented APIs using documented APIs and many other unrelated purposes that are often useful in one way or another.

Through the years, I have accumulated about 700+ such (Pascal) programs.  _Important note_ : these programs are _not_ programming examples, they are API _test_/example programs.  This means that almost all of them use global variables and other ways of getting things done that are fine in throw away code but, would be questionable and, sometimes even unacceptable, in production quality code.

I am offering these test programs mostly as a way to learn the Windows API.  A few, very few (maybe 5%), can be used as programming Windows examples (IOW, the programming is halfway decent.)

The point of this post is two fold:

First, find out if the community is interested in these API test programs.

If there is interest then, I'd post _about_ 5 of them on a daily basis.  One short post per API test program and, proceed from simplest (usually downright trivial) to more complex as the number of post increases with a few exceptions along the way to keep things interesting.

Second if there is interest then I'd like the moderators to confirm that my creating about 5 individual posts every day is acceptable (possibly fewer on some days but never larger.)

If there is interest and approval from the moderators, I'd start about a week from today, this to give me a little time to organize the programs.

All feedback welcome,

Thank you.
Title: Re: Windows API test programs / Examples
Post by: trev on January 08, 2021, 10:45:15 pm
Your Windows API test programs sound really useful, but it may be better to put them up on your favourite repository (Sourceforge, Github, GitLab etc) and add a link in the forum and the Wiki.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 08, 2021, 11:09:25 pm
Thank you Trev.

I am usually not fond of sharing my code, for that reason, sourceforge, github, etc, are not appealing avenues to me. 

That said, I thought that if FPC forum members were interested then I would share it here and, since this is s forum, answer any questions that may arise.  Given that all programs are pure API, I didn't expect much interest but, I figured just in case, I'd offer.

However, I appreciate your suggestion, which admittedly is probably the "natural" solution. 
Title: Re: Windows API test programs / Examples
Post by: Martin_fr on January 08, 2021, 11:44:03 pm
May I make a suggestion, maybe towards a middle ground.

Since you want to post an article with the sources, yes that can be done on the forum.
I would suggest the "Programming / os / win" board. Or 3rd party announce.

However, it probably be best to collect several posts into one thread. If you had a 100 samples (no idea how many you have) grouping an average of 5 per day would still be 20 topics. But IMHO ok (well to much for the announcment board). If you have 1000 then maybe grouping several days....

The announcement board should be shared fairly. Max 2 or 3 threads per user per month. But as many posts as you like, inside those threads.

In the programming/os/win, I think it is ok. I would suggest that if several daily topics do not attract lots of answers, then they could be merged by an admin to become one thread.

About a repro to share source, I am not sure if by "not appealing" you mean
- you do not want the sources to end up there.
- you do not want to go into the trouble to put them there.

If the latter, then maybe someone  can volunteer to upload them in groups of 5 a day?

Also, it may be easier than you think. Github allows you to upload via web interface.
- Get an account
- Create a repro
- Go to upload files
- drag and drop each folder that you want to upload (that way the folder is created in the repro)
- add a commit message
- commit


Having the sources avail on a repro would also, if interest is big, make it easier to collect info on the wiki. (again could be done by a volunteer, if alright with you, and someone wants to do that)
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 09, 2021, 12:01:23 am
Thank you Martin.

The programming / os / win does sound like it would be the best suited forum.  I estimate that I have somewhere between 700 and 800 examples/test programs.

The reason I thought about doing one post per API was to make it easy to find.  I would title each post along the lines of "Win API example - api name" which I believe would make it easy to find using the forum's search engine.  Combining multiple APIs into one post could make finding the example for a specific API more difficult for the interested party.

I don't find the repositories attractive because they are very static.  The great advantage of the forum is that they are interactive, every one can participate either with questions, suggestions and/or improvements. 

The above is just to show where I'm coming from.  I appreciate your suggestions.

So far, the level of interest does not seem to be high enough to even merit concern on how to go about it.

Title: Re: Windows API test programs / Examples ()
Post by: Martin_fr on January 09, 2021, 12:16:25 am
The programming / os / win does sound like it would be the best suited forum.  I estimate that I have somewhere between 700 and 800 examples/test programs.
700 topics would mean anyone just browsing the board would see nothing else.
Even 140 (700 div 5) is way too much (unless they really all attract big exchange of messages).

The reason I thought about doing one post per API was to make it easy to find.  I would title each post along the lines of "Win API example - api name" which I believe would make it easy to find using the forum's search engine.  Combining multiple APIs into one post could make finding the example for a specific API more difficult for the interested party.
An idea would be to have on overview topic, in which to post the name and link (to the post inside a thread) for each example. That would help finding them. You can keep that topic locked, so only you can write to it (you may need to unlock it to edit it, not sure).
(It may need updating, if topics need to be split/merged)

Each post in a topic can have a modified subject.

If a specific example takes on an extraordinary amount of discussion, then you can pm me, and I can split the topic.

I don't find the repositories attractive because they are very static.  The great advantage of the forum is that they are interactive, every one can participate either with questions, suggestions and/or improvements. 

Well you can disable all feedback on github, and include a readme that points to the forum as place for discussion.
Title: Re: Windows API test programs / Examples ()
Post by: 440bx on January 09, 2021, 01:34:41 am
700 topics would mean anyone just browsing the board would see nothing else.
Even 140 (700 div 5) is way too much (unless they really all attract big exchange of messages).
that's definitely a valid concern, which is why I thought about limiting the number of posts to a maximum of 5 per day, to avoid "drowning" other posts in the target forum.

An idea would be to have on overview topic, in which to post the name and link (to the post inside a thread) for each example. That would help finding them. You can keep that topic locked, so only you can write to it (you may need to unlock it to edit it, not sure).
(It may need updating, if topics need to be split/merged)
I figured we could try 5 posts a day in a particular forum (the one you suggested sounds very appropriate) and if that creates a problem then address whatever problem arises (if any) with a solution tailored for it.

Well you can disable all feedback on github, and include a readme that points to the forum as place for discussion.
That is a possibility but, since the examples would be on github (or some other repo) it would result in a lack of context (the API in one place - the repo - and, its discussion in another - the forums here.) 

I believe the greatest value is obtained by open discussions on the topic in one place.

Anyway, at least so far, it doesn't seem like there is much interest in the examples.  IOW, so far, it's a non-problem. <chuckle>
Title: Re: Windows API test programs / Examples
Post by: trev on January 09, 2021, 02:00:37 am
> Anyway, at least so far, it doesn't seem like there is much interest in the examples.

Build it and they will come :) Seriously, your examples sound like valuable content that should be preserved for posterity and shared with the Pascal populace.

In my past experience, relying on a Forum for longevity of preservation does not work. While I don't expect this Forum to disappear which has happened to me in the past with JAL, I've seen Forum software changes in the past which have "lost" the previous content, but perhaps the biggest disadvantage is that the content becomes scattered and hard to find once it's been buried in several hundred/thousand posts.

The Wiki is a good place to collect the examples and document them. Discussion can then be either linked back to the Forum (as is already done in some cases) or held on the Discussion page assuming there's an Overview page which lists them all with links to each one on its own page. Yes, it sounds like work :) I could help.

Anyway, what you do is, of course, up to you.
Title: Re: Windows API test programs / Examples
Post by: lucamar on January 09, 2021, 02:24:34 am
I was thinking about this (and yes, I'm also very interested: I'm almost completely out of touch with the Windows API :-[) and it really sounds as a subject for a whole (if small) new site.

The problem, as Martin said already, is that here it would be easy to "drown" other posts under yours (unless it's just one or a few topics listing related examples), more so in any of the "specialties" boards which see relatively little traffic.

Of course, builiding a whole new site has its own problems too (mainly that "someone" has to maintain it ;)) but it might be worth at least thinking about it: "Pascal and the WAPI!"

But however you do it (if you do it) I'll surely collect both the examples and any discussion about them. Would be a nice addition to my Pascal "'ciclopedic notes"*


* ... so called; really just a bunch of badly organized text files, saved web pages, zipped examples,etc. :-X
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 09, 2021, 08:39:29 am
Build it and they will come :)
I think you're quite right about that.  I'm going to prepare a few "teaser" examples in the next few days.  That might raise interest.  "Seeing is coveting"  ;)

In my past experience, relying on a Forum for longevity of preservation does not work.  <snip>  perhaps the biggest disadvantage is that the content becomes scattered and hard to find once it's been buried in several hundred/thousand posts.
I'm not concerned about the longevity.  I presume that if members find the examples useful they will be remain, if not they'll be dropped or archived away as they eventually should be.

The Wiki is a good place to collect the examples and document them. Discussion can then be either linked back to the Forum (as is already done in some cases) or held on the Discussion page assuming there's an Overview page which lists them all with links to each one on its own page. Yes, it sounds like work :) I could help.
I definitely appreciate the offer to help, particularly considering that I'm not particularly good at structuring and formatting text.  I'm much better at casual explanations (like those found in forum posts. ;) )

Anyway, what you do is, of course, up to you.
Thank you, my "greatest" concern is to ensure I don't disrupt the smooth operation of the forum by, for instance, "drowning" other posts.



I was thinking about this (and yes, I'm also very interested:
Adding one (1) to the "interested count". :)

Of course, builiding a whole new site has its own problems too (mainly that "someone" has to maintain it ;))
I just don't have the talent nor ability to format a bunch of text.  My artistic abilities are at the low end of the Kelvin scale.
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 13, 2021, 05:37:43 pm
This will be a great resources, 440bx!

I need to refer to it for my "basic compiler". I have come up with ReadConsole and WriteConsole (both prefixed with GetStdHandle) but I need more, such as MsgBox and wsprintf??

Will your example include which library(kernel32.dll, user32.dll) in use?

Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 13, 2021, 05:46:08 pm
Adding one (1) to the "interested count". :)

Count me in.

I just don't have the talent nor ability to format a bunch of text.  My artistic abilities are at the low end of the Kelvin scale.

I will try and see if I can help..... With some basic HTML5 +CSS3 skill. Hopefully most part of the text formatting can be automated using Pascal program itself?  8-)
Title: Re: Windows API test programs / Examples
Post by: marcov on January 13, 2021, 05:49:41 pm
As maintainer of the windows api headers, count me in too.
Title: Re: Windows API test programs / Examples
Post by: Seenkao on January 13, 2021, 08:07:58 pm
Добрый вечер!
То, чем вы хотите поделиться, я думаю, будет полезно многим. Но на форуме, если данная тема/ветка потеряются. А это будет не очень хорошо...
Так же будет и везде: Git, SourceForge и др...

Тему надо периодически обновлять, чтобы она не забывалась людьми. Потому что полезно это может быть и через несколько десятков лет будет.

Eng:
Good evening!
What you want to share, I think, will be useful to many. But on the forum, if this topic / thread is lost. And it won't be very good ...
It will be the same everywhere: Git, SourceForge, etc.

The topic should be periodically updated so that it is not forgotten by people. Because it can be useful and it will be in a few decades.  :)
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 13, 2021, 09:51:38 pm
Honestly, since the level of interest seemed to be modest at best so far, I thought about letting this offer "die" silently.  Given that today an additional number of forum members have expressed interest, I've decided to go ahead and post the examples.

Sometime between today and tomorrow, I'll post a few "teaser" examples. "teaser" because, I'll include the source code only for _one_ of the examples and the remaining examples will hopefully wet the appetite of additional forum members.



Will your example include which library(kernel32.dll, user32.dll) in use?
Examples include mostly, ntdll.dll, kernel32.dll, user32.dll and gdi.dll.  There are examples for other dlls but those mentioned make up the great majority of them.



As maintainer of the windows api headers, count me in too.
good :)

About API headers, I have several hundred definitions of APIs and constants but, they are currently scattered in multiple files (used by examples and programs), I will try to consolidate them to make them useful to you.  However, there is a caveat, I organize all definitions by the dll that depends on it.  For instance, the Windows message definitions are in user32.dll instead of in a "messages" unit.  IOW, for them to be useful to you, you'll have to reorganize them as required by FPC.



What you want to share, I think, will be useful to many. But on the forum, if this topic / thread is lost. And it won't be very good ...

The topic should be periodically updated so that it is not forgotten by people. Because it can be useful and it will be in a few decades.  :)
My "plan" is to name each post "Win API - api name", this way if someone uses the forum search engine, they will always be able to find it even if no one has updated it in a long time (there is only so much that can be said about any one API function.)



Time for me to prepare those "teaser" examples. :)

Title: WinAPI - Windows bitness & Teaser programs
Post by: 440bx on January 14, 2021, 06:14:21 pm
Hello,

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)
Code: Pascal  [Select][+][-]
  1. {$define         DEBUG}     { to cause a breakpoint in WM_CREATE handler      }
  2.  
  3. {$APPTYPE        GUI}
  4.  
  5. {$LONGSTRINGS    OFF}
  6. {$WRITEABLECONST ON}
  7. {$DESCRIPTION    'Win32 API function - WindowsBitness example'}
  8.  
  9. {$R WindowsBitness.Res}
  10.  
  11. program _WindowsBitness;
  12.   { Win32 technique - WindowsBitness example                                  }
  13.  
  14. uses Windows,
  15.      Messages,
  16.      Resource
  17.      ;
  18.  
  19. const
  20.   AppNameBase  = 'WindowsBitness';
  21.  
  22.   {$ifdef WIN64}                         { heading for 64 bit                 }
  23.     Bitness64  = ' - 64bit';
  24.     AppName    = AppNameBase + Bitness64;
  25.   {$else}                                { heading for 32 bit                 }
  26.     Bitness32  = ' - 32bit';
  27.     AppName    = AppNameBase + Bitness32;
  28.   {$endif}
  29.  
  30.   AboutBox     = 'AboutBox';
  31.   APPICON      = 'APPICON';
  32.   APPMENU      = 'APPMENU';
  33.  
  34. {-----------------------------------------------------------------------------}
  35.  
  36. {$ifdef VER90} { Delphi 2.0 }
  37. type
  38.   ptrint  = longint;
  39.   ptruint = dword;
  40. {$endif}
  41.  
  42. {$include ImageHeaders.inc}
  43.  
  44. function IsDebuggerPresent                       { missing in Delphi 2        }
  45.          : BOOL; stdcall; external 'kernel32';
  46.  
  47.  
  48. {-----------------------------------------------------------------------------}
  49.  
  50. function About(DlgWnd : hWnd; Msg : UINT; wParam, lParam : ptrint)
  51.          : ptrint; stdcall;
  52. begin
  53.   About := ord(TRUE);
  54.  
  55.   case Msg of
  56.  
  57.     WM_INITDIALOG: exit;
  58.  
  59.     WM_COMMAND:
  60.     begin
  61.       if (LOWORD(wParam) = IDOK) or (LOWORD(wParam) = IDCANCEL) then
  62.       begin
  63.         EndDialog(DlgWnd, ord(TRUE));
  64.  
  65.         exit;
  66.       end;
  67.     end;
  68.   end;
  69.  
  70.   About := ord(FALSE);
  71. end;
  72.  
  73. {-----------------------------------------------------------------------------}
  74.  
  75. function GetModuleBitness(Module : HMODULE) : integer;
  76. var
  77.   DosHeader      : PIMAGE_DOS_HEADER;
  78.   NtHeader       : PIMAGE_NT_HEADERS;
  79.   OptionalHeader : PIMAGE_OPTIONAL_HEADER;
  80.  
  81. begin
  82.   result := 0;
  83.  
  84.   HMODULE(DosHeader) := Module;
  85.  
  86.   { ensure we got a valid PE file                                             }
  87.  
  88.   if IsBadReadPtr(DosHeader, sizeof(DosHeader^))           then exit;
  89.   if DosHeader^.Signature <> IMAGE_DOS_SIGNATURE           then exit;
  90.  
  91.   pointer(NtHeader) := pchar(DosHeader) + DosHeader^.OffsetToNewExecutable;
  92.  
  93.   if IsBadReadPtr(NtHeader, sizeof(NtHeader^))             then exit;
  94.  
  95.   OptionalHeader := @NtHeader^.OptionalHeader;
  96.  
  97.   if IsBadReadPtr(OptionalHeader, sizeof(OptionalHeader^)) then exit;
  98.  
  99.   case OptionalHeader^.Magic of
  100.     IMAGE_NT_OPTIONAL_HDR32_MAGIC : result := 32;
  101.     IMAGE_NT_OPTIONAL_HDR64_MAGIC : result := 64;
  102.  
  103.     { otherwise leave it at zero                                              }
  104.   end;
  105. end;
  106.  
  107. {-----------------------------------------------------------------------------}
  108.  
  109. function WndProc (Wnd : HWND; Msg : UINT; wParam, lParam : ptrint)
  110.          : ptrint; stdcall;
  111.   { main application/window handler function                                  }
  112. const
  113.   WindowBitness_Call     = 'Windows bitness';
  114.  
  115.   Bitness32              = 'This is a 32 bit Windows installation'#0;
  116.   Bitness64              = 'This is a 64 bit Windows installation'#0;
  117.  
  118.   BitnessUnknown         = 'failed to determine this Windows installation '   +
  119.                            'bitness'#0;
  120.  
  121.   { initialize to "unknown" until we determine the Windows bitness            }
  122.  
  123.   Bitness                : pchar = BitnessUnknown;
  124.  
  125.   CSRSS                  = 'csrss.exe';
  126.   BACKSLASH              = '\';
  127.  
  128.   _64BIT_POINTER_SIZE    = 8;
  129.  
  130. var
  131.   ps                     : TPAINTSTRUCT;
  132.   ClientRect             : TRECT;
  133.  
  134.   TextSize               : TSIZE;
  135.  
  136.   CsrssFullpath : packed array[0..511] of char;
  137.  
  138.   i                      : DWORD;
  139.   CsrssLoadAddress       : HMODULE;
  140.   LoadAddress            : HMODULE;
  141.  
  142. begin
  143.   WndProc := 0;
  144.  
  145.   case Msg of
  146.     WM_CREATE:
  147.     begin
  148.       {$ifdef DEBUG}
  149.         if IsDebuggerPresent() then DebugBreak();
  150.       {$endif}
  151.  
  152.  
  153.       { when using FPC and compiling for 32 bit, ignore the unreachable code  }
  154.       { warning.                                                              }
  155.  
  156.       if sizeof(pointer) = _64BIT_POINTER_SIZE then
  157.       begin
  158.         { we are a running 64bit program, therefore the Windows installation  }
  159.         { must be 64bit.  Not much to do in this case.                        }
  160.  
  161.         Bitness := Bitness64;
  162.  
  163.         exit;                               { we are done!                    }
  164.       end;
  165.  
  166.       { if we are a 32bit program, we need to find out if this is a 32bit or  }
  167.       { 64bit windows installation.                                           }
  168.  
  169.       ZeroMemory(@CsrssFullpath, sizeof(CsrssFullpath));
  170.       GetSystemDirectory(CsrssFullpath,
  171.                          sizeof(CsrssFullpath));
  172.  
  173.       { append CSRSS to the system directory and a backslash if needed        }
  174.  
  175.       i := lstrlen(CsrssFullpath);
  176.  
  177.       if CsrssFullpath[i - 1] <> BACKSLASH then
  178.       begin
  179.         { append the backslash                                                }
  180.  
  181.         lstrcat(CsrssFullpath, BACKSLASH);
  182.       end;
  183.  
  184.       lstrcat(CsrssFullpath, CSRSS);             { path to CSRSS              }
  185.  
  186.       { load CSRSS as a data file. At this time, Windows will not apply file  }
  187.       { system redirection when the call to LoadLibraryEx is to load a file   }
  188.       { as a data file.  Therefore csrss.exe should have been found, if it    }
  189.       { wasn't then we are dealing with an unexpected situation in which case }
  190.       { we declare the attempt to determine the O/S as having failed.         }
  191.  
  192.       CsrssLoadAddress := LoadLibraryEx(CsrssFullpath,
  193.                                         0,
  194.                                         LOAD_LIBRARY_AS_DATAFILE);
  195.  
  196.       if CsrssLoadAddress = 0 then
  197.       begin
  198.  
  199.         exit;
  200.       end;
  201.  
  202.       { because we specified LOAD_LIBRARY_AS_DATAFILE the load address        }
  203.       { returned isn't "quite right".  When LOAD_LIBRARY_AS_DATAFILE is used  }
  204.       { the address points one (1) byte past the actual load address.  We     }
  205.       { need the "real" address, therefore we subtract one (1) from the       }
  206.       { address returned in this case.                                        }
  207.  
  208.       LoadAddress := CsrssLoadAddress;
  209.       if LoadAddress and $1 <> 0 then
  210.       begin
  211.         dec(LoadAddress);
  212.       end;
  213.  
  214.       { presuming there are only two versions of Windows, a 32 bit version    }
  215.       { and a 64 bit version, the module bitness should be 32 bit, if it      }
  216.       { isn't then we must be running under an unexpected version that is     }
  217.       { neither 32 nor 64 bit.  In that case, the Bitness is left as "unknown"}
  218.  
  219.       case GetModuleBitness(LoadAddress) of
  220.         32 : Bitness := Bitness32;
  221.         64 : Bitness := Bitness64;
  222.       end;
  223.  
  224.       FreeLibrary(CsrssLoadAddress);  { we no longer need it                  }
  225.  
  226.       exit;
  227.     end;
  228.  
  229.     WM_PAINT:
  230.     begin
  231.       BeginPaint(Wnd, ps);
  232.  
  233.       { set up the dc                                                         }
  234.  
  235.       GetClientRect(Wnd, ClientRect);
  236.  
  237.       SelectObject(ps.hdc, GetStockObject(DEFAULT_GUI_FONT));
  238.       SetBkMode(ps.hdc, TRANSPARENT);
  239.       SetTextAlign(ps.hdc, TA_CENTER or TA_BOTTOM);
  240.  
  241.       GetTextExtentPoint32(ps.hdc,
  242.                            Bitness,
  243.                            lstrlen(Bitness),
  244.                            TextSize);
  245.  
  246.       {-----------------------------------------------------------------------}
  247.       { output the bitness of this Windows installation as determined during  }
  248.       { WM_CREATE.                                                            }
  249.  
  250.       TextOut(ps.hdc,
  251.               ClientRect.Right  div 2,
  252.               ClientRect.Bottom div 2,
  253.               Bitness,
  254.               lstrlen(Bitness));
  255.  
  256.  
  257.       {-----------------------------------------------------------------------}
  258.       { draw the function call                                                }
  259.  
  260.       TextOut(ps.hdc,
  261.               ClientRect.Right  div 2,
  262.               ClientRect.Bottom - TextSize.cy,
  263.               WindowBitness_Call,
  264.               lstrlen(WindowBitness_Call));
  265.  
  266.       {-----------------------------------------------------------------------}
  267.       { we're done painting                                                   }
  268.  
  269.       EndPaint(Wnd, ps);
  270.  
  271.       exit;
  272.     end;
  273.  
  274.     WM_COMMAND:
  275.     begin
  276.       case LOWORD(wParam) of
  277.         IDM_ABOUT:
  278.         begin
  279.           DialogBox(hInstance, ABOUTBOX, Wnd, @About);
  280.  
  281.           exit;
  282.         end; { IDM_ABOUT }
  283.  
  284.         IDM_EXIT:
  285.         begin
  286.           DestroyWindow(Wnd);
  287.  
  288.           exit;
  289.         end; { IDM_EXIT }
  290.       end; { case LOWORD(wParam) }
  291.     end; { WM_COMMAND }
  292.  
  293.     WM_DESTROY:
  294.     begin
  295.       PostQuitMessage(0);
  296.  
  297.       exit;
  298.     end; { WM_DESTROY }
  299.   end; { case msg }
  300.  
  301.   WndProc := DefWindowProc (Wnd, Msg, wParam, lParam);
  302. end;
  303.  
  304. {-----------------------------------------------------------------------------}
  305.  
  306. function InitAppClass: WordBool;
  307.   { registers the application's window classes                                }
  308. var
  309.   cls : TWndClassEx;
  310.  
  311. begin
  312.   cls.cbSize          := sizeof(TWndClassEx);           { must be initialized }
  313.  
  314.   if not GetClassInfoEx (hInstance, AppName, cls) then
  315.   begin
  316.     with cls do
  317.     begin
  318.       { cbSize has already been initialized as required above                 }
  319.  
  320.       style           := CS_BYTEALIGNCLIENT;
  321.       lpfnWndProc     := @WndProc;                    { window class handler  }
  322.       cbClsExtra      := 0;
  323.       cbWndExtra      := 0;
  324.       hInstance       := system.hInstance;
  325.       hIcon           := LoadIcon (hInstance, APPICON);
  326.       hCursor         := LoadCursor(0, IDC_ARROW);
  327.       hbrBackground   := GetSysColorBrush(COLOR_WINDOW);
  328.       lpszMenuName    := APPMENU;                     { Menu name             }
  329.       lpszClassName   := AppName;                     { Window Class name     }
  330.       hIconSm         := LoadImage(hInstance,
  331.                                    APPICON,
  332.                                    IMAGE_ICON,
  333.                                    16,
  334.                                    16,
  335.                                    LR_DEFAULTCOLOR);
  336.     end; { with }
  337.  
  338.     InitAppClass := WordBool(RegisterClassEx(cls));
  339.   end
  340.   else InitAppClass := TRUE;
  341. end;
  342.  
  343. {-----------------------------------------------------------------------------}
  344.  
  345. function WinMain : integer;
  346.   { application entry point                                                   }
  347. var
  348.   Wnd : hWnd;
  349.   Msg : TMsg;
  350.  
  351. begin
  352.   if not InitAppClass then Halt (255);  { register application's class        }
  353.  
  354.   { Create the main application window                                        }
  355.  
  356.   Wnd := CreateWindowEx(WS_EX_CLIENTEDGE,
  357.                         AppName,                { class name                  }
  358.                         AppName,                { window caption text         }
  359.                         ws_Overlapped       or  { window style                }
  360.                         ws_SysMenu          or
  361.                         ws_MinimizeBox      or
  362.                         ws_ClipSiblings     or
  363.                         ws_ClipChildren     or  { don't affect children       }
  364.                         ws_Visible,             { make showwindow unnecessary }
  365.                         20,                     { x pos on screen             }
  366.                         20,                     { y pos on screen             }
  367.                         400,                    { window width                }
  368.                         200,                    { window height               }
  369.                         0,                      { parent window handle        }
  370.                         0,                      { menu handle 0 = use class   }
  371.                         hInstance,              { instance handle             }
  372.                         nil);                   { parameter sent to WM_CREATE }
  373.  
  374.   { a message box indicating failure and the reason for it would be more      }
  375.   { desirable.                                                                }
  376.  
  377.   if Wnd = 0 then Halt;                         { could not create the window }
  378.  
  379.   while GetMessage (Msg, 0, 0, 0) do            { wait for message            }
  380.   begin
  381.     TranslateMessage (Msg);                     { key conversions             }
  382.     DispatchMessage  (Msg);                     { send to window procedure    }
  383.   end;
  384.  
  385.   WinMain := Msg.wParam;                        { terminate with return code  }
  386. end;
  387.  
  388. begin
  389.   WinMain;
  390. end.
  391.  
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.




All comments and feedback welcome.
Title: Re: WinAPI - Windows bitness & Teaser programs
Post by: PascalDragon on January 15, 2021, 09:35:25 am
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!

Wouldn't it be better to check for WoW64.dll? If you're running on a 32-bit system that library won't exist/load and on a 64-bit system it will be always loaded into a 32-bit process already thus avoiding the need to map csrss.exe into the address space.
Title: Re: WinAPI - Windows bitness & Teaser programs
Post by: 440bx on January 15, 2021, 10:41:01 am
Wouldn't it be better to check for WoW64.dll? If you're running on a 32-bit system that library won't exist/load and on a 64-bit system it will be always loaded into a 32-bit process already thus avoiding the need to map csrss.exe into the address space.
That was my first thought too and ended up being more cumbersome than going after csrss.exe.   

Going after Wow64.dll, the first thing I tried was using GetModuleHandle, it failed.  Tried using LoadLibrary and it also failed, most likely because a 32 bit app is not allowed to load a 64bit dll.  It would work with LoadLibraryEx specifying "load as datafile" but that dll is 238K whereas csrss.exe is only 8K.  Better (faster) to load the smaller one.

Another way that works is to use any of the module enumeration API (toolhelp32 or EnumProcessModules) but that requires more code and it's slower.

After trying a number of ways, going after csrss.exe with LoadLibraryEx ended up being the solution that required the least amount of code, least in required user privileges and still reliable.


ETA

The call to GetTextExtentPoint32 on lines 241 through 244 is superfluous and can be removed.  It cannot be removed because the height of the font is used to calculate the Y coordinate used in TextOut. 
Title: Re: Windows API test programs / Examples
Post by: marcov on January 15, 2021, 12:21:14 pm
Best to use the MS advised way, dynload it if you have to, and only use hackish ways for XP. That avoids the chance that some future way breaks the hackish way for non XP systems.

But I think this goes a bit far for the infinitesimally small percentage of users that still run XP 64-bit, a number that was never very big to being with.   

So dynloading iswowprocess(2) and returning 32-bit if that fails is IMHO more than good enough. 

Title: Re: Windows API test programs / Examples
Post by: 440bx on January 15, 2021, 01:01:48 pm
@marcov

Honestly, I would rather depend on checking the bitness of csrss.exe than use either IsWowProcess or IsWowProcess2. 

csrss.exe isn't going to go away anytime soon and loading it as a data file requires no privileges.  Additionally, the IsWow64Process() documentation states the following:
Quote
If the process is a 32-bit application running under 64-bit Windows 10 on ARM, the value is set to FALSE.
which means that calling IsWow64Process on an ARM platform would tell the 32 bit program that it is _not_ running on a 64 bit Windows even though it is. 

IOW, I avoid APIs that are "moody".  I prefer methods that return unadulterated facts.  Checking csrss.exe's bitness is much more reliable.
Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 15, 2021, 01:02:28 pm
But I think this goes a bit far for the infinitesimally small percentage of users that still run XP 64-bit, a number that was never very big to being with.

It's not only XP, but also Windows Server 2003. It provides the API only starting from SP1.

Going after Wow64.dll, the first thing I tried was using GetModuleHandle, it failed.  Tried using LoadLibrary and it also failed, most likely because a 32 bit app is not allowed to load a 64bit dll.  It would work with LoadLibraryEx specifying "load as datafile" but that dll is 238K whereas csrss.exe is only 8K.  Better (faster) to load the smaller one.

Fair enough.
Title: Re: WinAPI - Windows bitness & Teaser programs
Post by: ASerge on January 15, 2021, 05:59:06 pm
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.
Some far-fetched reasons. First, IsWow64Process present in the original XP 64, because it was born from Windows 2003 Server (it also has this function). The 32-bit version of XP may not have this function, but the 64-bit version has always had it, from birth.
Second, the process of course has read rights to its own kernel32.
Title: Re: WinAPI - Windows bitness & Teaser programs
Post by: 440bx on January 15, 2021, 06:46:36 pm
Some far-fetched reasons. First, IsWow64Process present in the original XP 64, because it was born from Windows 2003 Server (it also has this function). The 32-bit version of XP may not have this function, but the 64-bit version has always had it, from birth.
Second, the process of course has read rights to its own kernel32.
Not far fetched.  The fact that the 64 bit version had it from the start is useless since a 64 bit program does not need either function to determine that it is running under a 64 bit version of Windows.

It is the fact that the 32 bit version of Windows did _not_ have it from the start that causes problems.  In addition to that, on some platforms, e.g, ARM the API returns FALSE to a 32 bit program even though it is running under a 64 bit version of Windows.  MS does not say how IsWowProcess2() behaves on the ARM platform in that case but, the inevitable conclusion is that using those APIs to determine whether a 32 bit program is running  under a 64 bit version of Windows is not a reliable method to make the determination.
Title: Re: Windows API test programs / Examples
Post by: ASBzone on January 15, 2021, 08:09:51 pm

So far, the level of interest does not seem to be high enough to even merit concern on how to go about it.


I'm interested for sure.   I've just been busy of late, but this would be of interest to me, especially APIs dealing with physical memory and the Task Scheduler.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 15, 2021, 08:16:22 pm
I'm interested for sure.   I've just been busy of late, but this would be of interest to me, especially APIs dealing with physical memory and the Task Scheduler.
Pleased to hear you're interested, adding one (1) to the "interested count". :) 

As far as APIs that deal with Physical Memory, there are very few APIs that deal with Physical Memory, the great majority deal with Virtual Memory and, they are, I believe, all informational.  As far as the Task Scheduler, unfortunately I don't believe I have any examples that apply to it.
Title: Re: Windows API test programs / Examples
Post by: mika on January 15, 2021, 09:18:59 pm
csrss.exe isn't going to go away anytime soon and loading it as a data file requires no privileges. 
It fails in wine under linux (failed to determine windows installation bitness).

It's charming to see old style windows gui programing.
Title: Re: Windows API test programs / Examples
Post by: marcov on January 15, 2021, 10:44:56 pm
But I think this goes a bit far for the infinitesimally small percentage of users that still run XP 64-bit, a number that was never very big to being with.

It's not only XP, but also Windows Server 2003. It provides the API only starting from SP1.

I stand corrected. Windows Server 2003 that even didn't get all their 2005 patches also suffer. That will be a big number :-)
Title: Re: Windows API test programs / Examples
Post by: marcov on January 15, 2021, 10:46:10 pm
csrss.exe isn't going to go away anytime soon

No but your access from some types of accounts might. Like fully ordinary user.


Title: Re: Windows API test programs / Examples
Post by: 440bx on January 16, 2021, 03:43:56 am
It fails in wine under linux (failed to determine windows installation bitness).
Given that it's under Linux, it should be expected for the method to fail.

IOW, there is no _real_ Windows in that case.  Emulation layers often have limitations.

I guess/speculate that IsWowProcess and IsWowProcess2 under Wine might always return FALSE.



ETA

No but your access from some types of accounts might. Like fully ordinary user.
So far, the method seems to work with "ordinary"/"limited" users.  That said, it is conceivable and even likely it would fail for users in a custom and very restricted group.  In that case, IsWowProcess and IsWowProcess2 would probably fail too.

IOW, In the "normal"/common case, the method woks as well or better than the IsWowProcess and IsWowProcess2 APIs.
Title: Re: WinAPI - Windows bitness & Teaser programs
Post by: PascalDragon on January 16, 2021, 11:11:33 am
Some far-fetched reasons. First, IsWow64Process present in the original XP 64, because it was born from Windows 2003 Server (it also has this function). The 32-bit version of XP may not have this function, but the 64-bit version has always had it, from birth.
Second, the process of course has read rights to its own kernel32.
Not far fetched.  The fact that the 64 bit version had it from the start is useless since a 64 bit program does not need either function to determine that it is running under a 64 bit version of Windows.

Putting aside the ARM64 issue: assuming that every 64-bit Windows OS has the IsWow64Process function then your check in a 32-bit application would simply be: does the IsWow64Process exist? If no, the OS must be 32-bit. Otherwise call IsWow64Process.

In addition to that, on some platforms, e.g, ARM the API returns FALSE to a 32 bit program even though it is running under a 64 bit version of Windows.  MS does not say how IsWowProcess2() behaves on the ARM platform in that case but, the inevitable conclusion is that using those APIs to determine whether a 32 bit program is running  under a 64 bit version of Windows is not a reliable method to make the determination.

MS says that IsWow64Process2 returns the image machine type of the process and of the host. I consider that enough information.

I personally would simply use a combination of IsWow64Process and IsWow64Process2 as even the priviledge issue is highly theoretical with the handle of the current process obtained with GetCurrentProcess as the creator of the process would need to actively disable the QUERY_INFORMATION_PROCESS and QUERY_LIMITED_INFORMATION_PROCESS priviledges and even then I don't know right now whether Windows silently enables them for the handle of the current process anyway (or, considering that the handle returned by GetCurrentProcess is a pseudohandle, doesn't even need them).
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 16, 2021, 12:28:49 pm
@PascalDragon

Note that I didn't say that it is not possible to determine the O/S bitness using IsWowProcess and/or IsWowProcess2.   What I state is that, making the determination using those APIs is cumbersome and, in the case of ARM, it is unreliable.  To use those APIs, the programmer first has to determine if those APIs are available and, if they are, use them.  To make matters worse, on an ARM processor, the result, if using IsWowProcess, will be incorrect. 

The alternative I recommend is a straight, reliable line to the correct answer.  Load csrss.exe and check its bitness.  No need to find out if a particular API is available and no possibility of getting it wrong regardless of the processor the O/S runs on.

Obviously, everyone is free to choose whatever method they want to use but, I recommend checking csrss.exe because it is the simplest and most reliable route to make the determination. No concerns about user privileges, API availability and processor affect the method.
Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 16, 2021, 05:09:22 pm
Note that I didn't say that it is not possible to determine the O/S bitness using IsWowProcess and/or IsWowProcess2.   What I state is that, making the determination using those APIs is cumbersome and, in the case of ARM, it is unreliable.  To use those APIs, the programmer first has to determine if those APIs are available and, if they are, use them.  To make matters worse, on an ARM processor, the result, if using IsWowProcess, will be incorrect.

And here I don't agree. I consider the API the more straightforward, sane and stable approach then checking some file as long as one knows the restrictions of the functions (e.g. use IsWow64Process2 if available and only then IsWow64Process). In theory nothing is stopping Microsoft from using some file system redirection for csrss.exe (as they do for SysWow64 already) and then your checks would be completely screwed.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 16, 2021, 10:12:42 pm
And here I don't agree.
We can agree to disagree.  I have no problem with that.

I consider the API the more straightforward, sane and stable approach then checking some file as long as one knows the restrictions of the functions (e.g. use IsWow64Process2 if available and only then IsWow64Process).
that "sane and stable approach" takes more code that is more complicated and, in the case of ARM whether or not it works is a question mark.  You know that is a fact.

In theory nothing is stopping Microsoft from using some file system redirection for csrss.exe (as they do for SysWow64 already) and then your checks would be completely screwed.
But there is something in _practice_ that stops MS from using file system redirection in this case which is, a 32 bit program is allowed to load a 64 bit DLL as a resource file (to either use its resources or manipulate them.)  If MS were to change that, they would break all 32 bit programs that load resources from 64 bit executables.  Additionally, in the case of 64 bit modules, there is only _one_ copy available in the system, there is nowhere to redirect to.  The bottom line is, yes, _in theory_ they could do it, _in practice_ doing it would result in the breaking of a fair amount of code including some of their own code for no good reason.  Doesn't seem a likely possibility.
Title: Re: Windows API test programs / Examples
Post by: ASerge on January 16, 2021, 10:34:33 pm
This code work:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. uses Windows, SysUtils;
  5.  
  6. function IsThisWin32OnWin64: Boolean;
  7. type
  8.   TIsWow64Process = function(hProcess: HMODULE; Wow64Process: PBOOL): BOOL; stdcall;
  9.   TIsWow64Process2 = function(hProcess: HMODULE; pProcessMachine: PUSHORT; pNativeMachine: PUSHORT): BOOL; stdcall;
  10. var
  11.   K32: HMODULE;
  12.   P: FARPROC;
  13.   Wow64Process: BOOL = False;
  14.   ProcessMachine: USHORT = IMAGE_FILE_MACHINE_UNKNOWN;
  15.   NotUsed: USHORT = 0;
  16. begin
  17.   K32 := GetModuleHandle(kernel32);
  18.   P := GetProcAddress(K32, 'IsWow64Process');
  19.   if Assigned(P) and TIsWow64Process(P)(GetCurrentProcess, @Wow64Process) and Wow64Process then
  20.     Exit(True);
  21.   P := GetProcAddress(K32, 'IsWow64Process2');
  22.   if Assigned(P) and TIsWow64Process2(P)(GetCurrentProcess, @ProcessMachine, @NotUsed) and (ProcessMachine <> IMAGE_FILE_MACHINE_UNKNOWN) then
  23.     Exit(True);
  24.   Result := False;
  25. end;
  26.  
  27. begin
  28.   Writeln(IsThisWin32OnWin64);
  29.   Readln;
  30. end.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 16, 2021, 10:39:02 pm
@Serge.

Have you tried that code on an ARM platform ?... how does it work there ?
Title: Re: Windows API test programs / Examples
Post by: ASerge on January 16, 2021, 10:48:09 pm
Have tried that code on an ARM platform ?... how does it work there ?
No, I don't have an ARM machine. This part is based on the documentation for the IsWow64Process2 function, which does not make an exception for ARM machine.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 16, 2021, 11:03:19 pm
No, I don't have an ARM machine. This part is based on the documentation for the IsWow64Process2 function, which does not make an exception for ARM machine.
That's a precarious thing to depend on because the documentation for IsWowProcess does _not_ state that on an ARM machine, IsWowProcess2 should be used instead.

When an API, such as IsWowProcess, doesn't work in a particular case, they point the reader to the one that works, which in this case should be IsWowProcess2 but, that recommendation is notably missing.

Contrast that with an API such as GlobalMemoryStatus which does not work on systems with more than 4GB, the description of that API directs the reader to use GlobalMemoryStatusEx because that one works in that case.  That is missing in IsWowProcess, they state the problem and do _not_ offer IsWowProcess2 as a solution to it.

Unless you test it on an ARM machine, I wouldn't trust the code you provided to work on that platform.  Succinctly, without testing, you're just hoping it works (it may or may not, like you, I don't know.)

Title: Re: Windows API test programs / Examples
Post by: ASerge on January 17, 2021, 12:37:05 pm
Unless you test it on an ARM machine, I wouldn't trust the code you provided to work on that platform.  Succinctly, without testing, you're just hoping it works (it may or may not, like you, I don't know.)
Until you test your code for ARM64, I can't trust it either. Mine is at least based on official documentation, and is more likely to work.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 17, 2021, 12:54:21 pm
Until you test your code for ARM64, I can't trust it either. Mine is at least based on official documentation, and is more likely to work.
Your argument is reasonable but, mine will work as long as csrss.exe exists, which is rather likely. :) 

Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 17, 2021, 10:14:58 pm
No, I don't have an ARM machine. This part is based on the documentation for the IsWow64Process2 function, which does not make an exception for ARM machine.
That's a precarious thing to depend on because the documentation for IsWowProcess does _not_ state that on an ARM machine, IsWowProcess2 should be used instead.

When an API, such as IsWowProcess, doesn't work in a particular case, they point the reader to the one that works, which in this case should be IsWowProcess2 but, that recommendation is notably missing.

Apparently we're not reading the same documentation (https://docs.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process#remarks) then (emphasis mine):

Quote
Remarks

Applications should use IsWow64Process2 instead of IsWow64Process to determine if a process is running under WOW. IsWow64Process2 removes the ambiguity inherent to multiple WOW environments by explicitly returning both the architecture of the host and guest for a given process. Applications can use this information to reliably identify situations such as running under emulation on ARM64. To compile an application that uses this function, define _WIN32_WINNT as 0x0501 or later. For more information, see Using the Windows Headers.


Unless you test it on an ARM machine, I wouldn't trust the code you provided to work on that platform.  Succinctly, without testing, you're just hoping it works (it may or may not, like you, I don't know.)

I've modified ASerge's program as follows:

Code: Pascal  [Select][+][-]
  1. program tiswow64;
  2.  
  3. {$MODE OBJFPC}
  4. {$APPTYPE CONSOLE}
  5.  
  6. uses Windows, SysUtils;
  7.  
  8. procedure TestWow64Process2;
  9. type
  10.   TIsWow64Process2 = function(hProcess: HMODULE; pProcessMachine: PUSHORT; pNativeMachine: PUSHORT): BOOL; stdcall;
  11. var
  12.   K32: HMODULE;
  13.   P: FARPROC;
  14.   ProcessMachine,
  15.   HostMachine: USHORT;
  16. begin
  17.   K32 := GetModuleHandle(kernel32);
  18.   P := GetProcAddress(K32, 'IsWow64Process2');
  19.   if Assigned(P) then begin
  20.     if TIsWow64Process2(P)(GetCurrentProcess, @ProcessMachine, @HostMachine) then begin
  21.       Writeln('Machines: Process = ', HexStr(ProcessMachine, 4), ', Host = ', HexStr(HostMachine, 4));
  22.     end else
  23.       Writeln('Failed to execute IsWow64Process2: ', SysErrorMessage(GetLastOSError));
  24.   end else
  25.     Writeln('IsWow64Process2 not found');
  26. end;
  27.  
  28. begin
  29.   TestWow64Process2;
  30. end.

This I have run on Windows on ARM64 once compiled as aarch64-win64 and once as i386-win32:

Code: [Select]
D:\fpc\git\fpctests>tiswow64-a64.exe
Machines: Process = 0000, Host = AA64

D:\fpc\git\fpctests>tiswow64-i386.exe
Machines: Process = 014C, Host = AA64

And I'd wager that x86_64-win64 on Windows on ARM64 would look like this (since I'm not on the Windows Insider Preview channel I can't test):

Code: [Select]
Machines: Process = 8664, Host = AA64
And arm-win32 would look like this (I've not yet ported FPC for arm-win32 and arm-wince won't work there):

Code: [Select]
Machines: Process = 01C4, Host = AA64
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 17, 2021, 10:22:58 pm
Apparently we're not reading the same documentation (https://docs.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process#remarks) then (emphasis mine):
Quote
Remarks

Applications should use IsWow64Process2 instead of IsWow64Process to determine if a process is running under WOW. IsWow64Process2 removes the ambiguity inherent to multiple WOW environments by explicitly returning both the architecture of the host and guest for a given process. Applications can use this information to reliably identify situations such as running under emulation on ARM64. To compile an application that uses this function, define _WIN32_WINNT as 0x0501 or later. For more information, see Using the Windows Headers.
You are right. My mistake.  Thank you for point it out.  Lesson: I should read the whole thing instead of stopping.

This I have run on Windows on ARM64 once compiled as aarch64-win64 and once as i386-win32:
Did you happen to test the method I recommend on ARM64 ? ... if the answer is yes, I'd like to know what the result was.  Thank you.
Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 19, 2021, 09:17:49 am
This I have run on Windows on ARM64 once compiled as aarch64-win64 and once as i386-win32:
Did you happen to test the method I recommend on ARM64 ? ... if the answer is yes, I'd like to know what the result was.  Thank you.

I have not. Will try to fit that in some time this week.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 19, 2021, 11:47:26 am
I have not. Will try to fit that in some time this week.
Thank you.
Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 24, 2021, 12:59:54 pm
This I have run on Windows on ARM64 once compiled as aarch64-win64 and once as i386-win32:
Did you happen to test the method I recommend on ARM64 ? ... if the answer is yes, I'd like to know what the result was.  Thank you.

I have not. Will try to fit that in some time this week.

Of course it would take me till the weekend... ::) Anyway, your example seems to work. At least it shows that the Windows on ARM64 is a 64-bit one.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 24, 2021, 01:21:31 pm
Anyway, your example seems to work. At least it shows that the Windows on ARM64 is a 64-bit one.
Thank you.  I appreciate your confirming that it works on other CPUs as well (ARM64 in this case.)
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 28, 2021, 03:44:44 pm
I ran TISWOW64.pas on my machine and get:

Quote
Running "e:\tiswow64.exe "
Machines: Process = 0000, Host = 014C

From MS docs, I found 0x014C is:
Quote
0x14c
Intel 386 or later processors and compatible processors

From the docs though, I don't recognize which is Intel x64 CPU. Sorry if my question was dumb.

I see only the below are x64 but none are Intel's:
Quote
MACHINE_IA64
0x200
Intel Itanium processor family

MACHINE_ARM64
0xaa64
ARM64 little endian

MACHINE_AMD64
0x8664
x64

Is there any other list?
Title: Re: Windows API test programs / Examples
Post by: lucamar on January 28, 2021, 06:05:38 pm
Is there any other list?

Difficult to say without knowing which list you're looking to, but the one for Image File Machine Constants (https://docs.microsoft.com/en-us/windows/win32/sysinfo/image-file-machine-constants) (as returned by e,g, IsWow64Process2) contains the following Intel-related constants:
Code: Pascal  [Select][+][-]
  1. IMAGE_FILE_MACHINE_I386 = 0x014c = Intel 386
  2. IMAGE_FILE_MACHINE_IA64 = 0x0200 = Intel 64
Note that while in your list MACHINE_IA64 is taken to mean Itanium family, in this one it's used for any Intel IA64 processor.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 28, 2021, 06:49:32 pm
I ran TISWOW64.pas on my machine and get:

Quote
Running "e:\tiswow64.exe "
Machines: Process = 0000, Host = 014C
It's one thing to determine the CPU Windows is running under and another thing to determine if a program is running under Wow64.  The presence of a 64 bit CPU does not imply that a 64 bit O/S is installed.

For instance, a 32bit version of Windows runs fine on an x64 CPU (Intel or AMD.)  A program that returns which CPU is present should report an x86_64 type of CPU even though the program is _not_ running under Wow64.  The intel/AMD 64 bit CPUs are identified as an IMAGE_FILE_MACHINE_AMD64.

It's also important to note that depending on the API used, Windows may report different results.  For instance, if a 32 bit program uses GetSystemInfo on a 64 bit processor (and running under Wow64), Windows will report the processor architecture as being PROCESSOR_ARCHITECTURE_INTEL and processor type as PROCESSOR_INTEL_PENTIUM (which is a 32 bit processor), whereas a 32 bit program that uses GetNativeSystemInfo will return a processor architecture of PROCESSOR_ARCHITECTURE_AMD64 and a processor type of PROCESSOR_AMD_8664. 

For some reason, MS explicitly discourages using GetNativeSystemInfo to determine if a program is running under Wow64.  The reason why is still unclear.
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 29, 2021, 09:31:29 am
Difficult to say without knowing which list you're looking to, but the one for Image File Machine Constants (https://docs.microsoft.com/en-us/windows/win32/sysinfo/image-file-machine-constants) (as returned by e,g, IsWow64Process2) contains the following Intel-related constants:
Code: Pascal  [Select][+][-]
  1. IMAGE_FILE_MACHINE_I386 = 0x014c = Intel 386
  2. IMAGE_FILE_MACHINE_IA64 = 0x0200 = Intel 64
Note that while in your list MACHINE_IA64 is taken to mean Itanium family, in this one it's used for any Intel IA64 processor.

Thank you for the link, looks like the 'Itanium' word has been removed from that list.

It's one thing to determine the CPU Windows is running under and another thing to determine if a program is running under Wow64.  The presence of a 64 bit CPU does not imply that a 64 bit O/S is installed.
......The intel/AMD 64 bit CPUs are identified as an IMAGE_FILE_MACHINE_AMD64.

Then I think Process is the Windows and Host is the CPU?

Thanks for the answer, AMD64, need to test it on Windows 64-bit one day.

It's also important to note that depending on the API used, Windows may report different results.  For instance, if a 32 bit program uses GetSystemInfo on a 64 bit processor (and running under Wow64), Windows will report the processor architecture as being PROCESSOR_ARCHITECTURE_INTEL and processor type as PROCESSOR_INTEL_PENTIUM (which is a 32 bit processor), whereas a 32 bit program that uses GetNativeSystemInfo will return a processor architecture of PROCESSOR_ARCHITECTURE_AMD64 and a processor type of PROCESSOR_AMD_8664. 

For some reason, MS explicitly discourages using GetNativeSystemInfo to determine if a program is running under Wow64.  The reason why is still unclear.

This is an additional information. I am not sure how .NET detects Windows bitness although I believe there is a simpler way (than IsWow64)?

P.S. You do not have to answer anyway. The information you and others provided are enough for me.
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 29, 2021, 10:37:09 am
Then I think Process is the Windows and Host is the CPU?
The "process" is the container Windows creates to run an executable.  The process' bitness is always determined by the bitness of the executale.  IOW, a 32 bit executable runs in a 32 bit process and a 64 bit executable in a 64 bit process.

As far as "Host", without context it's a bit dangerous to define it.  For instance, the process can be considered the "host" to the executable.

This is an additional information. I am not sure how .NET detects Windows bitness although I believe there is a simpler way (than IsWow64)?
I have no idea how .NET goes about detecting the Windows bitness.  Based on MS' documentation, I would guess it uses the APIs IsWow64Process(2) but, personally, I prefer checking csrss.exe's bitness because that eliminates the possibility that an API may lie to the program (which some Windows APIs seem a bit too fond of doing and seems to be quite acceptable to MS in general.)

Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 29, 2021, 01:15:57 pm
I ran TISWOW64.pas on my machine and get:

Quote
Running "e:\tiswow64.exe "
Machines: Process = 0000, Host = 014C

From MS docs, I found 0x014C is:
Quote
0x14c
Intel 386 or later processors and compatible processors

If your Windows is a 32-bit Windows, then this is indeed correct.

As others already said, if your Windows is a 64-bit one running on a x86 64-bit processor then HostMachine will be $8664 (IMAGE_FILE_MACHINE_AMD64 even for Intel CPUs because Intel licensed the 64-bit extensions from AMD).

Then I think Process is the Windows and Host is the CPU?

No. ProcessMachine is the machine type of the process if different from HostMachine and HostMachine is the machine type the operating system is using natively. The CPU needs to be determined differently (at least for e.g. Intel vs. AMD vs. VIA).
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 29, 2021, 02:02:16 pm
Thanks 440bx and PascalDragon for the explanation. Learned a bit because I am new to OS and most part of Windows API.

With that said  :D, I think I found a quick way to determine Windows Bitness.
Code: [Select]
GetSystemWow64DirectoryA
Quote
Retrieves the path of the system directory used by WOW64. This directory is not present on 32-bit Windows.

Someone posted the ASM code and I modified it a bit and compile to EXE... if you want to test. Or perhaps can implement it in Pascal?

EDIT ON Mar 1st, 2021:

The "64-bit Windows" and "32-bit Windows" text string in the EXE (compiled by Flat Assembler) do not have null byte as terminated string. That's why you might already notice on 64-bit Windows, you get "64-bit Windows. 32-bit Windows" concatenated together.

Below is the correct code (just append ",0" to the back of both text string):
(However, I am not going to re-compile and re-upload the correct version of EXE. If you like, you can compile it yourself with FASM )
Code: ASM  [Select][+][-]
  1. format PE GUI 4.0
  2.  
  3. entry start
  4.  
  5. include '\fasm\include\win32a.inc'
  6.  
  7. section '.data' data readable writeable
  8.  
  9. a rb MAX_PATH
  10. IS64 db '64-bit Windows',0
  11. IS32 db '32-bit Windows',0
  12.  
  13. section '.code' code readable executable
  14.  
  15. start:
  16.       invoke GetSystemWow64Directory, a, MAX_PATH
  17.       cmp eax, 0
  18.       je NOT64
  19.  
  20.       invoke MessageBox, 0, IS64, a, MB_OK
  21.       jmp exit
  22. NOT64:
  23.       invoke MessageBox, 0, IS32, '', MB_OK
  24.  
  25. exit:
  26.       invoke ExitProcess, 0        
  27.      
  28. section '.idata' import data readable
  29.  
  30.  library kernel32,'kernel32.dll',\
  31.       user32,'user32.dll'
  32.  
  33. import kernel32,\
  34.      ExitProcess,'ExitProcess',\
  35.      GetSystemWow64Directory,'GetSystemWow64DirectoryA'
  36.  
  37. import user32,\
  38.      MessageBox,'MessageBoxA'


Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 29, 2021, 02:33:54 pm
I have modified the previous TIsWow64Process.pas with this one. Please help to rectify it if not correct..  :P

Code: Pascal  [Select][+][-]
  1. program tiswow64;
  2.  
  3.  
  4. {$APPTYPE CONSOLE}
  5.  
  6. uses Windows, SysUtils;
  7.  
  8. procedure TestWow64Process2;
  9. type
  10.   TIsWow64Process2 = function(lpBuffer: PCHAR; uSize: UINT): UINT; stdcall;
  11. var
  12.   K32: HMODULE;
  13.   P: FARPROC;
  14.   Buffer: PCHAR;
  15. begin
  16.   K32 := GetModuleHandle(kernel32);
  17.   P := GetProcAddress(K32, 'GetSystemWow64DirectoryA');
  18.   if Assigned(P) then begin
  19.     if TIsWow64Process2(P)(@Buffer, 255)>0 then begin
  20.       Writeln('64-bit' + Buffer);
  21.     end else
  22.       Writeln('32-bit');
  23.   end else
  24.     Writeln('GetSystemWow64DirectoryA not found');
  25.   ReadLn;
  26. end;
  27.  
  28. begin
  29.   TestWow64Process2;
  30. end.
  31.  
Title: Re: Windows API test programs / Examples
Post by: ASerge on January 29, 2021, 03:38:40 pm
I have modified the previous TIsWow64Process.pas with this one. Please help to rectify it if not correct..  :P
Interesting idea. Only you forgot to allocate memory for the buffer.
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. uses Windows, SysUtils;
  4.  
  5. procedure TestWow64Process2;
  6. type
  7.   GetSystemWow64DirectoryA = function(lpBuffer: PCHAR; uSize: UINT): UINT; stdcall;
  8. var
  9.   P: FARPROC;
  10.   Buffer: array[Byte] of AnsiChar;
  11. begin
  12.   P := GetProcAddress(GetModuleHandle(kernel32), 'GetSystemWow64DirectoryA');
  13.   if Assigned(P) then
  14.     if GetSystemWow64DirectoryA(P)(@Buffer, Length(Buffer)) > 0 then
  15.       Writeln('64-bit ' + Buffer)
  16.     else
  17.       Writeln('32-bit')
  18.   else
  19.     Writeln('GetSystemWow64DirectoryA not found');
  20. end;
  21.  
  22. begin
  23.   TestWow64Process2;
  24.   ReadLn;
  25. end.
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 29, 2021, 04:03:18 pm
Thanks @ASerge for the nice correction.

I just need to replace plus sign with comma on my FPC 3.2.0:
Code: Pascal  [Select][+][-]
  1. Writeln('64-bit ', Buffer)

Have you tested it? Not sure what is the result if running on 64-bit Windows (both ARM64 and AMD64).
Title: Re: Windows API test programs / Examples
Post by: 440bx on January 29, 2021, 04:18:06 pm
Just an observation, you probably don't need to GetProcAddress the function since it is implemented in Windows XP and above.  IOW, you need the GetProcAddress only for O/Ses older than XP.

This also means that the "else" part of the "if Assigned" can report the O/S as being 32 bit (it cannot be anything else, at least on intel compatible hardware.)

Tested on 64 bit Windows and 32 bit Windows XP, not tested on any ARM cpu.

It would be nice to know if that method works on an ARM64.  Maybe PascalDragon can do that test if he is so inclined. ;)

part of my test program :
Code: Pascal  [Select][+][-]
  1. function WndProc (Wnd : HWND; Msg : UINT; wParam, lParam : ptrint)
  2.          : ptrint; stdcall;
  3.   { main application/window handler function                                  }
  4. const
  5.   GetSystemWow64Directory_Call
  6.     = 'GetSystemWow64Directory (Buf : pchar; BufSize : UINT) : UINT;';
  7.  
  8.   SystemWow64Directory : packed array[0..MAX_PATH] of char = #0;
  9.  
  10.   NotImplemented       = 'There is no Wow64 directory in this installation';
  11.  
  12.   DisplayText          : pchar = NotImplemented;  { 32 bit                    }
  13.  
  14. var
  15.   ps           : TPAINTSTRUCT;
  16.   ClientRect   : TRECT;
  17.   Buf          : packed array[0..MAX_PATH] of char;
  18.   TextSize     : TSIZE;
  19.  
  20. begin
  21.   WndProc := 0;
  22.  
  23.   case Msg of
  24.     WM_CREATE:
  25.     begin
  26.       ZeroMemory(@SystemWow64Directory, sizeof(SystemWow64Directory));
  27.  
  28.       if GetSystemWow64Directory(SystemWow64Directory,
  29.                           sizeof(SystemWow64Directory)) <> 0 then
  30.       begin
  31.         DisplayText := SystemWow64Directory;  { s/b 64 bit                    }
  32.       end;
  33.     end;
  34.  
Title: Re: Windows API test programs / Examples
Post by: PascalDragon on January 30, 2021, 06:45:58 pm
I just need to replace plus sign with comma on my FPC 3.2.0:
Code: Pascal  [Select][+][-]
  1. Writeln('64-bit ', Buffer)

You need to compile with {$H+}.

Have you tested it? Not sure what is the result if running on 64-bit Windows (both ARM64 and AMD64).

The output is the following on both x86_64 and aarch64:

Code: [Select]
64-bit C:\WINDOWS\SysWOW64
Please note however that you can not detect whether you're running on Windows on ARM64 this way, only that you're running on a 64-bit system which is where IsWow64Process2 comes in if one doesn't want to parse an important PE file like csrss.exe as 440bx is doing.
Title: Re: Windows API test programs / Examples
Post by: FlierMate on January 31, 2021, 08:22:42 am
Just an observation, you probably don't need to GetProcAddress the function since it is implemented in Windows XP and above......
Tested on 64 bit Windows and 32 bit Windows XP, not tested on any ARM cpu..

Good catch. And thanks for the testing.

You need to compile with {$H+}.

The output is the following on both x86_64 and aarch64:

Code: [Select]
64-bit C:\WINDOWS\SysWOW64
Please note however that you can not detect whether you're running on Windows on ARM64 this way, only that you're running on a 64-bit system which is where IsWow64Process2 comes in if one doesn't want to parse an important PE file like csrss.exe as 440bx is doing.

Didn't know about the long string compiler directive, hah. And thanks a lot for helping to test it on ARM64 as well.
I admit that it would be good to know CPU architecture as well.

Can I say we have third method to check for Windows Bitness?    8)

From the reply of one of .NET user, he said he used:
Code: [Select]
public static bool Is64BitOperatingSystem { get; }
It is intriguing if System.Runtime.dll has some access to Win32 APIs, or has direct access to the operating system? Of course this is not in the scope of this series of topic discussion.

Title: Re: Windows API test programs / Examples
Post by: 440bx on January 31, 2021, 11:08:19 am
Good catch. And thanks for the testing.
You're welcome.

Can I say we have third method to check for Windows Bitness?    8)
The method you've proposed looks quite reasonable but, the fact that MS does not acknowledge it could be used to determine the O/S bitness creates some doubt about its reliability.  The same can be said about GetNativeSystemInfo, at first glance, it would also seem to be a reasonable alternative but, given the fact that MS can muck around with either leaves open some room for discomfort.

It is intriguing if System.Runtime.dll has some access to Win32 APIs, or has direct access to the operating system? Of course this is not in the scope of this series of topic discussion.
Access to the API is direct access to the O/S (at least its user mode part) and .NET most definitely has direct access to the API but, it is on the programmer's shoulders to ensure any direct access to the API doesn't violate assumptions the .NET framework makes about a .NET program.
TinyPortal © 2005-2018