Getting the Windows version revisited

 Delphi  Comments Off on Getting the Windows version revisited
Apr 222019
 

In my last blog post Getting the Windows version, I claimed that there is no way to get the actual version number of Windows 10 without reading the version information of the Kernel32.dll and interpreting it 1.

Since then I have been told that there is actually a Registry key that contains the Windows version:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

It has several entries that can be used to get the same version info that the Winver tool displays to the user. On Windows 10 that’s:

  • ProductName
  • ReleaseId
  • CurrentBuildNumber
  • UBR

On my test installation they get me
“Windows 10 Pro” Version “1809” (OS Build “17762”.”437″)

(The quotes denote values read from the registry.)

By changing the ReleaseId value I verified that Winver (at least the on my Windows 10 installation) also reads these entries.

Some of these entries are available for older versions of Windows too, while some other entries apparently are only available in older versions. On the installations I have available for tests, it looks like this:

Entry Windows 10 Windows 8.1 Windows 7
ProductName x x x
ReleaseId x
CurrentBuildNumber x x x
UBR x x
CSDVersion x

There are some other interesting entries there, but I won’t go into that any further.

So I wrote another function that reads these entries and creates a version string from it. It returns the following results:

Windows Version Output
7 Windows 7 Professional (OS Build 7601.24411) Service Pack 1
8.1 Windows 8.1 Pro (OS Build 9600.19327)
10 Windows 10 Pro Version 1809 (OS Build 17763.437)

There is one catch though: Windows 64 bit versions by default return different view of the registry to 32 bit programs than they do to 64 bit programs. In order for a 32 bit program to read the “real” registry, it must open the registry specifying the KEY_WOW64_64KEY flag in addition to the desired access.

So here is the code:

function TryGetWindowsVersionFromRegistry(out _Values: TWinCurrentRec): Boolean;
var
  Reg: TRegistry;
begin
  // We need to read the real registry, not the 32 bit view, because some of the entries
  // don't exist there.
  Reg := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY);
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    Result := Reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows NT\CurrentVersion');
    if not Result then
      Exit; //==>

    _Values.BuildLab := TRegistry_ReadString(Reg, 'BuildLab');
    _Values.BuildLabEx := TRegistry_ReadString(Reg, 'BuildLabEx');
    _Values.CSDBuildNumber := TRegistry_ReadString(Reg, 'CSDBuildNumber');
    _Values.CSDVersion := TRegistry_ReadString(Reg, 'CSDVersion');
    _Values.CurrentBuildNumber := TRegistry_ReadString(Reg, 'CurrentBuildNumber');
    _Values.CurrentVersion := TRegistry_ReadString(Reg, 'CurrentVersion');
    _Values.EditionId := TRegistry_ReadString(Reg, 'EditionId');
    _Values.ProductName := TRegistry_ReadString(Reg, 'ProductName');
    _Values.ReleaseId := TRegistry_ReadString(Reg, 'ReleaseId');
    _Values.UBR := TRegistry_ReadInteger(Reg, 'UBR');
  finally
    FreeAndNil(Reg);
  end;
end;

function GetWindowsVersionStringFromRegistry: string;
var
  Values: TWinCurrentRec;
begin
  if not TryGetWindowsVersionFromRegistry(Values) then begin
    Result := _('Unknown Windows 10 version (registry key does not exist)');
    Exit; //==>
  end;

  if Values.ProductName = '' then begin
    Result := _('Unknown Windows version (ProductName entry cannot be read)');
    Exit; //==>
  end;
  Result := Values.ProductName;
  if Values.ReleaseId <> '' then
    Result := Result + ' Version ' + Values.ReleaseId;
  if Values.CurrentBuildNumber <> '' then begin
    Result := Result + ' (OS Build ' + Values.CurrentBuildNumber;
    if Values.UBR <> 0 then
      Result := Result + '.' + IntToStr(Values.UBR);
    Result := Result + ')';
  end;
  if Values.CSDVersion <> '' then
    Result := Result + ' ' + Values.CSDVersion;
end;

That code works even with Delphi 6, but you will have to declare the constant

KEY_WOW64_64KEY        = $0100;

there since it did not exist when Delphi 6 was released. It does exist in Delphi 2007. I haven’t tried other versions.

These functions are again also available in the unit u_dzOsUtils of my dzlib library.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

—–

1 Actually I did not want to claim that there is no way but that I could not find one. ↩

 Posted by on 2019-04-22 at 15:17

Getting the Windows version

 Delphi  Comments Off on Getting the Windows version
Apr 212019
 

When Microsoft introduced Windows 10 they said it would be the last version of Windows ever. They lied of course, because since then we have had multiple versions of an operating system called “Windows 10”.

Even worse, starting with Windows 8 they made their WinAPI function GetVersionEx lie about the Windows version, if the program did not say it knows about the current version. Which means every program written with older versions of Delphi will report Windows 6.2 (which was the internal version number of Windows 8). Only Windows 8 – and later Windows 10 – compatible Delphi compilers added a manifest to the executables to tell these Windows versions they know about it.

Since many Delphi developers still use ancient versions (there are always a few GExperts downloads for Delphi 6 when I publish an update, and quite a few for Delphi 2007), this is an issue.

I still want the Bug Report Wizard to report the correct Windows version, and not just “Windows 10” but “Windows 10 version 1809” as the WinVer tool says. So I did some digging and found that apparently there are two methods for getting the true version number:

  1. Call the RtlGetVersion API function, which is meant to be called by drivers only and also only returns version 10.0 for Windows 10 so it isn’t of much use to me (but at least it doesn’t lie).
  2. Get the product version from the version information of one of the main Windows DLLs, e.g. kernel32.dll, and use the revision number to determine the Windows 10 version number from it.

Since I could not find any ready made code for this, I rolled my own (maybe I didn’t look hard enough).

And being a generous person I am sharing it with you, including all the bugs I probably have introduced.

It’s in the u_dzOsUtils unit of my dzlib library.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

EDIT: See also my follow up post Getting the Windows version revisited.

 Posted by on 2019-04-21 at 20:29

A simple way to create an URL label in Delphi

 Delphi  Comments Off on A simple way to create an URL label in Delphi
Apr 192019
 

An URL label is a TLabel on a form which displays some blue, underlined text which, when clicked with the mouse, opens a web page.

This kind of labels is nothing new in Delphi, I remember using them even back in the 1990s, but they always were a pain in the lower back to create:

  • Write the URL into the caption.
  • Set the font color to blue
  • Set the font to underlined
  • Set the cursor to crHandPoint
  • Write an OnClick event handler that opens the desired URL

Lots of boring and repetitive work and on top of that a Windows API call which I kept forgetting.
This led me to simply not use them.

Alternatively there were specialized components which you could use, so you simply dropped a TUrlLabel control on a form, filled in the caption and be done. There were quite a lot of these, and every time I wanted to use them I stopped, thought about it and didn’t. Install a package into the IDE and add another 3rd party dependency to the program just to have such a simple component? Nah!

Today, I came across these labels again on the GExperts About- and Feedback forms. I had to change some of them, and being a lazy basterd™, I decided to make this process easier once and for all. And not by adding a package / unit with a custom component but writing an overloaded pair of helper procedures which I call once and which does everything for me:

procedure TLabel_MakeUrlLabel(_lbl: TLabel); overload;
procedure TLabel_MakeUrlLabel(_lbl: TLabel; const _URL: string; _SetCaption: Boolean = False); overload;

They are used by simply calling them in the form’s constructor or in FormCreate passing a TLabel as parameter:

constructor TMyForm.Create(Owner: TComponent);
begin
  inherited;

  // if the caption already contains the URL to open:
  TLabel_MakeUrlLabel(l_SomeUrlLabel);

  // if it contains some descriptive text instead:
  TLabel_MakeUrlLabel(l_AnotherUrlLabel, 'https://blog.dummzeuch.de');

  // or if the caption should just be set in this code:
  TLabel_MakeUrlLabel(l_YetAnotherUrlLabel, 'https://blog.dummzeuch.de', True);
end;

Everything is taken care of, no changes to the label’s properties and no OnClick event handler necessary.

Here is how it is implemented:

type
  TUrlLabelHandler = class(TComponent)
  private
    FLbl: TLabel;
    FUrl: string;
    procedure HandleOnClick(_Sender: TObject);
  public
    constructor Create(_lbl: TLabel; const _URL: string); reintroduce;
  end;

constructor TUrlLabelHandler.Create(_lbl: TLabel; const _URL: string);
begin
  inherited Create(_lbl);
  FLbl := _lbl;
  FUrl := _URL;
  FLbl.OnClick := HandleOnClick;
  FLbl.Font.Style := FLbl.Font.Style + [fsUnderline];
  FLbl.Font.Color := clBlue;
  FLbl.Cursor := crHandPoint;
  if (FLbl.hint = '') and (Menus.StripHotkey(FLbl.Caption) <> FUrl) then begin
    FLbl.hint := FUrl;
    FLbl.ShowHint := True;
  end;
end;

procedure TUrlLabelHandler.HandleOnClick(_Sender: TObject);
begin
  ShellExecute(Application.Handle, 'open', PChar(FUrl), nil, nil, SW_SHOWNORMAL);
end;

procedure TLabel_MakeUrlLabel(_lbl: TLabel);
begin
  TLabel_MakeUrlLabel(_lbl, Menus.StripHotkey(_lbl.Caption), False);
end;

procedure TLabel_MakeUrlLabel(_lbl: TLabel; const _URL: string; _SetCaption: Boolean = False);
begin
  if _SetCaption then
    _lbl.Caption := _URL;
  TUrlLabelHandler.Create(_lbl, _URL);
end;

Not rocket science either. All it does is setting some properties and then it uses a simple trick to attach an OnClick event handler to the label: It creates a specialized TComponent descendant which sets the label as its owner and assigns one of its methods as the OnClick event handler. Since the component is owned by the label, it will take care of freeing it, so no further action required and no memory leak possible. The OnClick event then executes the WinApi call I mentioned above and which I kept forgetting:

  ShellExecute(Application.Handle, 'open', PChar(FUrl), nil, nil, SW_SHOWNORMAL);

I like neat tricks like those: Call and forget.

For your (or actually my own) convenience I have added it to the unit u_dzVclUtils of my dzlib. Feel free to use it.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

 Posted by on 2019-04-19 at 19:35

Image Processing Magic

 Delphi  Comments Off on Image Processing Magic
Apr 162019
 

It’s a bit frightening what image processing “magic” can do to a picture:

Original:

Processed:

And that’s not even advanced “magic” but some rather simple code written in Delphi (most of which I can’t publish here unfortunately).

What it does is:

  • Smooth brigthness by applying a brigthness map.
  • Adjust for lens edge blur by selective sharpening.
  • Improve contrast by histogram stretching.

Of course since this is a gray scale picture, the processing is much simpler than it would be for colour pictures.

 Posted by on 2019-04-16 at 11:57

Deleting unwanted entries from the “New” submenu of Windows Explorer

 Batch, Windows  Comments Off on Deleting unwanted entries from the “New” submenu of Windows Explorer
Apr 092019
 

Microsoft Office has the annoying habit of adding itself to various places in the Windows user interface. One of them is the popup menu of the Windows Explorer. It adds one entry for each of the installed programs to the “New” submenu.

Have you ever used them? I have only used that submenu for creating folders, shortcuts and text files, but never ever (apart from trying it once I think) for creating new MS Office documents.

So I have removed these entries several times (every time having to google the process) but every update recreates the entries and today I’ve had enough.

So here is a cmd file which will delete them (works for Office 2016 only). You will have to run it as administrator to work.

@echo off
echo This batch file will delete the Registry entries for
echo the "New" submenu of Windows Explorer
echo added by MS Office 2016.
echo This batch file must be run as Administrator.
echo Continue?
pause
reg delete HKEY_CLASSES_ROOT\.docx\Word.Document.12\ShellNew /f
reg delete HKEY_CLASSES_ROOT\.xlsx\Excel.Sheet.12\ShellNew /f
reg delete HKEY_CLASSES_ROOT\.pptx\PowerPoint.Show.12\ShellNew /f
pause

download link

It uses the built in reg command. The /f switch forces the deletion without confirmation. Maybe you want to remove it.

As always: Check any exectuable code you download from the Internet! It might damage your computer.

 Posted by on 2019-04-09 at 11:27

How to defeat useful tools

 Windows  Comments Off on How to defeat useful tools
Apr 042019
 

Sizer is a very useful tool for sizing and positioning windows. It adds a right click menu to a window’s title bar where you can select one of multiple configured window sizes and positions. It also displays the current window size while you are resizing it by dragging its frame.

Now, why would somebody try to defeat the usability of this tool?

Because they can?

Google and Mozilla did exactly that: By moving the tab bar into the window title. In Chrome, Thunderbird and Firefox right clicking on the title bar no longer brigs up the Sizer menu. In Chrome at least clicking on the window frame still gets this menu. Both browsers also prevent the size display while dragging the frame which at least works for Thunderbird.

And now guess which windows I would like to use Sizer the most?

Some software developers should be shot, just so they don’t have a bad influence on others.

(Since nowadays it apparently is necessary to explicitly say that: No, I don’t want anybody to take this as an ecouragement to commit a felony or act of terror.)

 Posted by on 2019-04-04 at 10:16

Extend the event OnClick of all MenuItems in the screen to execute another block of code

 Delphi, GExperts  Comments Off on Extend the event OnClick of all MenuItems in the screen to execute another block of code
Apr 032019
 

Shameless self promotion:

On StackOverflow somebody asked the question in the title and I answered it. And as you might have guessed, I am mighty proud of having had that idea.

It might be interesting to know that this is how GExperts fixes some of the IDE bugs and enhances some forms: It creates a TComponent descendant which hooks some events of the form to implement the fixes or enhancements. And since it is added to the form, it automatically gests freed in the form’s destructor.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

 Posted by on 2019-04-03 at 12:18

Things that every desktop program should do

 Delphi  Comments Off on Things that every desktop program should do
Apr 032019
 

There are a few things that every single one of our internal programs should do:

  • Every dialog should be sizeable if it makes any sense at all. Delphi makes that easy with the Align and Anchor properties. Ask yourself the following questions. If you answer any of them with Yes, you should make the dialog sizeable:
    • Is there a list with an unkown number of entries?
    • Is there a text entry field which can contain very long text (e.g. a file name)?
  • If a dialog is sizeable, set a minimum window size that allows all controls to be visible in a reasonable size.
  • If a dialog is sizeable, you should test it after every change. It is embarrassing if one button does not move the way it should.
  • If a dialog is sizeable, it should store its position and size and load it when it is being opened again. But keep in mind, that the screen sizes and arrangements can vary from one computer to another and can even change on the same computer because somebody rearranged the physical positions of his monitors or got a new one.
    • Do not store the position and size centrally for all users. If you do that, some of them will hate you. I prefer HKCU in the registry for that.
    • Always make sure that the dialog is fully visible on a monitor. A dialog that opens outside the visible area is an embarrasment and also a support nightmare.
  • Make it easy for the user to find version information. I prefer having it right in the main window’s title. e.g. “My Super Application [1.0.1.532 2019-04-03]”. The date here comes from the project version which our build scripts automatically fill with the build date.

There are a few functions in the unit u_dzVclUtils of my dzlib that make this a lot easier. They are usually called in the form’s constructor:

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

 Posted by on 2019-04-03 at 11:55

Changes between GExperts 1.3.12 and 1.3.13

 Delphi, GExperts  Comments Off on Changes between GExperts 1.3.12 and 1.3.13
Mar 312019
 

I had to look them up myself, so it took a while but here they are, the changes, bugfixes and improvements between the GExperts 1.3.12 (released 2018-12-22) and 1.3.13 (released yesterday, 2019-03-30):

  • Bugfix (#105): Set Tab Order expert no longer worked with Delphi 6, 7 and 2005 (Remember what I wrote about testing these versions?)
  • Improvements to the Uses Clause Manager:
    • New configuration setting “GExperts caching directory”, used to store the cached identifiers per unit
    • Additional configuration options for the expert: Caching can now be disabled and the cache can be cleared. (Bug report #104)
    • It is now possible to use the project’s map file rather than the dpr for getting a list of used units. This includes the VCL/RTL and possibly any 3rd party units and makes the Project tab much more useful.
    • Bugfix (#110): Entries in the VCL/RTL list were clipped / overlapped.
  • Bugfix (#109): When loading a form’s position only, Width and Height no longer change every time.
  • Added additional Delphi 10.2 and 10.3 warnings to the Insert Warn Directive editor expert
  • Bugfix: The number of entries for the Favourite Files expert in the registry doubled every time the list was saved.
  • Improvements to the Grep expert:
    • Hint about separating multiple directories with semicolon.
    • It can now use the project’s map file rather than the dpr for getting the list of used units. This includes all units compiled in the project not just those explicitly added to it. There is a configuration option for enabling that.
    • Ensure that the RTL and all existing subsystem Paths (VLC, FMX, CLX) are in the drop down menu.
    • Searching DFM files can now handle strings split into multiple lines and containing special characters stored as #<number>. (Note: This is not quite finished. The display in the Results window is broken.) (Bug report #112)
  • Custom beep as a WAV file for the Proof Reader expert (Suggested by Philip Rayment, Patch by Achim Kalwa, Bug report #111).
  • Bugfix (#113): Added another check for duplicate GExperts DLLs being loaded into the same IDE to prevent error message “PrivateGXMenuActionManager is not nil upon creation”
  • Project Dependency expert:
    • Dramatically improved performance for the indirect dependencies tab. The strings and string lists are no longer stored in and read from the UI. Also string lists are now sorted so look ups are much faster.
    • Units in the project (root node of the tree view are now also listed)
    • New configuration option to search for units in the library path and also in the browsing path.
  • Bugfix (#114): Disabled editor experts still blocked the associated keyboard shortcut and could be called with this shortcut.
  • Bugfix (#117): Editor experts could be executed even if not the code editor but the form editor was active.

As you can see above, I am looking into bugs reported on Sourceforge and also feature suggestions posted there. That is my preferred way of users to report bugs and request features because it is easy for me to manage them in one place. The other channels (email via the GExperts “Send a Bug Report / Suggestion” button and the GExperts subforum in the international Delphi Praxis forum) work too, but they are much less convenient for me and some reports / requests may get lost. Also, please remember that I am working on this project in my spare time, so any additional work, e.g. creating bug reports on Sourceforge from emails I receive, takes time in which I am not working on the actual software. It’s also boring work which might discourage me from doing it and if it is getting out of hand might even make me stop working on GExperts at all.

Let me close this post by thanking all those people who have worked on GExperts before me and on whose work I have been building over the years (and whose work I have also been using as a GExperts user for more than two decades as a professional software developer). And also those who continue to contribute their work and some money to this project.

Discussion about this post in the international Delphi Praxis forum.

 Posted by on 2019-03-31 at 13:56

GExperts 1.3.13 experimental twm 2019-03-30 released

 Delphi, GExperts  Comments Off on GExperts 1.3.13 experimental twm 2019-03-30 released
Mar 302019
 

Just in time before April fools day 2019 there is the new GExperts release (it’s still 2019-03-30 so you are safe 😉 ).

Please be aware that I mostly work with Delphi 2007, so this version can be regarded as tested quite well, followed by Delphi XE2. The others are only known to compile and new features are usually tested superficially with all versions. This is particularly true for Delphi 6/7 and 2005/2006.

If you want to help by testing new versions before I release them, please contact me e.g. via Delphi Praxis (link on the side bar) or via the Send Message button on my Sourceforge profile. You may also consider contributing to GExperts. You are supposedly a Delphi developer yourself and it’s not rocket science after all.

Head over to the Experimental GExperts page to download the latest release.

I will blog about the new features later. I have to look them up myself. 😉

Discussion about this post in the international Delphi Praxis forum.

https://en.delphipraxis.net/topic/917-gexperts-1313-experimental-twm-2019-03-30-released/

 Posted by on 2019-03-30 at 18:05