From 848d284bdc4450c811333bdc875213e658a54156 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 11:54:58 +0100 Subject: [PATCH 01/23] Split Docs/ReadMe.txt into two different versions Split into ReadMe-portable.txt relating to Portable edition and ReadMe-standard.txt for Standard edition. Both files were edited according to new purpose and to update content where necessary. --- Docs/ReadMe-portable.txt | 257 +++++++++++++++++++++++ Docs/{ReadMe.txt => ReadMe-standard.txt} | 174 ++++----------- 2 files changed, 303 insertions(+), 128 deletions(-) create mode 100644 Docs/ReadMe-portable.txt rename Docs/{ReadMe.txt => ReadMe-standard.txt} (64%) diff --git a/Docs/ReadMe-portable.txt b/Docs/ReadMe-portable.txt new file mode 100644 index 000000000..e0883fa5c --- /dev/null +++ b/Docs/ReadMe-portable.txt @@ -0,0 +1,257 @@ +================================================================================ + +DELPHIDABBLER CODESNIP v4 PORTABLE EDITION README + +================================================================================ + + +What is CodeSnip? +================================================================================ + +DelphiDabbler CodeSnip 4 is a code snippets repository targetted at the Pascal / +Delphi programming languages. It can download and display code snippets from the +online DelphiDabbler Code Snippets database as well as maintain a database of +user-defined snippets. + +It displays details of each snippet in the database and can test-compile them +with each installed Win32 version of Delphi from Delphi 2 to Delphi 12.x and +Free Pascal. + +Compilable Pascal units can be created that contain selected snippets. + + +CodeSnip Editions +================================================================================ + +This document relates to the PORTABLE edition of CodeSnip. This edition can be +run from any writeable removable storage medium (e.g. a USB memory stick) or +from any folder on the computer's hard disk. It makes no changes to the host +computer. + +There is also a standard edition of the program. This edition is installed on +the user's computer using an installer. It records its presence in the registry +and stores data in the system's application and user data directories. You can +get the standard edition from the same place you downloaded the this edition. + +You can run both the standard and portable editions together on the same +computer and even run them at the same time. However, each edition maintains its +own settings and keeps its own copies of the snippets databases. To share user +defined snippets you must export them from one edition and import into the +other. CodeSnip provides no mechanism for keeping them synchronised. + + +Installation +================================================================================ + +CodeSnip requires Windows 2000 or later. It also requires MS Internet Explorer 6 +or later, although IE 8, 9 or 10 are strongly recommended. Note that recent +releases have only been tested on Windows 11. + +The portable edition of CodeSnip 4 is distributed in a zip file that contains +the program executable, the help file and various documentation files. + +Install the program using the following steps: + +1) Mount any storage medium on which you want to install CodeSnip. + +2) Create a folder on the storage medium or on your computer's internal disk in + which to copy the required files. + +3) Copy the files CodeSnip-p.exe (the executable program) and CodeSnip.chm + (the help file) into the folder you created. + + CodeSnip does not need the other files included in the zip file in order to + run, but you may find them useful. Copy them if you wish. + +Run the program by double clicking it. When it first runs it will create two +sub-directories within the folder where you installed the program. These will +be named AppData and UserData. Do not remove these directories or alter any of +the contents because CodeSnip uses them to store configuration data along with +your code snippets. + +No files are written outside the folder where you copied the files and the +registry is not modified. + +** WARNING: When updating an existing portable installation with a new version +of CodeSnip it is important that you do not change or delete the AppData and +UserData folders. If you do this you risk loosing your settings and/or database. + + +Uninstallation +================================================================================ + +Simply delete the folder where you installed the portable edition of CodeSnip +along with all its contents. + +Be aware that any snippets you have created will be lost. If you want to keep +them for use in another CodeSnip installation, either export them or back up the +user database before deleting the folder. See the help file for details of how +to do this. + + +Downloading & Updating the Code Snippets Database +================================================================================ + +The online DelphiDabbler Code Snippets database is not installed with the +program. + +CodeSnip's start-up screen shows details of any installed databases. If there is +no copy of the online database then a link is displayed that enables the +database to be installed. This link opens the "Install or Update DelphiDabbler +Snippets Database" wizard dialogue box. The dialogue box explains how to +download and install the database. + +You can download or update the database later by opening the same dialogue box +using the "Database | Install or Update DelphiDabbler Snippets Database" menu +option. + + +Configuring CodeSnip to Work With Your Compilers +================================================================================ + +A feature of CodeSnip is its ability to test compile snippets with any installed +Windows 32 version of Delphi (from Delphi 2 to Delphi.x) and FreePascal, +providing some simple rules are followed. + +When CodeSnip is first installed it knows nothing about the available compilers +and so test compilations cannot be performed. If any supported Delphi compiler +is detected when the program is first run you will be given the option of +registering it. This does not work for Free Pascal. + +You can also tell CodeSnip about the available compilers by using the "Tools | +Configure Compilers" menu option. The resulting dialogue can automatically +detect all installed versions of supported Delphi compilers at the click of a +button. Free Pascal, where installed, must be set up manually. The Welcome page +displays a list of compilers it has been configured to work with. + +Compilers that do not use English as their output language will need further +configuration. See the help file for information (look up "configure compilers +dialogue" in the help file index). + +Each user can configure compilers differently. + +Delphi XE2 and later may need to be configured to search for required units in +the correct namespaces. This is explained in the Add/Edit Snippet Dialogue Box +help topic and in the FAQ at +https://github.com/delphidabbler/codesnip-faq/blob/master/UsingCodeSnip.md#faq-7 + +Any type of snippet other than "freeform" can be test compiled. + + +Updating the Program +================================================================================ + +Updates are published on GitHub. See +https://github.com/delphidabbler/codesnip/releases + +News of new updates is published on the CodeSnip Blog: +https://codesnip-app.blogspot.com/. + + +Known Installation and Upgrading Issues +================================================================================ + ++ If you have updated to CodeSnip v4.2.0 or later from any earlier v4 release, + and then run the earlier version of the program again, its saved main window + state, size, position and layout will have been lost and the program will + display in its default size. + ++ If you have updated to CodeSnip v4.3.0 or later from v4.2.x or earlier any -NS + command line options you have specified on the "Switches" (aka "Command Line") + tab of the Configure Compilers dialogue box for Delphi XE2 or later will be + removed and equivalent entries will have been made on the "Namespaces" tab. + ++ CodeSnip v4.16.0 and later cannot be registered. Any previous registration + information may be lost. + + +License & Disclaimer +================================================================================ + +CodeSnip is made available under the terms of the Mozilla Public License v2.0. +The license is explained in full in the file License.html that is installed with +CodeSnip. A summary of the license can be viewed from the "Help | License" menu +option. + +CodeSnip is supplied on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either +express or implied. See License.html for details. + +The source code of any snippet managed by CodeSnip, whether from the +DelphiDabbler Code Snippets Database or the user database, is used WITHOUT +WARRANTY OF ANY KIND, either express or implied. The code is used entirely at +the user's own risk. + +The snippets from the DelphiDabbler Code Snippets Database are open source. See +the "About The Database" tab of the About dialogue box for details of the +applicable license. (You can display the About box from the "Help" menu.) + +The user is responsible to ensure that any code snippets managed by CodeSnip are +used in accordance with any applicable license. + + +Source Code +================================================================================ + +CodeSnip's source code is freely available. For details of how to obtain the +source see the FAQ at +https://github.com/delphidabbler/codesnip-faq/blob/master/SourceCode.md#faq-1 + +The portable edition of CodeSnip shares the same source code base with the +standard edition. + +The original source code of v4 is released under the Mozilla Public license +v2.0 (see https://www.mozilla.org/MPL/) and other open source licenses. See the +file "License.html" in the "Docs" directory of the repository for full licensing +information. + + +Bugs & Feature Requests +================================================================================ + +Please do report any bugs you find. Suggestions for new features are also +welcomed. + +Both bug reports and feature requests are made using the GitHub issue tracker +(GitHub account required). For details about using the issue tracker see +https://github.com/delphidabbler/codesnip/blob/master/CONTRIBUTING.md#issues. + + +FAQs +================================================================================ + +There are Frequently Asked Questions pages for CodeSnip on the web, at +https://github.com/delphidabbler/codesnip-faq/blob/master/README.md + + +Privacy +================================================================================ + +From v4.16.0 CodeSnip neither stores nor transmits any personally identifiable +data. + +Do note though that CodeSnip can display web pages via your default web browser, +but only in response to user input. No guarantee is made about any personal data +collected by such web pages. + + +Thanks +================================================================================ + +Thanks to: + ++ David Mustard and Bill Miller for providing information that enabled me to add + Delphi 2007 and Delphi 2009 support, respectively, to the program. + ++ geoffsmith82 and an anonymous contributor for information about getting + CodeSnip to work with Delphi XE2. + ++ The authors of the third party source code and images used by the program. See + the program's about box or License.html for details. + ++ Various contributors to the DelphiDabbler Code Snippets database. Names of + contributors are listed in the program's About Box (use the "Help | About" + menu option then select the "About the Database" tab). The list will be empty + if the Code Snippets Database has not been installed. + + +================================================================================ diff --git a/Docs/ReadMe.txt b/Docs/ReadMe-standard.txt similarity index 64% rename from Docs/ReadMe.txt rename to Docs/ReadMe-standard.txt index b7806db53..5f5ea703f 100644 --- a/Docs/ReadMe.txt +++ b/Docs/ReadMe-standard.txt @@ -1,6 +1,6 @@ ================================================================================ -DELPHIDABBLER CODESNIP v4 README +DELPHIDABBLER CODESNIP v4 STANDARD EDITION README ================================================================================ @@ -14,30 +14,26 @@ online DelphiDabbler Code Snippets database as well as maintain a database of user-defined snippets. It displays details of each snippet in the database and can test-compile them -with each installed Win32 version of Delphi from Delphi 2 to Delphi 12 Athens -and Free Pascal. +with each installed Win32 version of Delphi from Delphi 2 to Delphi 12.x and +Free Pascal. Compilable Pascal units can be created that contain selected snippets. -Features new to CodeSnip 4 are listed in the "What's New In CodeSnip 4" topic -in the program's help file. - CodeSnip Editions ================================================================================ -There are two different editions of CodeSnip 4 available: - -+ The standard edition, which is installed on the user's computer using an - installer and which records its presence in the registry and stores data in - the system's application and user data directories. +This document relates to the STANDARD edition of CodeSnip. This edition is +installed on the user's computer using a standard Windows installer and which +records its presence in the registry and stores data in the system's application +and user data directories. -+ The portable edition that can be run from any writeable removable storage - medium (e.g. a USB memory stick) and that makes no changes to the host - computer. This edition has no installer and is simply copied onto the required - medium. +There is also a portable edition of the program. This edition can be run from +any writeable removable storage medium (e.g. a USB memory stick) or from any +folder on the computer's hard disk. It makes no changes to the host computer. +This edition has no installer and is simply copied to the required location. -You can run both the standard and portable editions together on the same +You can run both the portable and standard editions together on the same computer and even run them at the same time. However, each edition maintains its own settings and keeps its own copies of the snippets databases. To share user defined snippets you must export them from one edition and import into the @@ -48,18 +44,14 @@ Installation ================================================================================ CodeSnip requires Windows 2000 or later. It also requires MS Internet Explorer 6 -or later, although IE 8, 9 or 10 are strongly recommended. But note that recent -releases have only been tested on Windows 10/11. +or later, although IE 8, 9 or 10 are strongly recommended. Note that recent +releases have only been tested on Windows 11. -Installing the Standard Edition -------------------------------- - -You will need administrator privileges to run the setup program for the standard -edition. If you are using a non-admin user account on Windows 2000 or XP you -should run setup as administrator. By default Windows Vista to Windows 11 will -require an admin password if running as a standard user and setup will attempt -to elevate the process. If UAC prompts are disabled you must run setup as -administrator. +You will need administrator privileges to run the setup program. If you are +using a non-admin user account on Windows 2000 or XP you should run setup as +administrator. By default Windows Vista to Windows 11 will require admin +privileges and setup will attempt to elevate the process if required. If UAC +prompts are disabled you must run setup as administrator. CodeSnip v4 will install alongside any v3 or earlier release that may already be installed. If you want to replace the earlier version simply uninstall it in the @@ -106,53 +98,15 @@ If you are updating to CodeSnip 4 from version 3 or earlier, CodeSnip will give you the option of bringing forward your old settings and / or user defined database. This happens the first time v4 is run for each user. -Installing the Portable Edition -------------------------------- - -The portable edition of CodeSnip 4 is distributed in a zip file that contains -the program executable, the help file and various documentation files. - -Install the program using the following steps: - -1) Mount any storage medium on which you want to install CodeSnip. - -2) Create a folder on the storage medium or on your computer's internal disk in - which to copy the required files. - -3) Copy the files CodeSnip-p.exe (the executable program) and CodeSnip.chm - (the help file) into the folder you created. - - CodeSnip does not need the other files included in the zip file in order to - run, but you may find them useful. Copy them if you wish. - -Run the program by double clicking it. When it first runs it will create two -sub-directories within the folder where you installed the program. These will -be named AppData and UserData. Do not remove these directories or alter any of -the contents. CodeSnip uses them to store configuration data along with your -code snippets. - -No files are written outside the folder where you copied the files and the -registry is not modified. - -** WARNING: When updating an existing portable installation with a new version -of CodeSnip it is important that you do not change or delete the AppData and -UserData folders. If you do this you risk loosing your settings and/or database. - Uninstallation ================================================================================ -Uninstalling the Standard Edition ---------------------------------- - -CodeSnip can be uninstalled via "Installed Apps" (a.k.a. "Apps and Features", -a.k.a. "Programs and Features", a.k.a. "Add / Remove Programs") accessed from the -Windows Control Panel or by choosing "Uninstall DelphiDabbler CodeSnip" from the -program's start menu group. +CodeSnip can be uninstalled using your version of Windows' application +uninstaller, run from Control Panel. Alternatively you can choose "Uninstall +DelphiDabbler CodeSnip" from the program's start menu group. -Administrator privileges will be required to uninstall CodeSnip. Windows Vista -to Windows 11 with UAC prompts enabled will prompt for an admin password if -necessary. +Administrator privileges will be required to uninstall CodeSnip. The uninstall program will delete any local copy of the online Code Snippets database but will leave any user defined database, configuration data and @@ -161,16 +115,6 @@ delete the %AppData%\DelphiDabbler\CodeSnip.4 directory and all its contents for each user who ran CodeSnip. If any user has moved the user database directory those directories also need to be deleted. -Uninstalling the Portable Edition ---------------------------------- - -Simply delete the folder where you installed CodeSnip, with all its contents. - -Be aware that any snippets you have created will be lost. If you want to keep -them for use in another CodeSnip installation either export them or back up the -user database before deleting the folder. See the help file for details of how -to do this. - Downloading & Updating the Code Snippets Database ================================================================================ @@ -179,22 +123,19 @@ The online DelphiDabbler Code Snippets database is not installed with the program. CodeSnip's start-up screen shows details of any installed databases. If there is -no copy of the online database a link is displayed that enables the database to -be installed. This link opens the "Install or Update DelphiDabbler Snippets -Database" wizard style dialogue box. The dialogue box explains how to download -and install the database. +no copy of the online database then a link is displayed that enables the +database to be installed. This link opens the "Install or Update DelphiDabbler +Snippets Database" wizard dialogue box. The dialogue box explains how to +download and install the database. You can download or update the database later by opening the same dialogue box using the "Database | Install or Update DelphiDabbler Snippets Database" menu option. -Standard Edition Only ---------------------- - -When installing the standard edition, the setup program will detect if an older -database installation is present and will give the option to carry it forward. -When setup completes it checks for the presence of the database and puts up a -message if it is not present. +During installation the setup program will detect if an older database version +is present and will give the option to carry it forward. When setup completes it +checks for the presence of the database and puts up a message if it is not +present. Database updates will apply to all users of the computer the next time they start CodeSnip. @@ -204,7 +145,7 @@ Configuring CodeSnip to Work With Your Compilers ================================================================================ A feature of CodeSnip is its ability to test compile snippets with any installed -Windows 32 version of Delphi (from Delphi 2 to Delphi 12 Athens) and FreePascal, +Windows 32 version of Delphi (from Delphi 2 to Delphi.x) and FreePascal, providing some simple rules are followed. When CodeSnip is first installed it knows nothing about the available compilers @@ -235,7 +176,7 @@ Any type of snippet other than "freeform" can be test compiled. Updating the Program ================================================================================ -Updates are published on GitHub. See +Updates are published on GitHub. See https://github.com/delphidabbler/codesnip/releases News of new updates is published on the CodeSnip Blog: @@ -288,7 +229,7 @@ DelphiDabbler Code Snippets Database or the user database, is used WITHOUT WARRANTY OF ANY KIND, either express or implied. The code is used entirely at the user's own risk. -The snippets from the DelphiDabbler Code Snippets Database is open source. See +The snippets from the DelphiDabbler Code Snippets Database are open source. See the "About The Database" tab of the About dialogue box for details of the applicable license. (You can display the About box from the "Help" menu.) @@ -303,7 +244,8 @@ CodeSnip's source code is freely available. For details of how to obtain the source see the FAQ at https://github.com/delphidabbler/codesnip-faq/blob/master/SourceCode.md#faq-1 -The standard and portable editions of CodeSnip share the same source code. +The standard edition of CodeSnip shares the same source code base with the +portable edition. The original source code of v4 is released under the Mozilla Public license v2.0 (see https://www.mozilla.org/MPL/) and other open source licenses. See the @@ -311,36 +253,15 @@ file "License.html" in the "Docs" directory of the repository for full licensing information. -Bugs +Bugs & Feature Requests ================================================================================ -Please do report any bugs you find. - -Bugs are recorded in tracker software. View the reported and fixed bugs via -https://github.com/delphidabbler/codesnip/issues (GitHub account required). - -You can also access the bug tracker from CodeSnip by using the "Tools | Report -Bug Online" menu option then following the link that appears in the resulting -dialogue box. - -If you wish to report a bug, please check the current reports on the bug -tracker. If your bug hasn't already been reported or fixed please add a report -using the "Add new" link on Tracker. +Please do report any bugs you find. Suggestions for new features are also +welcomed. -Please ensure that you have installed the latest version of CodeSnip and checked -if the bug is still present before reporting it. - - -Feedback -================================================================================ - -If you want to suggest new features please use the feature request tracker -accessed from https://github.com/delphidabbler/codesnip/issues (GitHub account -required). Please check whether anyone else has requested something similar and -add a comment to their request if so. - -Always check the latest version of CodeSnip before requesting a feature just in -case it has already been implemented! +Both bug reports and feature requests are made using the GitHub issue tracker +(GitHub account required). For details about using the issue tracker see +https://github.com/delphidabbler/codesnip/blob/master/CONTRIBUTING.md#issues. FAQs @@ -353,15 +274,12 @@ https://github.com/delphidabbler/codesnip-faq/blob/master/README.md Privacy ================================================================================ -As of v4.16.0 CodeSnip no longer stores or transmits any personally identifiable +From v4.16.0 CodeSnip neither stores nor transmits any personally identifiable data. -Because of this change the privacy statement that used to be provided with the -program has been removed. - -Do note though that CodeSnip can display web pages via your default web -browser, but only in response to user input. No guarantee is made about any -personal data collected by such web pages. +Do note though that CodeSnip can display web pages via your default web browser, +but only in response to user input. No guarantee is made about any personal data +collected by such web pages. Thanks From 7f2f758efc9ef2785c51a92a0cee0c266ac330fe Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:09:29 +0100 Subject: [PATCH 02/23] Release different ReadMe.txt for standard & portable Makefile updated to copy appropriate ReadMe-portable.txt or ReadMe-standard.txt as ReadMe.txt to release zip files for portable and standard edition builds respectively. Made similar changes to Inno Setup script to copy ReadMe-standard.txt as ReadMe.txt into the install program. Both versions of ReadMe.txt were temporarily stored in new _build\release\~tmp~ directory. --- Src/Install/CodeSnip.iss | 3 ++- Src/Makefile | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Src/Install/CodeSnip.iss b/Src/Install/CodeSnip.iss index 47a85f471..3e154e1d0 100644 --- a/Src/Install/CodeSnip.iss +++ b/Src/Install/CodeSnip.iss @@ -29,6 +29,7 @@ #define SrcDocsPath SourcePath + "..\..\Docs\" #define SrcAssetsPath SourcePath + "\Assets\" #define SrcExePath SourcePath + "..\..\_build\exe\" +#define TmpPath SourcePath + "..\..\_build\release\~tmp~\" #define ProgDataSubDir AppName + ".4" #define ExeProg SrcExePath + ExeFile #define AppVersion DeleteToVerStart(GetFileProductVersion(ExeProg)) @@ -89,7 +90,7 @@ Name: {commonappdata}\{#AppPublisher}\{#ProgDataSubDir}\Database; permissions: e Source: {#SrcExePath}{#ExeFile}; DestDir: {app} Source: {#SrcExePath}{#HelpFile}; DestDir: {app}; Flags: ignoreversion Source: {#SrcDocsPath}{#LicenseTextFile}; DestDir: {app}; Flags: ignoreversion -Source: {#SrcDocsPath}{#ReadMeFile}; DestDir: {app}; Flags: ignoreversion +Source: {#TmpPath}{#ReadMeFile}; DestDir: {app}; Flags: ignoreversion Source: {#SrcAssetsPath}UpdatingPreview.rtf; Flags: dontcopy [Icons] diff --git a/Src/Makefile b/Src/Makefile index 17b443abf..ce6cb8cf8 100644 --- a/Src/Makefile +++ b/Src/Makefile @@ -12,12 +12,15 @@ BUILD_ROOT = _build BIN_ROOT = $(BUILD_ROOT)\bin EXE_ROOT = $(BUILD_ROOT)\exe RELEASE_ROOT = $(BUILD_ROOT)\release +RELEASE_TMP_ROOT = $(RELEASE_ROOT)\~tmp~ DOCS_ROOT = Docs SRC_ROOT = Src # Defines macros giving directories relative to location of the Makefile BIN_REL = ..\$(BIN_ROOT) EXE_REL = ..\$(EXE_ROOT) +DOCS_REL = ..\$(DOCS_ROOT) +RELEASE_TMP_REL = ..\$(RELEASE_TMP_ROOT) # Check for required environment variables @@ -115,6 +118,7 @@ config: @mkdir $(BIN_ROOT) @if not exist $(EXE_ROOT) mkdir $(EXE_ROOT) @if not exist $(RELEASE_ROOT) mkdir $(RELEASE_ROOT) + @if not exist $(RELEASE_TMP_ROOT) mkdir $(RELEASE_TMP_ROOT) @cd $(SRC_ROOT) # Builds CodeSnip pascal files and links program @@ -160,8 +164,10 @@ typelib: # Builds setup program setup: !ifndef PORTABLE - @del $(EXE_REL)\CodeSnip-Setup-* + copy $(DOCS_REL)\ReadMe-standard.txt $(RELEASE_TMP_REL)\ReadMe.txt + del $(EXE_REL)\CodeSnip-Setup-* @$(ISCC) Install\CodeSnip.iss + del $(RELEASE_TMP_REL)\ReadMe.txt !else @echo **** Portable build - no setup file created **** !endif @@ -195,12 +201,16 @@ release: @cd .. -@if exist $(OUTFILE) del $(OUTFILE) !ifndef PORTABLE - @$(ZIP) -j -9 $(OUTFILE) $(EXE_ROOT)\CodeSnip-Setup-*.exe $(DOCS_ROOT)\ReadMe.txt + copy $(DOCS_ROOT)\ReadMe-standard.txt $(RELEASE_TMP_ROOT)\ReadMe.txt + @$(ZIP) -j -9 $(OUTFILE) $(EXE_ROOT)\CodeSnip-Setup-*.exe $(RELEASE_TMP_ROOT)\ReadMe.txt + del $(RELEASE_TMP_ROOT)\ReadMe.txt !else + copy $(DOCS_ROOT)\ReadMe-portable.txt $(RELEASE_TMP_ROOT)\ReadMe.txt @$(ZIP) -j -9 $(OUTFILE) $(EXE_ROOT)\CodeSnip-p.exe @$(ZIP) -j -9 $(OUTFILE) $(EXE_ROOT)\CodeSnip.chm - @$(ZIP) -j -9 $(OUTFILE) $(DOCS_ROOT)\ReadMe.txt + @$(ZIP) -j -9 $(OUTFILE) $(RELEASE_TMP_ROOT)\ReadMe.txt @$(ZIP) -j -9 $(OUTFILE) $(DOCS_ROOT)\License.html + del $(RELEASE_TMP_ROOT)\ReadMe.txt !endif @cd $(SRC_ROOT) From dc7cb79e3c82c94a6e8526949c73fec5957c01e3 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:49:25 +0100 Subject: [PATCH 03/23] Update copyright date in header comments --- Src/Install/CodeSnip.iss | 2 +- Src/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/Install/CodeSnip.iss b/Src/Install/CodeSnip.iss index 3e154e1d0..229db969a 100644 --- a/Src/Install/CodeSnip.iss +++ b/Src/Install/CodeSnip.iss @@ -2,7 +2,7 @@ ; v. 2.0. If a copy of the MPL was not distributed with this file, You can ; obtain one at https://mozilla.org/MPL/2.0/ ; -; Copyright (C) 2006-2022, Peter Johnson (gravatar.com/delphidabbler). +; Copyright (C) 2006-2024, Peter Johnson (gravatar.com/delphidabbler). ; ; Install file generation script for use with Inno Setup. diff --git a/Src/Makefile b/Src/Makefile index ce6cb8cf8..b8b69e3b5 100644 --- a/Src/Makefile +++ b/Src/Makefile @@ -2,7 +2,7 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/ # -# Copyright (C) 2009-2022, Peter Johnson (gravatar.com/delphidabbler). +# Copyright (C) 2009-2024, Peter Johnson (gravatar.com/delphidabbler). # # Makefile for the CodeSnip project. From bcaf6cc90084ff19a45480904d4227dccab4ea90 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:08:59 +0100 Subject: [PATCH 04/23] Update Build.html re changes per issue #127 Updated re change from using single ReadMe.txt to using a version of the files for each edition. Made some other changes for clarity. --- Build.html | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Build.html b/Build.html index fc6947166..0f96ec05e 100644 --- a/Build.html +++ b/Build.html @@ -723,9 +723,13 @@

- The release file for the standard edition of CodeSnip includes the - setup file along with ReadMe.txt from the Docs - directory. Both files must exist. + The release zip file for the standard edition requires that the setup files is already + present in the _build/exe directory. +

+ +

+ The release file includes the setup file along with ReadMe.txt + that is automatically generated from Docs/ReadMe-standard.txt.

@@ -752,9 +756,16 @@

- The release file for the portable edition includes the portable executable - file, CodeSnip-p.exe, the help file CodeSnip.chm and - several files from the Docs directory. All must be present. + The release zip file for the portable edition cannot be created until the + CodeSnip excutable and the compiled help file are already present in the + _build\exe directory. +

+ +

+ The release file includes the portable executable file, CodeSnip-p.exe, + the help file CodeSnip.chm, Docs/License.html and + ReadMe.txt that is automatically generated from + Docs/ReadMe-portable.txt.

From 4e120754407cde6a4124e6ff0a85b979e933c3b3 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:46:38 +0100 Subject: [PATCH 05/23] Add Deploy.bat Deployment script that creates release zip files for both editions of CodeSnip and includes the version number passed on command line in zip file names. Fixes #128 --- Deploy.bat | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Deploy.bat diff --git a/Deploy.bat b/Deploy.bat new file mode 100644 index 000000000..23d95a425 --- /dev/null +++ b/Deploy.bat @@ -0,0 +1,99 @@ +:: This Source Code Form is subject to the terms of the Mozilla Public License, +:: v. 2.0. If a copy of the MPL was not distributed with this file, You can +:: obtain one at https://mozilla.org/MPL/2.0/ +:: +:: Copyright (C) 2024, Peter Johnson (gravatar.com/delphidabbler). +:: +:: Deploy script for CodeSnip. +:: +:: This script compiles release versions of the standard and portable editions +:: of CodeSnip and places them into two different zip files ready for release. +:: +:: This script uses Embarcadero Make. Various other programs are required to +:: run Make. See Src/Makefile for details. +:: +:: To use the script: +:: 1) Set the environment variables required for Make to succeed. See +:: Src/Makefile for details +:: 2) Change directory to that where this script is located. +:: 3) Run the script. +:: +:: Usage: +:: Deploy +:: where +:: is the version number of the release, e.g. 0.5.3-beta or 1.2.0. + +@echo off + +setlocal + +:: Check for required parameter +if "%1"=="" goto paramerror + +:: Store parameter +set Version=%1 + +:: Store common make parameters +set CommonParams=-DVERSION=%Version% + +:: Store standard edition make parameters +set StandardParams=%CommonParams% + +:: Store portable edition make parameters +set PortableParams=-DPORTABLE %CommonParams% + +:: Set command line +set MakeCmd=Make +set StandardMakeCmd=%MakeCmd% %StandardParams% +set PortableMakeCmd=%MakeCmd% %PortableParams% + +echo ---------------------------------------------- +echo Deploying CodeSnip Standard And Portable Builds +echo ----------------------------------------------- +echo. +echo Standard edition Make command: %StandardMakeCmd% +echo Portable edition Make command: %PortableMakeCmd% + +cd Src + +echo. +echo. +echo. +echo ========================= +echo Building Standard edition +echo ========================= +echo. +echo. +%StandardMakeCmd% + +echo. +echo. +echo. +echo ========================= +echo Building Portable edition +echo ========================= +echo. +echo. +%PortableMakeCmd% + +echo. +echo. +echo. +echo ==================== +echo Deployment completed +echo ==================== + +goto end + +:: Error messages + +:paramerror +echo. +echo ***ERROR: Please specify a version number as a parameter +echo. +goto end + +:: End +:end + +endlocal From b52c7853ee4baed649c3c43875db84bbbabc5b80 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:47:51 +0100 Subject: [PATCH 06/23] Change README.md re use per-edition ReadMe.txt files --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8672ddf64..3787b2439 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ CodeSnip can import code from the DelphiDabbler [Code Snippets Database](https:/ The standard edition of CodeSnip is installed and removed using a Windows installer. Administrator privileges are required for installation. -The portable edition has no installer. Simply follow the instructions in the [read me file](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe.txt) that is included in the download. +The portable edition has no installer. Simply follow the instructions in the [read me file](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe-portable.txt) that is included in the download. The program _should_ run on Windows 2000, with Internet Explorer 6 or later, although XP and IE 8 and later are recommended. _But_ note that recent releases of CodeSnip have only been tested on Windows 10 & 11. @@ -33,14 +33,14 @@ The program _should_ run on Windows 2000, with Internet Explorer 6 or later, alt The following support is available to CodeSnip users: * A comprehensive help file. -* A [read-me file](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe.txt) that discusses installation, configuration, updating and known issues. [^1] +* A read-me file that discusses installation, configuration, updating and known issues. There are different versions of this file for each edition of CodeSnip: one for the [standard edition](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe-standard.txt) and another for the [portable edition](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe-portable.txt). [^1] * The [Using CodeSnip FAQ](https://github.com/delphidabbler/codesnip-faq/blob/master/UsingCodeSnip.md). * The [CodeSnip Blog](https://codesnip-app.blogspot.co.uk/). * CodeSnip's own [Web Page](https://delphidabbler.com/software/codesnip). There's also plenty of info available on how to compile CodeSnip from source - see below. -> [^1]: The linked read-me file is the most recent version. It can change from release to release. +> [^1]: The linked read-me file is the most recent version. It can change from release to release. ## Source Code From 101592fb8af64b1be9fa85b8d93d2c597fd28a1c Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:41:32 +0100 Subject: [PATCH 07/23] Update Build.html re addition of Deploy.bat script Also fixed some errors and inconsistencies and tweaked some content. --- Build.html | 58 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/Build.html b/Build.html index 0f96ec05e..ba8a9086f 100644 --- a/Build.html +++ b/Build.html @@ -6,7 +6,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2023, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2024, Peter Johnson (gravatar.com/delphidabbler). * * Instructions for building CodeSnip from source. --> @@ -513,12 +513,14 @@

| +-- exe - receives executable code and compiled help file | | | +-- release - receives release files + | | + | +-- ~tmp~ - store for temp files ceated in release process | ...

If the _build/bin folder already existed, it will have been emptied. - In addition, Make will have created a .cfg file from + In addition, Make will have created a .cfg file from a template in the Src folder. This .cfg file is needed for DCC32 to run correctly. The file will be ignored by Git.

@@ -580,7 +582,7 @@

You have several options:

- +

- Each of these options is described below. All except the last assume that + Each of these options is described below. All except options 5 and 6 assume that Make config has been run.

@@ -648,7 +650,7 @@

> Make -DPORTABLE codesnip

- Again the executable is placed in the _build/exe folder, but this time + Again the executable is placed in the _build\exe folder, but this time it is named CodeSnip-p.exe

@@ -665,12 +667,17 @@

The compiled help file will be written to the _build\exe folder.

+ +

+ The same help file is used for the standard and portable editions. +

+

Build the Setup Program

- The setup program requires that the CodeSnip excutable and the + The setup program requires that the CodeSnip executable and the compiled help file are already present in the _build\exe directory.

@@ -690,7 +697,7 @@

The setup program is named CodeSnip-Setup-x.x.x.exe, where x.x.x is the version number extracted from CodeSnip's version - information. It is placed in the _build/exe directory. + information. It is placed in the _build\exe directory.

@@ -715,7 +722,7 @@

Make can create zip files containing all the files that are included in a release. - Zip files are written to the _build/release directory. + Zip files are written to the _build\release directory.

@@ -724,12 +731,12 @@

The release zip file for the standard edition requires that the setup files is already - present in the _build/exe directory. + present in the _build\exe directory.

The release file includes the setup file along with ReadMe.txt - that is automatically generated from Docs/ReadMe-standard.txt. + that is automatically generated from Docs\ReadMe-standard.txt.

@@ -763,9 +770,9 @@

The release file includes the portable executable file, CodeSnip-p.exe, - the help file CodeSnip.chm, Docs/License.html and + the help file CodeSnip.chm, Docs\License.html and ReadMe.txt that is automatically generated from - Docs/ReadMe-portable.txt. + Docs\ReadMe-portable.txt.

@@ -855,6 +862,31 @@

zip file names can be used here too.

+

+ There is also a quicker way to build a release, but you must provide a version number to use it. First navigate up + to the repository root. Then run +

+ +
> Deploy 9.9.9
+ +

+ where 9.9.9 is the release version number. +

+ +

+ This command will build both the standard and portable executables, the help file, the standard edition setup file + and finally create the release zip files for both editions, with the release version number incorporated in the file names. +

+ +

+ Using Deploy 9.9.9 is the equivalent of doing: +

+ +
> cd Src
+> Make -DVERSION=9.9.9
+> Make -DPORTABLE -DVERSION=9.9.9
+> cd ..
+

Clean Up

From b38d5fe1ff81e7bc7a767e22bc7141b1c641efa3 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:01:05 +0100 Subject: [PATCH 08/23] Update 3rd party PJSysInfo unit to v5.27.0 Fixes #126 --- Src/3rdParty/PJSysInfo.pas | 348 +++++++++++++++++++++++++------------ 1 file changed, 241 insertions(+), 107 deletions(-) diff --git a/Src/3rdParty/PJSysInfo.pas b/Src/3rdParty/PJSysInfo.pas index efd2c4de6..88f726505 100644 --- a/Src/3rdParty/PJSysInfo.pas +++ b/Src/3rdParty/PJSysInfo.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2001-2023, Peter Johnson (https://gravatar.com/delphidabbler). + * Copyright (C) 2001-2024, Peter Johnson (https://gravatar.com/delphidabbler). * * This unit contains various static classes, constants, type definitions and * global variables for use in providing information about the host computer and @@ -21,8 +21,12 @@ * 3: When run on operating systems up to and including Windows 8 running the * host program in compatibility mode causes some variables and TPJOSInfo * methods to be "spoofed" into returning information about the emulated - * OS. When run on Windows 8.1 and later details of the actual host - * operating system are always returned and the emulated OS is ignored. + * OS. When run on Windows 8.1 details of the actual host operating system + * are always returned and the emulated OS is ignored. + * + * 4: On Windows 10 and later the correct operating system will only be + * reported if the application declares the operating systems it supports + * in its manifest. * * ACKNOWLEDGEMENTS * @@ -1226,11 +1230,12 @@ TBuildNameMap = record https://en.wikipedia.org/wiki/Windows_NT https://en.wikipedia.org/wiki/Windows_10_version_history https://en.wikipedia.org/wiki/Windows_11_version_history + https://blogs.windows.com/windows-insider/tag/windows-insider-program/ https://en.wikipedia.org/wiki/Windows_Server https://en.wikipedia.org/wiki/Windows_Server_2019 https://en.wikipedia.org/wiki/Windows_Server_2016 + https://en.wikipedia.org/wiki/Windows_Server_2022 https://tinyurl.com/y8tfadm2 (MS Windows Server release information) - https://tinyurl.com/usupsz4a (Win 11 Version Numbers & Build Versions) https://docs.microsoft.com/en-us/lifecycle/products/windows-server-2022 https://tinyurl.com/yj5e72jt (MS Win 10 release info) https://tinyurl.com/kd3weeu7 (MS Server release info) @@ -1239,6 +1244,10 @@ TBuildNameMap = record For Vista and Win 7 we have to add service pack number to these values to get actual build number. For Win 8 onwards we just use the build numbers as is. + + References: + [^1] MS community blog post https://tinyurl.com/3c8e3hsc + [^2] https://en.wikipedia.org/wiki/Windows_11_version_history } { @@ -1267,24 +1276,30 @@ TBuildNameMap = record // Windows 10 ---------------------------------------------------------------- - // Version 1507 previews - // Preview builds with major/minor version number 6.4 - Win10_6point4Builds: array[0..2] of Integer = (9841, 9860, 9879); - // Preview builds with major/minor version number 10.0 - Win10_1507_Preview_Builds: array[0..10] of Integer = ( - 9926, 10041, 10049, 10061, 10074, 10122, 10130, 10158, 10159, 10162, 10166 - ); + // Version 1507 preview builds + // Preview builds with major/minor version number 6.4 + // Expired by 2015-04-30 [^1]: + // 9841, 9860, 9879 + // Preview builds with major/minor version number 10.0 + // Expired by 2015-10-15 [^1]: + // 9926, 10041, 10049, 10061, 10074, 10122, 10130, 10158, 10159, 10162, + // 10166 - // Version 1511 previews - Win10_1511_Preview_Builds: array[0..4] of Integer = ( - 10525, 10532, 10547, 10565, 10576 - ); + // Version 1511 preview builds + // Expired by 2016-07-30 [^1]: + // 10525, 10532, 10547, 10565, 10576 // Version 1607 previews - Win10_1607_Preview_Builds: array[0..24] of Integer = ( - 11082, 11099, 11102, 14251, 14257, 14271, 14279, 14291, 14295, 14316, - 14328, 14332, 14342, 14352, 14361, 14366, 14367, 14371, 14372, 14376, - 14379, 14383, 14385, 14388, 14390 + Win10_1607_Preview_Builds: array[0..5] of Integer = ( + // Expired 2016-07-30 [^1]: + // 11082, 11099 + // Expired 2016-08-01 [^1]: + // 11102, 14251, 14257, 14267, 14271, 14279, 14291, 14295, 14316, 14328, + // 14332, 14342, 14352, 14361 + // Expired 2016-10-15 [^1]: + // 14366, 14367, 14371, 14372, + 14376, 14379, 14383, 14385, // unknown expiry date [^1] + 14388, 14390 // permanently activated [^1] ); // Version 1703 previews @@ -1349,7 +1364,7 @@ TBuildNameMap = record ); { - End of support information for Windows 10 versions (as of 2022-12-31). + End of support information for Windows 10 versions (as of 2023-05-01). GAC = General Availablity Channel. LTSC = Long Term Support Channel. @@ -1365,10 +1380,10 @@ TBuildNameMap = record 1903 | ended | N/a 1909 | ended | N/a 2004 | ended | N/a - 20H2 | 2023-09-05 | N/a + 20H2 | ended | N/a 21H1 | ended | N/a 21H2 | 2024-06-11 | 2032-01-13 - 22H2 | 2025-05-13 | N/a + 22H2 | 2025-10-14 | N/a } // Map of Win 10 builds from 1st release (version 1507) to version 20H2 @@ -1397,7 +1412,7 @@ TBuildNameMap = record Name: 'Version 1909: November 2019 Update'), (Build: 19041; LoRev: 264; HiRev: 1415; Name: 'Version 2004: May 2020 Update'), - (Build: 19042; LoRev: 572; HiRev: MaxInt; + (Build: 19042; LoRev: 572; HiRev: 2965; Name: 'Version 20H2: October 2020 Update') ); @@ -1430,6 +1445,7 @@ TBuildNameMap = record --------|------------|------------ 21H2 | 2023-10-10 | 2024-10-08 22H2 | 2024-10-08 | 2025-10-14 + 23H2 | 2025-11-11 | 2026-11-10 } // 1st build released branded as Windows 11 @@ -1445,50 +1461,93 @@ TBuildNameMap = record // various other channels. // See **REF1** in implementation Win11v22H2Build = 22621; - // Build 22632 was added as an alternative Beta channel build as of rev 290. + + // Windows 11 version 22H3 + // See **REF10** in implementation + Win11v23H2Build = 22631; + + // "Preview Builds of October 2022 component update in Beta Channel" // See **REF2** in implementation - Win11v22H2BuildAlt = 22622; - - // Windows 11 Dev channel releases (with version string "Dev"). - // For details see https://en.wikipedia.org/wiki/Windows_11_version_history - Win11DevChannelDevBuilds: array[0..25] of Integer = ( - // pre Win 11 release (expired 2021/10/31): - // 22449, 22454, 22458, 22463, - // pre Win 11 release (expired 2022/09/15): - // 22468, - // post Win 11 release, pre Win 11 22H2 beta release (expired 2022/09/15): - // 22471, 22478, 22483, 22489, 22494, 22499, 22504, 22509, 22518, 22523, - // 22526, 22533, 22538, 22543, 22557, 22563, - // post Win 11 22H2 beta release (expired 2022/09/15): + Win11Oct22ComponentBetaChannelBuild = 22622; + + // "Preview Builds of February 2023 component update in Beta Channel" + // See **REF7** in implementation + Win11Feb23ComponentBetaChannelBuild = 22623; + + // "Preview builds of May 2023 component update in Beta Channel" + // See **REF8** in implementation + Win11May23ComponentBetaChannelBuild = 22624; + + // "Preview builds of future component update in Beta Channel" + // See **REF9** in implementation + Win11FutureComponentBetaChannelBuild = 22635; + + // Windows 11 Dev channel releases with version string "Dev" [^2] + // pre Win 11 release (expired 2021/10/31): + // 22449, 22454, 22458, 22463, + // pre Win 11 release (expired 2022/09/15): + // 22468, + // post Win 11 release, pre Win 11 22H2 beta release (expired 2022/09/15): + // 22471, 22478, 22483, 22489, 22494, 22499, 22504, 22509, 22518, 22523, + // 22526, 22533, 22538, 22543, 22557, 22563, + + // Windows 11 Dev channel releases with version string "22H2" [^2] + Win1122H2DevChannelDevBuilds: array[0..20] of Integer = ( + // expired 2022/09/15 (pre Win 11 22H2 beta release): + // 22567, 22572, 22579 + // expired 2022/09/15 (post Win 11 22H2 beta release): // 25115, 25120, 25126, 25131, 25136, 25140, 25145, 25151, 25158, 25163, // 25169, 25174, 25179, - // post Win 11 22H2 beta release (expiring 2023/09/15): - 25182, 25188, 25193, 25197, 25201, 25206, 25211, - // post Win 11 22H2 release (expiring 2023/09/15): - 25217, 25227, 25231, 25236, 25247, 25252, 25262, 25267, 25272, 25276, 25281, - 25284, 25290, 25295, 25300, 25309, 23403, 23419, 23424 + // expired 2023/09/15 (post Win 11 22H2 beta release): + // 25182, 25188, 25193, 25197, 25201, 25206, 25211, + // expired 2023/09/15 (post Win 11 22H2 release): + // 25217, 25227, 25231, 25236, 25247, 25252, 25262, 25267, 25272, 25276, + // 25281, 25284, 25290, 25295, 25300, 25309, 23403, 23419, 23424, 23430, + // 23435, 23440, 23451, 23466, 23471, 23475, 23481, 23486, 23493, 23506, + // 23511, 23516, 23521, + // expiring 2024-09-15: + 23526, 23531, 23536, 23541, 23545, 23550, 23555, 23560, 23565, 23570, 23575, + 23580, 23585, 23590, 23595, 23601, 23606, 23612, 23615, 23619, 23620 + ); + + // Win 11 Dev channel releases with version string "24H2" [^2] + Win1124H2DevChannelDevBuilds: array[0..4] of Integer = ( + // expiring 2024-09-15: + 26052, 26058, 26080, 26085, 26090 ); - // Preview builds of Windows 11 in the Canary Channel - // For details see https://en.wikipedia.org/wiki/Windows_11_version_history - Win11CanaryPreviewBuilds: array[0..2] of Integer = ( - // expiring 2023/09/15: - 25314, 25324, 25330 + // Preview builds of Windows 11 in the Canary Channel with version string + // "22H2" [^2] + // (expired 2023-09-15): + // 25314, 25324, 25330, 25336, 25346, 25352, 25357, 25370, + + // Preview builds of Windows 11 in the Canary Channel with version string + // "23H2" [^2] + Win11Canary23H2PreviewBuilds: array[0..15] of Integer = ( + // expired 2023-09-15: + // 25375, 25381, 25387, 25393, 25905, 25915, 25921, 25926, + // expires 2024-09-15: + 25931, 25936, 25941, 25947, 25951, 25967, 25977, 25982, 25987, 25992, 25997, + 26002, 26010, 26016, 26020, 26040 ); - // Windows 11 Dev channel builds with version string "22H2" - // expired 2022/09/15): - // 22567, 22572, 22579 + // Preview builds of Windows 11 in the Canary Channel with version string + // "24H2" [^2] + Win11Canary24H2PreviewBuilds: array[0..5] of Integer = ( + // expires 2024-09-15: + 26052, 26058, 26063, 26080, 26085, + // expiry date unknown + 26090 + ); - // Windows 11 Dev & Beta channel builds with version string "22H2" + // Windows 11 Dev & Beta channel builds with version string "22H2" [^2] Win11DevBetaChannels22H2Builds: array[0..1] of Integer = ( - // expired 2022/09/15: 22581, 22593, 22598, + // Expired 2022/09/15: + // 22581, 22593, 22598 + // Unknown expiry date: 22610, 22616 ); - Win11Feb23ComponentBetaChannelBuild = 22623; - Win11FutureComponentBetaChannelBuild = 22624; - Win11FirstBuild = Win11DevBuild; // First build number of Windows 11 // Windows server v10.0 version ---------------------------------------------- @@ -2006,6 +2065,13 @@ procedure InitPlatformIdEx; ); end; + // Append "Moment N" to InternalExtraUpdateInfo + procedure AppendMomentToInternalExtraUpdateInfo(N: Cardinal); + begin + InternalExtraUpdateInfo := InternalExtraUpdateInfo + + ' Moment ' + IntToStr(N); + end; + begin // Load version query functions used externally to this routine VerSetConditionMask := LoadKernelFunc('VerSetConditionMask'); @@ -2073,17 +2139,6 @@ procedure InitPlatformIdEx; // Windows 2016 Server tech preview 1 InternalBuildNumber := Win2016TP1Build; InternalExtraUpdateInfo := 'Technical Preview 6'; - end - else - begin - if FindBuildNumberFrom( - Win10_6point4Builds, InternalBuildNumber - ) then - // Early Win 10 preview builds report v6.4, not v10.0 - InternalExtraUpdateInfo := Format( - 'Version 1507 Preview v6.4.%d.%d', - [InternalBuildNumber, InternalRevisionNumber] - ) end; end; if Win32ServicePackMajor > 0 then @@ -2150,11 +2205,13 @@ procedure InitPlatformIdEx; 1288, 1348, 1387, 1415, 1466, 1469, 1503, 1526, 1566, 1586, 1620, 1645, 1682, 1706, 1708, 1741, 1766, 1767, 1806, 1826, 1865, 1889, 1949, 2006, 2075, 2130, 2132, 2193, 2194, 2251, - 2311, 2364, 2486, 2546, 2604, 2673, 2728, 2788 .. MaxInt: + 2311, 2364, 2486, 2546, 2604, 2673, 2728, 2788, 2846, 2965, + 3086, 3208, 3324, 3448, 3570, 3693, 3803, 3930, 4046, + 4170 .. MaxInt: InternalExtraUpdateInfo := 'Version 21H2'; 1147, 1149, 1151, 1165, 1200, 1202, 1237, 1263, 1266, 1319, - 1320, 1379, 1381, 1499, 1618, 1679, 1737, 1739, 1862, 1947, - 2192, 2545: + 1320, 1379, 1381, 1499, 1618, 1679, 1737, 1739, 1862, + 1947, 2192, 2545: InternalExtraUpdateInfo := Format( 'Version 21H2 [Release Preview Channel v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] @@ -2172,16 +2229,19 @@ procedure InitPlatformIdEx; InternalBuildNumber := Win1022H2Build; case InternalBuildNumber of 2006, 2130, 2132, 2193, 2194, 2251, 2311, 2364, 2486, 2546, - 2604, 2673, 2728, 2788 .. MaxInt: + 2604, 2673, 2728, 2788, 2846, 2913, 2965, 3031, 3086, 3208, + 3271, 3324, 3393, 3448, 3516, 3570, 3636, 3693, 3758, 3803, + 3930, 3996, 4046, 4123, 4170, 4239 .. MaxInt: InternalExtraUpdateInfo := 'Version 22H2'; - 1865, 1889, 1949, 2075, 2301, 2670, 2787: + 1865, 1889, 1949, 2075, 2301, 2670, 2787, 2908, 3030, 3154, + 3155, 3269, 3391, 3513, 3754, 3757, 3992, 4116, 4233, 4235: InternalExtraUpdateInfo := Format( 'Version 22H2 [Release Preview Channel v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); else InternalExtraUpdateInfo := Format( - 'Version 22H1 [Unknown release v10.0.%d.%d]', + 'Version 22H2 [Unknown release v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); end; @@ -2209,7 +2269,9 @@ procedure InitPlatformIdEx; 194, 258, 282, 348, 376, 434, 438, 469, 493, 527, 556, 593, 613, 652, 675, 708, 739, 740, 778, 795, 832, 856, 918, 978, 1042, 1098, 1100, 1165, 1219, 1281, 1335, 1455, 1516, 1574, 1641, - 1696, 1761 .. MaxInt: + 1696, 1761, 1817, 1880, 1936, 2003, 2057, 2124, 2176, 2245, + 2295, 2360, 2416, 2482, 2538, 2600, 2652, 2713, 2777, + 2836 .. MaxInt: // Public releases of Windows 11 InternalExtraUpdateInfo := 'Version 21H2'; 51, 65, 71: @@ -2228,9 +2290,10 @@ procedure InitPlatformIdEx; + '[Beta & Release Preview Channels v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); - 651, 706, 776, 829, 917, 1041, 1163, 1279, 1515, 1639, 1757: + 651, 706, 776, 829, 917, 1041, 1163, 1279, 1515, 1639, 1757, + 1879, 2001, 2121, 2243, 2359, 2479: InternalExtraUpdateInfo := Format( - 'Version 21H1 Release Preview Channel v10.0.%d.%d]', + 'Version 21H2 Release Preview Channel v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); else @@ -2246,21 +2309,35 @@ procedure InitPlatformIdEx; InternalBuildNumber := Win11v22H2Build; case InternalRevisionNumber of 382, 521, 525, 608, 674, 675, 755, 819, 900, 963, 1105, 1194, - 1265, 1344, 1413, 1485, {placeholder->}1538 .. MaxInt: + 1265, 1344, 1413, 1485, 1555, 1635, 1702, 1778, 1848, 1926, + 1928, 1992, 2070, 2134, 2215, 2283, 2361, 2428, 2506, 2715, + 2792, 2861, 3007, 3085, 3155, 3235, 3296, 3374 .. MaxInt: + begin InternalExtraUpdateInfo := 'Version 22H2'; + case InternalRevisionNumber of + 675: AppendMomentToInternalExtraUpdateInfo(1); + 1344: AppendMomentToInternalExtraUpdateInfo(2); + 1778: AppendMomentToInternalExtraUpdateInfo(3); + 2361: AppendMomentToInternalExtraUpdateInfo(4); + 3235: AppendMomentToInternalExtraUpdateInfo(5); + end; + end; 1: InternalExtraUpdateInfo := Format( 'Version 22H2 [Beta & Release Preview v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); - 105, 169, 232, 317, 457, 607, 754, 898, 1192, 1343, 1483: + 105, 169, 232, 317, 457, 607, 754, 898, 1192, 1343, 1483, 1631, + 1776, 2066, 2213, 2359, 2500, 2787, 3078, 3227, 3371: InternalExtraUpdateInfo := Format( 'Version 22H2 [Release Preview v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); 160, 290, 436, 440, 450, 575, 586, 590, 598, 601, 730, 741, 746, 870, 875, 885, 891, 1020, 1028, 1037, 1095, 1180, 1245, 1250, - 1255, 1325, 1391, 1465, 1470, 1537: + 1255, 1325, 1391, 1465, 1470, 1537, 1546, 1616, 1680, 1690, + 1755, 1825, 1830, 1835, 1900, 1906, 1972, 2048, 2050, 2115, + 2129, 2191, 2199, 2262, 2265, 2271, 2338: InternalExtraUpdateInfo := Format( 'Version 22H2 [Beta v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] @@ -2272,11 +2349,40 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11v22H2BuildAlt) then + else if IsBuildNumber(Win11v23H2Build) then + begin + // **REF10** + InternalBuildNumber := Win11v23H2Build; + case InternalRevisionNumber of + 2428, 2506, 2715, 2792, 2861, 3007, 3085, 3155, 3235 {Moment 5}, 3296, 3374 .. MaxInt: + InternalExtraUpdateInfo := 'Version 23H2'; + 1825, 1830, 1835, 1900, 1906, 1972: + // revisions 1825..1972 had version string "22H2" + InternalExtraUpdateInfo := Format( + 'Version 22H2 [Beta v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + 2048, 2050, 2115, 2129, 2191, 2199, 2262, 2265, 2271, 2338: + InternalExtraUpdateInfo := Format( + 'Version 23H2 [Beta v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + 2361, 2787, 3078, 3227, 3371: + InternalExtraUpdateInfo := Format( + 'Version 23H2 [Release Preview v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + else + InternalExtraUpdateInfo := Format( + 'Version 23H2 [Unknown release v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + end; + end + else if IsBuildNumber(Win11Oct22ComponentBetaChannelBuild) then begin // **REF2** - InternalBuildNumber := Win11v22H2BuildAlt; - // Set fallback update info for unknown revisions + InternalBuildNumber := Win11Oct22ComponentBetaChannelBuild; case InternalRevisionNumber of 290, 436, 440, 450, 575, 586, 590, 598, 601: InternalExtraUpdateInfo := Format( @@ -2291,22 +2397,42 @@ procedure InitPlatformIdEx; end; end else if FindBuildNumberFrom( - Win11DevChannelDevBuilds, InternalBuildNumber + Win1122H2DevChannelDevBuilds, InternalBuildNumber + ) then + begin + // Win11 Dev Channel builds with version string "22H2" + InternalExtraUpdateInfo := Format( + 'Dev Channel Version 22H2 v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + end + else if FindBuildNumberFrom( + Win1124H2DevChannelDevBuilds, InternalBuildNumber + ) then + begin + // Win11 Dev Channel builds with version string "22H2" + InternalExtraUpdateInfo := Format( + 'Dev Channel Version 24H2 v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + end + else if FindBuildNumberFrom( + Win11Canary23H2PreviewBuilds, InternalBuildNumber ) then begin - // Win11 Dev Channel builds with version string "Dev" + // Win11 Canary Channel builds with version string "23H2" InternalExtraUpdateInfo := Format( - 'Dev Channel v10.0.%d.%d (Dev)', + 'Canary Channel Version 23H2 v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] ); end else if FindBuildNumberFrom( - Win11CanaryPreviewBuilds, InternalBuildNumber + Win11Canary24H2PreviewBuilds, InternalBuildNumber ) then begin - // Win11 Canary Channel builds + // Win11 Canary Channel builds with version string "24H2" InternalExtraUpdateInfo := Format( - 'Canary Channel v10.0.%d.%d (Dev)', + 'Canary Channel Version 24H2 v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] ); end @@ -2314,7 +2440,7 @@ procedure InitPlatformIdEx; Win11DevBetaChannels22H2Builds, InternalBuildNumber ) then begin - // Win 11 Dev & Beta channel builds with verison string "22H2" + // Win 11 Dev & Beta channel builds with version string "22H2" InternalExtraUpdateInfo := Format( 'Dev & Beta Channels v10.0.%d.%d (22H2)', [InternalBuildNumber, InternalRevisionNumber] @@ -2322,6 +2448,7 @@ procedure InitPlatformIdEx; end else if IsBuildNumber(Win11Feb23ComponentBetaChannelBuild) then begin + // **REF7** InternalBuildNumber := Win11Feb23ComponentBetaChannelBuild; case InternalRevisionNumber of 730, 741, 746, 870, 875, 885, 891, 1020, 1028, 1037, 1095, @@ -2337,11 +2464,32 @@ procedure InitPlatformIdEx; ); end; end + else if IsBuildNumber(Win11May23ComponentBetaChannelBuild) then + begin + // **REF8** + InternalBuildNumber := Win11May23ComponentBetaChannelBuild; + case InternalRevisionNumber of + 1391, 1465, 1470, 1537, 1546, 1610, 1616, 1680, 1690, 1755 .. + MaxInt: + InternalExtraUpdateInfo := Format( + 'May 2023 Component Update Beta v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + else + InternalExtraUpdateInfo := Format( + 'May 2023 Component Update [Unknown Beta v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + end; + end else if IsBuildNumber(Win11FutureComponentBetaChannelBuild) then begin + // **REF9** InternalBuildNumber := Win11FutureComponentBetaChannelBuild; case InternalRevisionNumber of - 1391, 1465, 1470, 1537 .. MaxInt: + 2419, 2483, 2486, 2552, 2700, 2771, 2776, 2841, 2850, 2915, + 2921, 3061, 3066, 3130, 3139, 3140, 3209, 3212, 3276, 3286, + 3350, 3420 .. MaxInt: InternalExtraUpdateInfo := Format( 'Future Component Update Beta v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] @@ -2431,20 +2579,6 @@ procedure InitPlatformIdEx; begin // Nothing to do: required internal variables set in function call end - else if FindWin10PreviewBuildNameAndExtraFrom( - Win10_1511_Preview_Builds, '1511', - InternalBuildNumber, InternalExtraUpdateInfo - ) then - begin - // Nothing to do: required internal variables set in function call - end - else if FindWin10PreviewBuildNameAndExtraFrom( - Win10_1507_Preview_Builds, '1507', - InternalBuildNumber, InternalExtraUpdateInfo - ) then - begin - // Nothing to do: required internal variables set in function call - end end else // Win32ProductType in [VER_NT_DOMAIN_CONTROLLER, VER_NT_SERVER] begin @@ -3059,8 +3193,6 @@ class function TPJOSInfo.Platform: TPJOSPlatform; end; class function TPJOSInfo.Product: TPJOSProduct; -var - DummyBN: Integer; // dummy build number begin Result := osUnknown; case Platform of @@ -3150,8 +3282,10 @@ class function TPJOSInfo.Product: TPJOSProduct; // application is "manifested" for the correct Windows version. // See https://bit.ly/MJSO8Q. Result := osWin10Svr - else if FindBuildNumberFrom(Win10_6point4Builds, DummyBN) then - Result := osWin10; + // Version 6.4 was also used for some early Windows 10 preview + // builds, but they have all expired so detection has been + // removed. + // See https://tinyurl.com/3c8e3hsc else // Higher minor version: must be an unknown later OS Result := osWinLater From df62229ccefa0349445c08049261dd5233a19207 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:19:30 +0100 Subject: [PATCH 09/23] Fix copyright date in About box Fixes #129 --- Src/Res/HTML/dlg-about-program-tplt.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/Res/HTML/dlg-about-program-tplt.html b/Src/Res/HTML/dlg-about-program-tplt.html index 63a3a2cd4..be93a30c3 100644 --- a/Src/Res/HTML/dlg-about-program-tplt.html +++ b/Src/Res/HTML/dlg-about-program-tplt.html @@ -9,7 +9,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2023, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2024, Peter Johnson (gravatar.com/delphidabbler). * * Template for content displayed in program tab of about dialog box. --> @@ -47,7 +47,7 @@

- DelphiDabbler CodeSnip is copyright © 2005-2023 by CodeSnip is copyright © 2005-2024 by Peter D Johnson. From 6d33c8df7b5d3b97b7f3f55e61303a2b94fbcdca Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:46:59 +0100 Subject: [PATCH 10/23] Add version number to program caption Program release version number now displayed in the main form caption. Fixes #122 --- Src/UAppInfo.pas | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Src/UAppInfo.pas b/Src/UAppInfo.pas index eacc0d65d..37958f245 100644 --- a/Src/UAppInfo.pas +++ b/Src/UAppInfo.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2024, Peter Johnson (gravatar.com/delphidabbler). * * Class that provides information about the application. } @@ -36,12 +36,6 @@ TAppInfo = class(TNoConstructObject) const ProgramName = 'CodeSnip-p'; {$ENDIF} {Name of program} - {$IFNDEF PORTABLE} - const ProgramCaption = 'CodeSnip 4'; - {$ELSE} - const ProgramCaption = 'CodeSnip 4 (Portable Edition)'; - {$ENDIF} - {Name of program displayed in main window and task bar caption} const FullProgramName = CompanyName + ' ' + ProgramName; {Full name of program, including company name} const ProgramID = 'codesnip'; @@ -107,6 +101,10 @@ TAppInfo = class(TNoConstructObject) {Gets version number of program's executable file. @return Version number as dotted quad. } + class function ProgramCaption: string; + {Gets the program caption to be displayed in main window, + @return Required caption, + } end; @@ -214,6 +212,19 @@ class function TAppInfo.HelpFileName: string; Result := AppExeDir + '\CodeSnip.chm'; end; +class function TAppInfo.ProgramCaption: string; +var + ProductVer: TVersionNumber; +begin + ProductVer := TVersionInfo.ProductVerNum; + Result := Format( + 'CodeSnip v%d.%d.%d', [ProductVer.V1, ProductVer.V2, ProductVer.V3] + ); + {$IFDEF PORTABLE} + Result := Result + ' (Portable Edition)' + {$ENDIF} +end; + class function TAppInfo.ProgramFileVersion: string; {Gets version number of program's executable file. @return Version number as dotted quad. From b0d0726d6ae953adc42253a81d0be7f94f57b4a9 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 01:57:02 +0100 Subject: [PATCH 11/23] Fix full program name used by portable edition The full program name is now always "DelphiDabbler CodeSnip", regardless of the edition. Previously the name was "DelphiDabbler CodeSnip-p" on the portable edition. Fixes #130 --- Src/UAppInfo.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/UAppInfo.pas b/Src/UAppInfo.pas index 37958f245..7bc91fd6e 100644 --- a/Src/UAppInfo.pas +++ b/Src/UAppInfo.pas @@ -36,7 +36,7 @@ TAppInfo = class(TNoConstructObject) const ProgramName = 'CodeSnip-p'; {$ENDIF} {Name of program} - const FullProgramName = CompanyName + ' ' + ProgramName; + const FullProgramName = CompanyName + ' CodeSnip'; {Full name of program, including company name} const ProgramID = 'codesnip'; {Machine readable identifier of program} From 6d7a9925160333c8f2d50cec412696c74b9bcf77 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 4 Apr 2024 02:23:33 +0100 Subject: [PATCH 12/23] Fix REML documentation and help topic * REML docs in Docs/Design/reml.html has duplicate ¢ character entity entry. * REML help topic was never updated for the new ' character entity. Fixes #131 --- Docs/Design/reml.html | 4 ---- Src/Help/HTML/reml.htm | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Docs/Design/reml.html b/Docs/Design/reml.html index b945f4332..43adc650f 100644 --- a/Docs/Design/reml.html +++ b/Docs/Design/reml.html @@ -524,10 +524,6 @@

&deg; ° - - &cent; - ¢ - &laquo; « diff --git a/Src/Help/HTML/reml.htm b/Src/Help/HTML/reml.htm index f66afea9d..3b6753a59 100644 --- a/Src/Help/HTML/reml.htm +++ b/Src/Help/HTML/reml.htm @@ -353,6 +353,10 @@

&iquest; ¿ + + &apos; + ' +

From 0fb7ce6de170769f3f7aa102375ce8d533fda714 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:04:27 +0100 Subject: [PATCH 13/23] Rewrite Docs/Design/reml.html Docs/Design/reml.html now contains a brief overview of REML and links to documentation in the new delphidabbler/reml repository for a full language definition. --- Docs/Design/reml.html | 537 ++---------------------------------------- 1 file changed, 20 insertions(+), 517 deletions(-) diff --git a/Docs/Design/reml.html b/Docs/Design/reml.html index 43adc650f..fb8b0ce15 100644 --- a/Docs/Design/reml.html +++ b/Docs/Design/reml.html @@ -19,7 +19,7 @@ - + @@ -219,450 +116,56 @@ </p> </div> - <nav id="contents"> - <ul> - <li> - <a href="#intro">Introduction</a> - </li> - <li> - <a href="#tags">Tags</a> - </li> - <li> - <a href="#entities">Character Entities</a> - </li> - <li> - <a href="#changes">Change Log</a> - </li> - </ul> - </nav> - </header> -<section id="intro"> - - <h1> - Introduction - </h1> - - <p> - REML is a little markup language that can be used to style text. It is used in Code Snippets collection meta data for certain properties of a snippet. - </p> - <p> - The REML language is a SGML language similar to a greatly simplified XHTML. The are a small number of tags and character entities that can be used. - </p> - <aside> - <strong>Note:</strong> The language described here is REML v6. v4 is still in regular use in CodeSnip up to v4.20.x. Earlier versions are obsolete. - </aside> - -</section> +<main> -<section id="tags"> +<section id="intro"> <h1> - Tags + About REML </h1> <p> - There are two types of tags: block level and in-line. - </p> - - <p> - If an unrecognised tag is encountered an REML code the interpreter <em>should</em> report an error. However, providing start and end tags are matched, the interpreter <em>may</em> choose to simply ignore the tags. - </p> - - <h2> - Block Level Tags - </h2> - - <p> - Block level tags separate the enclosed text into paragraphs of some description. The supported tags are: - </p> - <ul class="half-spaced"> - <li> - <code class="value"><p>...</p></code> – Renders the enclosed markup as a simple paragraph. - </li> - <li> - <code class="value"><heading>...</heading></code> – Renders the enclosed markup as a heading. - </li> - <li> - <code class="value"><ol>...</ol></code> – Renders the enclosed markup as an ordered list. - </li> - <li> - <code class="value"><ul>...</ul></code> – Renders the enclosed markup as an unordered list. - </li> - <li> - <code class="value"><li>...</li></code> – Renders the enclosed markup as a list item. - </li> - </ul> - <p> - The following rules apply to the use of block level tags: - </p> - <ul class="unspaced"> - <li> - <span class="very-strong">Must</span> be matched, e.g. <code class="value"><p></code> <span class="very-strong">must</span> have a matching <code class="value"></p></code>. - </li> - <li> - <code class="value"><p>...</p></code> and <code class="value"><heading>...</heading></code> blocks <span class="very-strong">must not</span> contain other block level tags. - </li> - <li> - <code class="value"><ol>...</ol></code> and <code class="value"><ul>...</ul></code> blocks <span class="very-strong">must only</span> contain one or more <code class="value"><li>...</li></code> blocks. - </li> - <li> - <code class="value"><li>...</li></code> blocks <span class="very-strong">must</span> only be used within <code class="value"><ol>...</ol></code> and <code class="value"><ul>...</ul></code> blocks. <em>May</em> contain <code class="value"><p>...</p></code> and <code class="value"><heading>...</heading></code> blocks, but it is permitted to include text and inline tags directly without enclosing them one of the permitted blocks. Nested lists are permitted by including further <code class="value"><ul>...</ul></code> and <code class="value"><ol>...</ol></code> blocks. - </li> - <li> - All text <em>should</em> be embedded within <code class="value"><p>...</p></code>, <code class="value"><heading>...</heading></code> or <code class="value"><li>...</li></code> block level tags, e.g. <code class="value"><heading>heading</heading><p>text</p></code> or simply <code class="value"><p>text</p></code>. - </li> - <li> - White space between blocks <span class="very-strong">must</span> be ignored. - </li> - </ul> - <p> - Here is a valid example: - </p> - <pre class="sample"><heading>Hello</heading> -<p>Hello World</p> -<ol> - <li>one</li> - <li><p>two</p></li> - <ul> - <li>two A</li> - <li>two B</li> - <ul> - <li>three</li> -</ol></pre> - <p> - Strictly speaking, the following example is invalid code – all occurrences of <code class="value">wrong</code> are in error because they are not contained within block tags. - </p> - <pre class="sample">wrong <heading>blah</heading> wrong <p>blah</p> wrong</pre> - <p> - However interpreting code <em>may</em> interpret this permissively. If this is done the text outside blocks <em>should</em> be interpreted as if it was enclosed in <code class="value"><p></code> and <code class="value"></p></code> tags. Therefore the above code would be interpreted as: + REML is a little markup language that can be used to style text. It is a SGML language similar to HTML, albeit much smaller. A small number of tags and character entities are supported. </p> - <pre class="sample"><p>wrong </p><heading>blah</heading><p>wrong </p><p>blah</p><p>wrong</p></pre> - <aside> - <strong>Note:</strong> Code Snippets Database collections <em>may</em> contain such non-conforming REML. Therefore interpreters of REML that need to accept such collections <span class="very-strong">must</span> be able to handle text without enclosing block tags. - </aside> - <h2> - Inline Tags - </h2> - - <p> - In-line tags format the text enclosed between the start and end tags. - </p> <p> - Here are the available in-line tags: + See the <a href="https://htmlpreview.github.io/?https://raw.githubusercontent.com/delphidabbler/reml/main/docs/reml-v6.html">REML v6 language definition</a> for full details. </p> - <ul class="half-spaced"> - <li> - <code class="value"><strong>...</strong></code> – Renders the enclosed markup with strong emphasis. - </li> - <li> - <code class="value"><em>...</em></code> – Emphasises the enclosed markup. - </li> - <li> - <code class="value"><var>...</var></code> – Used to indicate the enclosed markup is a variable. - </li> - <li> - <code class="value"><warning>...</warning></code> – Used for warning text. - </li> - <li> - <code class="value"><mono>...</mono></code> – Renders markup in a mono-spaced font. - </li> - <li> - <code class="value"><a href="url">...</a></code> – Creates a hyper-link. The <code class="value">href</code> attribute <span class="very-strong">must</span> specify the required URL, which <span class="very-strong">must</span> use one of the <code class="value">http</code>, <code class="value">https</code> or <code class="value">file</code> protocols; others are not permitted. If you use the <code class="value">file</code> protocol it <span class="very-strong">must</span> reference a valid local or network file. - </li> - </ul> - <p> - The following rules apply to the use of in-line tags: - </p> - <ul class="unspaced"> - <li> - In-line tags <span class="very-strong">must</span> be embedded inside a valid block level tag. E.g. <code class="value"><p>one<strong>two</strong>three</p></code>. - </li> - <li> - Tags <span class="very-strong">must</span> match. E.g. <code class="value"><em></code> must be matched with <code class="value"></em></code>. - </li> - <li> - Tags may be nested, providing the tags are balanced. E.g. <code class="value"><em>blah <var>blah</var></em></code> is valid but <code class="value"><em>blah <var>blah</em></var></code> is not. - </li> - </ul> - <p> - Examples: - </p> - <pre class="sample"><p>Make stuff <strong>stand out</strong>.</p> -<p><em>Emphasised <warning>warning!</warning></em></p> -<p>Refer to a function <var>parameter</var>.</p> -<p>Use the: <mono>Windows</mono> unit.</p> -<p>See this <a href="https://example.com">example</a>.</p></pre> </section> -<section id="entities"> +<section id="reml-in-codesnip"> <h1> - Character Entities + REML in CodeSnip </h1> <p> - Some symbolic character entities are supported in REML. Many symbols, but not all, have analogues in the list of supported character entities in XHTML or HTML 5. Some entities have alternate symbols. Here is the complete list. + Code snippets include REML to format snippets' description and extra fields. CodeSnip interprets and renders the REML when displaying snippets in its UI and when printing them. </p> - <table> - <thead> - <tr> - <th>Character Entity</th> - <th>Actual Character</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>&amp;</code></td> - <td>&</td> - </tr> - <tr> - <td><code>&quot;</code></td> - <td>"</td> - </tr> - <tr> - <td><code>&gt;</code></td> - <td>></td> - </tr> - <tr> - <td><code>&lt;</code></td> - <td><</td> - </tr> - <tr> - <td><code>&copy;</code></td> - <td>©</td> - </tr> - <tr> - <td><code>&times;</code></td> - <td>×</td> - </tr> - <tr> - <td><code>&divide;</code> or <code>&div;</code></td> - <td>÷</td> - </tr> - <tr> - <td><code>&plusmn;</code></td> - <td>±</td> - </tr> - <tr> - <td><code>&ne;</code> or <code>&neq;</code></td> - <td>≠</td> - </tr> - <tr> - <td><code>&sum;</code></td> - <td>∑</td> - </tr> - <tr> - <td><code>&infin;</code></td> - <td>∞</td> - </tr> - <tr> - <td><code>&pound;</code></td> - <td>£</td> - </tr> - <tr> - <td><code>&curren;</code></td> - <td>¤</td> - </tr> - <tr> - <td><code>&yen;</code></td> - <td>¥</td> - </tr> - <tr> - <td><code>&euro;</code></td> - <td>€</td> - </tr> - <tr> - <td><code>&cent;</code></td> - <td>¢</td> - </tr> - <tr> - <td><code>&dagger;</code></td> - <td>†</td> - </tr> - <tr> - <td><code>&ddagger;</code> or <code>&Dagger;</code></td> - <td>‡</td> - </tr> - <tr> - <td><code>&hellip;</code></td> - <td>…</td> - </tr> - <tr> - <td><code>&para;</code></td> - <td>¶</td> - </tr> - <tr> - <td><code>&sect;</code></td> - <td>§</td> - </tr> - <tr> - <td><code>&reg;</code></td> - <td>®</td> - </tr> - <tr> - <td><code>&frac14;</code></td> - <td>¼</td> - </tr> - <tr> - <td><code>&frac12;</code> or <code>&half;</code></td> - <td>½</td> - </tr> - <tr> - <td><code>&frac34;</code></td> - <td>¾</td> - </tr> - <tr> - <td><code>&micro;</code></td> - <td>µ</td> - </tr> - <tr> - <td><code>&deg;</code></td> - <td>°</td> - </tr> - <tr> - <td><code>&laquo;</code></td> - <td>«</td> - </tr> - <tr> - <td><code>&raquo;</code></td> - <td>»</td> - </tr> - <tr> - <td><code>&iquest;</code></td> - <td>¿</td> - </tr> - <tr> - <td><code>&apos;</code></td> - <td>'</td> - </tr> - </tbody> - </table> - - <aside> - <strong>Note:</strong> the '<' and '&' characters are special within the markup and cannot be used literally, even when you are just entering plain text. You <span class="very-strong">must</span> use the <code class="value">&lt;</code> character entity in place of <code class="value"><</code> and <code class="value">&amp;</code> instead of <code class="value">&</code>. For example to write <code class="value">x<y</code> in REML use <code class="value">x&lt;y</code> and to write <code class="value">you & me</code> use <code class="value">you &amp; me</code>. - </aside> - <p> - To express other special symbols for which there is no symbolic character entity, numeric character entities can be used. For example to display the 'Ω' character (Unicode <em>Greek capital letter Omega</em>) use <code class="value">&#937;</code>. + CodeSnip currently supports REML v6. Earlier versions of CodeSnip supported different versions of REML: </p> - - <aside> - <strong>Note:</strong> Numeric entities should be used with caution because the characters they represent may vary across different text encodings, whereas symbolic entities are safe across encodings. - </aside> - -</section> - - -<section id="changes"> - - <h1>Change Log</h1> - - <p> - This section notes the changes in the various versions of REML. - </p> - - <p> - <strong>v1 of 2008-12-31</strong> - </p> - - <p> - Introduced in CodeSnip v2.2.5 - </p> - - <ul> - <li> - Supported tags: <code class="value"><strong></code> and <code class="value"><a></code>. - </li> - <li> - Supported entities: <code class="value">&gt;</code>, <code class="value">&lt;</code>, <code class="value">&quot;</code> and <code class="value">&amp;</code>. - </li> - <li> - Supported protocols for use in <code class="value"><a></code> tags: <code class="value">http</code>. - </li> - </ul> - - <p> - <strong>v2 of 2009-06-29</strong> - </p> - - <p> - Introduced in CodeSnip v3.0 - </p> - - <ul> - <li> - Added tags: <code class="value"><em></code>, <code class="value"><var></code>, <code class="value"><warning></code>, <code class="value"><mono></code>, <code class="value"><p></code> and <code class="value"><heading></code>. - </li> - <li> - Added entity: <code class="value">&copy;</code>. - </li> - </ul> - - <p> - <strong>v3 of 2009-07-06</strong> - </p> - - <p> - Introduced in CodeSnip v3.0.1 - </p> - - <ul> - <li> - Added protocol for use in <code class="value"><a></code> tags: <code class="value">file</code>. - </li> - </ul> - - <p> - <strong>v4 of 2011-12-31</strong> - </p> - - <p> - Introduced in CodeSnip v4.0 alpha 1 (preview) - </p> - - <ul> - <li> - Added protocol for use in <code class="value"><a></code> tags: <code class="value">https</code>. - </li> - </ul> - - <p> - <strong>v5 of 2022-12-16</strong> - </p> - - <p> - Introduced in CodeSnip v4.21.0 - </p> - + <ul> - <li> - Added support for lists with the <code class="value"><ol></code>, <code class="value"><ul></code> & <code class="value"><li></code> block tags. - </li> - <li> - Added entities: <code class="value">&times;</code>, <code class="value">&divide;</code>, <code class="value">&div;</code> <code class="value">&plusmn;</code>, <code class="value">&ne;</code>, <code class="value">&neq;</code>, <code class="value">&sum;</code>, <code class="value">&infin;</code>, <code class="value">&pound;</code>, <code class="value">&curren;</code>, <code class="value">&yen;</code>, <code class="value">&euro;</code>, <code class="value">&cent;</code>, <code class="value">&dagger;</code>, <code class="value">&ddagger;</code>, <code class="value">&Dagger;</code>, <code class="value">&hellip;</code>, <code class="value">&para;</code>, <code class="value">&sect;</code>, <code class="value">&reg;</code>, <code class="value">&frac14;</code>, <code class="value">frac12</code>, <code class="value">&half;</code>, <code class="value">&frac34;</code>, <code class="value">&micro;</code>, <code class="value">&deg;</code>, <code class="value">&laquo;</code>, <code class="value">&raquo;</code> & <code class="value">&iquest;</code>. - </li> + <li>REML v1 was first supported by CodeSnip v2.2.5</li> + <li>REML v2 was first supported by CodeSnip v3.0</li> + <li>REML v3 was first supported by CodeSnip v3.0.1</li> + <li>REML v4 was first supported by CodeSnip v4.0 alpha 1 (preview)</li> + <li>REML v5 was first supported by CodeSnip v4.21.0</li> + <li>REML v6 was first supported by CodeSnip v4.23.0</li> </ul> <p> - <strong>v6 of 2024-04-02</strong> + All CodeSnip versions are backward compatible with earlier versions of REML. </p> - <p> - Introduced in CodeSnip v4.23.0 - </p> +</section> - <ul> - <li> - Added entity: <code class="value">&apos;</code>. - </li> - </ul> - - </section> +</main> </body> From fca862c6d28a2b3fed1fa43a2e3852293791d6e0 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:46:15 +0100 Subject: [PATCH 14/23] Rewite REML help topic REML language definition was replaced with a link to the definition on the delphidabbler/reml project. Added a brief preamble and an example that uses all supported tags. Added screens shot image to illustrate example. --- Src/Help/HTML/reml.htm | 387 ++++---------------------------- Src/Help/Images/REMLExample.png | Bin 0 -> 47280 bytes 2 files changed, 47 insertions(+), 340 deletions(-) create mode 100644 Src/Help/Images/REMLExample.png diff --git a/Src/Help/HTML/reml.htm b/Src/Help/HTML/reml.htm index 3b6753a59..0783817a5 100644 --- a/Src/Help/HTML/reml.htm +++ b/Src/Help/HTML/reml.htm @@ -15,20 +15,6 @@ About REML - About the REML markup language

- REML is CodeSnip's own little markup language that can - be used to style the text of a snippet's description and / or extra - information. The latest version is v6, which is backwards compatible with - all other versions. + REML is a little markup language that can be used to style text. It is a SGML language similar to HTML, albeit much smaller. A small number of tags and character entities are supported.

-

- Language Details -

- The REML language is a SGML language similar to a greatly - simplified XHTML. The are a small number of tags you can use. Firstly - there are two block-level tags that render text in paragraphs, while the - other tags format text inline or embed hyplerlinks. + CodeSnip currently supports REML v6. See the REML v6 language definition for full details.

-

- Block level tags -

-
-
<p>...</p>
-
- Renders the enclosed markup as a simple paragraph. -
-
<heading>...</heading>
-
- Renders the enclosed markup as a heading. -
-
<ol>...</ol>
-
- Renders the enclosed HTML as an ordered list. Must contain - <li>...</li> blocks and nothing - else. -
-
<ul>...</ul>
-
- Renders the enclosed HTML as an unordered list. Must contain - <li>...</li> blocks and nothing - else. -
-
<l1>...</li>
-
- Renders the enclosed HTML as a list item. May only be used within - <ol>...</ol> and - <ul>...</ul> blocks. -
-

- The following rules apply to the use of block level tags: -

-
    -
  1. - Must be matched, e.g. - <p> must have a matching - </p>. -
  2. -
  3. - <p>...</p> and - <heading>...</heading> blocks - must not contain other block level tags. -
  4. -
  5. - <ol>...</ol> and - <ul>...</ul> blocks must only - contain one or more - <li>...</li> blocks. -
  6. -
  7. - <li>...</li> blocks may contain - <p>...</p> and - <heading>...</heading> blocks, - but it is permitted to include text and inline tags directly without - enclosing them one of the permitted blocks. Nested lists are permitted - by including further <ul>...</ul> - and <ol>...</ol> blocks. -
  8. -
  9. - All text should be embedded within - <p>...</p>, - <heading>...</heading> or - <li>...</li> block level tags, - e.g. <heading>heading</heading><p>text</p> - or simply <p>text</p>. -
  10. -
  11. - White space between blocks must be ignored. -
  12. -
-

- Here is a valid example: -

-
<heading>Hello</heading>
-<p>Hello World</p>
+      The following whimsical example demonstrates every supported REML tag along with a couple of character entities:
+
<heading>
+  Wombat converter
+</heading>
+<p>
+ Transforms <strong>wombats</strong> into <em>dongles</em>.
+ <warning><em>W</em>arning:</warning> The <var>Foo</var>
+ variable stores &lt;=<mono>12</mono> accumulated <mono>dongles</mono>.
+</p>
+<p>
+ All 3 species of wombat are supported:
+</p>
 <ol>
-  <li>one</li>
-  <li><p>two</p></li>
-  <ul>
-    <li>two A</li>
-    <li>two B</li>
-  <ul>
-  <li>three</li>
-</ol>
-

- Srictly speaking, the following example is invalid code – the - highlighted sections are in error, because they are not contained within - block tags. -

-
blah<heading>blah</heading>blah<p>blah</p>blah
-

- However, CodeSnip is quite permissive and, in many cases, - automatically adds block level tags for text that is not enclosed in block - level tags. The above code is interpreted similar ro: -

-
<p>blah </p>
-<heading>blah</heading>
-<p>blah </p>
-<p>blah</p>
-<p>blah</p>
-

- Inline tags -

-

- Here are the available inline tags: -

-
-
<strong>...</strong>
-
- Renders the enclosed markup with strong emphasis.
- Example: <p>Make stuff - <strong>stand out</strong>.</p> -
-
<em>...</em>
-
- Emphasises the enclosed markup.
- Example: <p>Draw - <em>attention</em> to something.</p> -
-
<var>...</var>
-
- Used to indicate the enclosed markup is a variable.
- Example: <p>Refer to a function - <var>parameter</var>.</p> -
-
<warning>...</warning>
-
- Used for warning text.
- Example: - <p><warning>Warning:</warning> - Don't do it!</p> -
-
<mono>...</mono>
-
- Renders markup in a mono-spaced font.
- Example: <p>Use the: - <mono>Windows</mono> unit.</p> -
-
<a href="url">...</a>
-
- Creates a hyperlink. The href attribute must - specify the required URL, which must use one of the http:, - https: or file: protocols; others are not permitted. - If you use the file: protocol it must reference a valid local - or network file. Be aware that if you export a snippet - containing a hyperlink that uses the file: protocol it will - only work on the recipient's system if the specified file exists in the - same location.
- Example: <p><a - href="https://example.com">Visit - example.com</a></p>.. -
-
-

- Character Entities -

-

- The "<" and "&" characters are special within - the markup and must not be used directly, even when you are just entering - plain text. You must use the &lt; character - entity in place of "<" and - &amp; instead of "&". -

-

- A few other character entities are supported for convenience. Here is the - complete list: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Character EntityActual Character
&amp;&
&quot;"
&gt;>
&lt;<
&copy;©
&times;×
&divide; or &div;÷
&plusmn;±
&ne; or &neq;
&sum;
&infin;
&pound;£
&curren;¤
&yen;¥
&euro;
&cent;¢
&dagger;
&ddagger; or &Dagger;
&hellip;
&para;
&sect;§
&reg;®
&frac14;¼
&frac12; or &half;½
&frac34;¾
&micro;µ
&deg;°
&laquo;«
&raquo;»
&iquest;¿
&apos;'
-

- By way of an example, if you want to display x ≠ y, use: -

-

- x &ne; y + <li> + <p> + <a href="https://en.wikipedia.org/wiki/Common_wombat">Common + wombat</a>. The following sub-species are supported: + </p> + <ul> + <li> + Bass Strait wombat + </li> + <li> + Hirsute wombat + </li> + <li> + Tasmanian wombat + </li> + </ul> + </li> + <li> + Northen hairy-nosed wombat + </li> + <li> + Southern hairy-nosed wombat + </li> +</ol> +<p> + Copyright &copy; wombaterama, 2024. +</p>

- No other symbolic character entities are supported. - However, numeric character entities can be used to insert other characters - by specifying its code. For example &#64; is - equivalent to "@". + All this silliness renders something like this:

- Numeric entities should be used with caution. Using a code that is - specific to an ANSI character set may cause unexpected results because - CodeSnip uses Unicode internally and the specified character code - may not represent the same character in ANSI and Unicode. +

diff --git a/Src/Help/Images/REMLExample.png b/Src/Help/Images/REMLExample.png new file mode 100644 index 0000000000000000000000000000000000000000..6e7a49f5bee6ce5ebb8a6c54f70e776991471493 GIT binary patch literal 47280 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�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 literal 0 HcmV?d00001 From bbdbdcaa87d613eaa18272d68516fbc2894c9c43 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 05:56:37 +0100 Subject: [PATCH 15/23] Reverse order of compilers in Snippets Editor Order of compilers on the Compile Results tab was reversed. Fixes #135 --- Src/UCompileResultsLBMgr.pas | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Src/UCompileResultsLBMgr.pas b/Src/UCompileResultsLBMgr.pas index 6d8fd29bd..c94b7fcab 100644 --- a/Src/UCompileResultsLBMgr.pas +++ b/Src/UCompileResultsLBMgr.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2024, Peter Johnson (gravatar.com/delphidabbler). * * Defines classes that manages display and interaction with a list box that * displays compiler results. @@ -600,13 +600,18 @@ procedure TCompileResultsLBMgr.PopulateListBox; 'unknown'. } var - Compiler: ICompiler; // each supported compiler + Compiler: ICompiler; + CompilerId: TCompilerID; begin - for Compiler in fCompilers do + // Populate list box in reverse order of compiler ID + for CompilerId := High(TCompilerID) downto Low(TCompilerID) do + begin + Compiler := fCompilers[CompilerId]; fLB.Items.AddObject( Compiler.GetName, TCompilerInfo.Create(Compiler.GetID, crQuery) ); + end; end; procedure TCompileResultsLBMgr.SetCompileResult(const Index: Integer; From 1ab44ec348e90be5dbf8a5360677263240279333 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 06:10:09 +0100 Subject: [PATCH 16/23] Fix bug in TFileIO.CheckBOM (TStream overload) Method was returning True for encodings with zero length BOMs instead of the False result per documentation. Fixes #139 --- Src/UIOUtils.pas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Src/UIOUtils.pas b/Src/UIOUtils.pas index 88beb3afa..09b02879d 100644 --- a/Src/UIOUtils.pas +++ b/Src/UIOUtils.pas @@ -206,6 +206,8 @@ class function TFileIO.CheckBOM(const Stream: TStream; Assert(Assigned(Stream), 'TFileIO.CheckBOM: Stream is nil'); Assert(Assigned(Encoding), 'TFileIO.CheckBOM: Encoding is nil'); Preamble := Encoding.GetPreamble; + if Length(Preamble) = 0 then + Exit(False); if Stream.Size < Length(Preamble) then Exit(False); OldPos := Stream.Position; From 2a323b6c524ca1ac106b65d3a2f5a22cfb7a6d83 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:40:24 +0100 Subject: [PATCH 17/23] Update Build.html with alternate zip download link The old link to InfoZip zip.exe is http only. Added an alternative https link that gets the file from delphidabbler.com. Fixes #137 --- Build.html | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Build.html b/Build.html index ba8a9086f..5dde59363 100644 --- a/Build.html +++ b/Build.html @@ -284,11 +284,19 @@

- This program is used to create CodeSnip's release file. - You can get a Windows command line version at + This program is used to create CodeSnip's release file. The InfoZip + version of zip is required. You can get a Windows command line version at http://stahlforce.com/dev/index.php?tool=zipunzip. + >http://stahlforce.com/dev/index.php?tool=zipunzip. +

+ +

+ Warning: The above link is http only. If you or + your browser object to the insecure link you can download an identical version + from delphidabbler.com, using the https protocol. See https://delphidabbler.com/extras/info-zip.

From 5217e273f4a81760a3b1ccd80bd63ab3e037a23a Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:06:47 +0100 Subject: [PATCH 18/23] Update user-db.html with links to REML documentation Fixes #134 --- Docs/Design/FileFormats/user-db.html | 50 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Docs/Design/FileFormats/user-db.html b/Docs/Design/FileFormats/user-db.html index bc761983e..d8d7773f0 100644 --- a/Docs/Design/FileFormats/user-db.html +++ b/Docs/Design/FileFormats/user-db.html @@ -322,15 +322,15 @@

  • version 6.0 to 6.10: Content is formatted text - encoded in REML markup. REML v4 is supported. + encoded in REML markup. REML v4 is supported.
  • version 6.11 & 6.12: Content is formatted text - encoded in REML markup. REML v5 is supported. + encoded in REML markup. REML v5 is supported.
  • version 6.13 & later: Content is formatted text - encoded in REML markup. REML v6 is supported. + encoded in REML markup. REML v6 is supported.
  • @@ -460,26 +460,26 @@

    version 2 and later: Additional information about a snippet. Content is formatted text encoded in - REML markup. + REML markup.
    • - version 2: supports REML v1. + version 2: supports REML v1.
    • - version 3: supports REML v2. + version 3: supports REML v2.
    • - version 4: supports REML v3. + version 4: supports REML v3.
    • - versions 5 & 6.10: supports REML v4. + versions 5 & 6.10: supports REML v4.
    • - version 6.11 & 6.12: supports REML v5. + version 6.11 & 6.12: supports REML v5.
    • - version 6.13 & later: supports REML v6. + version 6.13 & later: supports REML v6.
    @@ -788,7 +788,7 @@

    Supported Delphi compilers from Delphi 2 to Delphi 2007 plus Free Pascal.

    - REML not supported. + REML not supported.

    Data files were ANSI text using code page 1252. The XML file was in UTF-8 format with no BOM and no XML encoding attribute. @@ -833,8 +833,8 @@

    - The version of REML supported by the - codesnip-data/routines/routine/extra tag was v1. + The version of REML supported by the + codesnip-data/routines/routine/extra tag was v1.

    @@ -862,8 +862,8 @@

    - The version of REML supported by the - codesnip-data/routines/routine/extra tag was updated to v2. + The version of REML supported by the + codesnip-data/routines/routine/extra tag was updated to v2.

    @@ -875,8 +875,8 @@

    Introduced with CodeSnip v3.0.1.

    - The version of REML supported by the - codesnip-data/routines/routine/extra tag was updated to v3. + The version of REML supported by the + codesnip-data/routines/routine/extra tag was updated to v3.

    @@ -937,8 +937,8 @@

    New "class" and "unit" snippet kinds supported.

    - The version of REML supported by the - codesnip-data/routines/routine/extra tag was updated to v4. + The version of REML supported by the + codesnip-data/routines/routine/extra tag was updated to v4.

    @@ -950,7 +950,7 @@

    Introduced with CodeSnip v4.0 beta 1.

    - A snippet's description is now stored as formatted text using REML v4 markup. Previously the description was plain text. + A snippet's description is now stored as formatted text using REML v4 markup. Previously the description was plain text.

    The following tags were introduced: @@ -1028,7 +1028,7 @@

    Version 6.11 - 16 December 2022

    - Updated with CodeSnip v4.21.0 to add support for REML v5, which is backwards compatible with REML v4. + Updated with CodeSnip v4.21.0 to add support for REML v5, which is backwards compatible with REML v4.
    Version 6.12 - 7 November 2023 @@ -1040,7 +1040,7 @@

    Version 6.13 - 2 April 2024

    - Updated with CodeSnip v4.23.0 to add support for REML v6, which is backwards compatible with REML v4. + Updated with CodeSnip v4.23.0 to add support for REML v6, which is backwards compatible with REML v4.
    @@ -1073,7 +1073,7 @@

    - into valid REML code that simulates the parsed content of the codesnip-data/routines/routine/extra tag. + into valid REML code that simulates the parsed content of the codesnip-data/routines/routine/extra tag.

    @@ -1090,7 +1090,7 @@

    • Convert the plain text snippet description read from - codesnip-data/routines/routine/description into the REML + codesnip-data/routines/routine/description into the REML equivalent of a single paragraph containing the description.
    • @@ -1100,7 +1100,7 @@

    - Readers of v2 and later files may parse REML from any file version as if it were REML v6, since all versions of REML up to v6 are compatible. + Readers of v2 and later files may parse REML from any file version as if it were REML v6, since all versions of REML up to v6 are compatible.

    From 45b06d76452947e9ff3d193f9ed857f823faec4d Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:02:40 +0100 Subject: [PATCH 19/23] Omit untested compilers from generated snippet info Modified TSnippetDoc base class to filter out compilers without any test information and to use a special no tests message where no test compilations have been recorded. Modified both TTextSnippetDoc & TRTFSnippetDoc to render new "no tests" message. Also modified TRTFSnippetDoc to widen spacing between Compiler name and result in compile results table. Fixes #143 --- Src/URTFSnippetDoc.pas | 28 ++++++++++++++++++++++--- Src/USnippetDoc.pas | 45 ++++++++++++++++++++++++++++++----------- Src/UTextSnippetDoc.pas | 15 +++++++++++++- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/Src/URTFSnippetDoc.pas b/Src/URTFSnippetDoc.pas index b4f41d63a..0fe04c353 100644 --- a/Src/URTFSnippetDoc.pas +++ b/Src/URTFSnippetDoc.pas @@ -93,10 +93,14 @@ TRTFSnippetDoc = class(TSnippetDoc) /// to document. procedure RenderTitledList(const Title: string; List: IStringList); override; - /// Adds given compiler info, preceeded by given heading, to - /// document. + /// Output given compiler test info, preceded by given heading. + /// procedure RenderCompilerInfo(const Heading: string; const Info: TCompileDocInfoArray); override; + /// Output message stating that there is no compiler test info, + /// preceded by given heading. + procedure RenderNoCompilerInfo(const Heading, NoCompileTests: string); + override; /// Interprets and adds given extra information to document. /// /// Active text formatting is observed and styled to suit @@ -341,7 +345,8 @@ procedure TRTFSnippetDoc.RenderCompilerInfo(const Heading: string; TabStop: SmallInt; // tab stop where compile result displayed begin // Calculate tab stop where compile results are displayed - TabStop := (MaxCompilerNameLenInTwips div IndentDelta) * IndentDelta + IndentDelta; + TabStop := (MaxCompilerNameLenInTwips div IndentDelta) * IndentDelta + + 2 * IndentDelta; // Display heading fBuilder.SetFontStyle([fsBold]); fBuilder.SetParaSpacing( @@ -423,6 +428,23 @@ procedure TRTFSnippetDoc.RenderHeading(const Heading: string; fBuilder.EndPara; end; +procedure TRTFSnippetDoc.RenderNoCompilerInfo(const Heading, + NoCompileTests: string); +begin + // Display heading + fBuilder.SetFontStyle([fsBold]); + fBuilder.SetParaSpacing( + TRTFParaSpacing.Create(ParaSpacing, ParaSpacing / 3) + ); + fBuilder.AddText(Heading); + fBuilder.ResetCharStyle; + fBuilder.EndPara; + fBuilder.ClearParaFormatting; + fBuilder.SetFontSize(ParaFontSize); + fBuilder.AddText(NoCompileTests); + fBuilder.EndPara; +end; + procedure TRTFSnippetDoc.RenderSourceCode(const SourceCode: string); var Renderer: IHiliteRenderer; // renders highlighted source as RTF diff --git a/Src/USnippetDoc.pas b/Src/USnippetDoc.pas index 17fbe309b..35cd8e94a 100644 --- a/Src/USnippetDoc.pas +++ b/Src/USnippetDoc.pas @@ -39,7 +39,7 @@ TCompileDocInfo = record type /// Array of textual compiler result information. - TCompileDocInfoArray = array of TCompileDocInfo; + TCompileDocInfoArray = TArray; type /// Abstract base class for classes that render documents that @@ -76,10 +76,14 @@ TSnippetDoc = class(TObject) /// title. procedure RenderTitledList(const Title: string; List: IStringList); virtual; abstract; - /// Output given compiler info, preceeded by given heading. + /// Output given compiler test info, preceded by given heading. /// procedure RenderCompilerInfo(const Heading: string; const Info: TCompileDocInfoArray); virtual; abstract; + /// Output message stating that there is no compiler test info, + /// preceded by given heading. + procedure RenderNoCompilerInfo(const Heading, NoCompileTests: string); + virtual; abstract; /// Output given extra information to document. /// Active text must be interpreted in a manner that makes sense /// for document format. @@ -109,6 +113,7 @@ implementation uses // Delphi SysUtils, + Generics.Collections, // Project Compilers.UCompilers, DB.UMain, @@ -136,17 +141,24 @@ function TSnippetDoc.CompilerInfo(const Snippet: TSnippet): var Compilers: ICompilers; // provided info about compilers Compiler: ICompiler; // each supported compiler - InfoIdx: Integer; // index into output array + ResList: TList; begin Compilers := TCompilersFactory.CreateAndLoadCompilers; SetLength(Result, Compilers.Count); - InfoIdx := 0; - for Compiler in Compilers do - begin - Result[InfoIdx] := TCompileDocInfo.Create( - Compiler.GetName, Snippet.Compatibility[Compiler.GetID] - ); - Inc(InfoIdx); + ResList := TList.Create; + try + for Compiler in Compilers do + begin + if Snippet.Compatibility[Compiler.GetID] <> crQuery then + ResList.Add( + TCompileDocInfo.Create( + Compiler.GetName, Snippet.Compatibility[Compiler.GetID] + ) + ); + end; + Result := ResList.ToArray; + finally + ResList.Free; end; end; @@ -158,7 +170,10 @@ function TSnippetDoc.Generate(const Snippet: TSnippet): TEncodedData; sUnitListTitle = 'Required units:'; sDependListTitle = 'Required snippets:'; sXRefListTitle = 'See also:'; - sCompilers = 'Supported compilers:'; + sCompilers = 'Compiler test results:'; + sNoCompilerTests = 'No compiler tests were carried out.'; +var + CompileResults: TCompileDocInfoArray; begin Assert(Assigned(Snippet), ClassName + '.Create: Snippet is nil'); // generate document @@ -176,7 +191,13 @@ function TSnippetDoc.Generate(const Snippet: TSnippet): TEncodedData; RenderTitledList(sDependListTitle, SnippetsToStrings(Snippet.Depends)); RenderTitledList(sXRefListTitle, SnippetsToStrings(Snippet.XRef)); if Snippet.Kind <> skFreeform then - RenderCompilerInfo(sCompilers, CompilerInfo(Snippet)); + begin + CompileResults := CompilerInfo(Snippet); + if Length(CompileResults) > 0 then + RenderCompilerInfo(sCompilers, CompilerInfo(Snippet)) + else + RenderNoCompilerInfo(sCompilers, sNoCompilerTests); + end; if Snippet.Extra.HasContent then RenderExtra(Snippet.Extra); if not Snippet.UserDefined then diff --git a/Src/UTextSnippetDoc.pas b/Src/UTextSnippetDoc.pas index 5b80fd32b..4ea009d9d 100644 --- a/Src/UTextSnippetDoc.pas +++ b/Src/UTextSnippetDoc.pas @@ -63,10 +63,14 @@ TTextSnippetDoc = class(TSnippetDoc) /// to document. procedure RenderTitledList(const Title: string; List: IStringList); override; - /// Adds given compiler info, preceeded by given heading, to + /// Adds given compiler info, preceded by given heading, to /// document. procedure RenderCompilerInfo(const Heading: string; const Info: TCompileDocInfoArray); override; + /// Output message stating that there is no compiler test info, + /// preceded by given heading. + procedure RenderNoCompilerInfo(const Heading, NoCompileTests: string); + override; /// Interprets and adds given extra information to document. /// /// Active text is converted to word-wrapped plain text @@ -166,6 +170,15 @@ procedure TTextSnippetDoc.RenderHeading(const Heading: string; fWriter.WriteLine(Heading); end; +procedure TTextSnippetDoc.RenderNoCompilerInfo(const Heading, + NoCompileTests: string); +begin + // Write out compilers with results + fWriter.WriteLine; + fWriter.WriteLine(Heading); + fWriter.WriteLine(NoCompileTests); +end; + procedure TTextSnippetDoc.RenderSourceCode(const SourceCode: string); begin fWriter.WriteLine; From fa71b941198b8cf1048dbc533adc9d1ecd1b81c6 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:07:53 +0100 Subject: [PATCH 20/23] Update pjsysinfo library unit to v5.30.0 Fixes #144 --- Src/3rdParty/PJSysInfo.pas | 1048 +++++++++++++++++++++++++----------- 1 file changed, 726 insertions(+), 322 deletions(-) diff --git a/Src/3rdParty/PJSysInfo.pas b/Src/3rdParty/PJSysInfo.pas index 88f726505..342fd7750 100644 --- a/Src/3rdParty/PJSysInfo.pas +++ b/Src/3rdParty/PJSysInfo.pas @@ -59,6 +59,7 @@ {$UNDEF RTLNAMESPACES} // No support for RTL namespaces in unit names {$UNDEF HASUNIT64} // UInt64 type not defined {$UNDEF INLINEMETHODS} // No support for inline methods +{$UNDEF HASTBYTES} // TBytes not defined // Undefine facilities not available in earlier compilers // Note: Delphi 1 to 3 is not included since the code will not compile on these @@ -80,6 +81,9 @@ {$IF CompilerVersion >= 24.0} // Delphi XE3 and later {$LEGACYIFEND ON} // NOTE: this must come before all $IFEND directives {$IFEND} + {$IF CompilerVersion >= 18.5} // Delphi 2007 Win32 and later + {$DEFINE HASTBYTES} + {$IFEND} {$IF CompilerVersion >= 23.0} // Delphi XE2 and later {$DEFINE RTLNAMESPACES} {$IFEND} @@ -115,6 +119,11 @@ interface System.SysUtils, System.Classes, Winapi.Windows; {$ENDIF} +{$IFNDEF HASTBYTES} +// Compiler doesn't have TBytes: define it +type + TBytes = array of Byte; +{$ENDIF} type // Windows types not defined in all supported Delphi VCLs @@ -234,107 +243,190 @@ interface // These Windows-defined constants are required for use with the // GetProductInfo API call used with Windows Vista and later + // NOTE: PRODUCT_xxx constants marked with an asterisk comment have no + // associated description hard wired into this unit. // ** Thanks to Laurent Pierre for providing these definitions originally. // ** Subsequent additions were obtained from https://tinyurl.com/3rhhbs2z - PRODUCT_BUSINESS = $00000006; - PRODUCT_BUSINESS_N = $00000010; - PRODUCT_CLUSTER_SERVER = $00000012; - PRODUCT_CLUSTER_SERVER_V = $00000040; - PRODUCT_CORE = $00000065; - PRODUCT_CORE_COUNTRYSPECIFIC = $00000063; - PRODUCT_CORE_N = $00000062; - PRODUCT_CORE_SINGLELANGUAGE = $00000064; - PRODUCT_DATACENTER_EVALUATION_SERVER = $00000050; - PRODUCT_DATACENTER_A_SERVER_CORE = $00000091; - PRODUCT_STANDARD_A_SERVER_CORE = $00000092; - PRODUCT_DATACENTER_SERVER = $00000008; - PRODUCT_DATACENTER_SERVER_CORE = $0000000C; - PRODUCT_DATACENTER_SERVER_CORE_V = $00000027; - PRODUCT_DATACENTER_SERVER_V = $00000025; - PRODUCT_EDUCATION = $00000079; - PRODUCT_EDUCATION_N = $0000007A; - PRODUCT_ENTERPRISE = $00000004; - PRODUCT_ENTERPRISE_E = $00000046; - PRODUCT_ENTERPRISE_EVALUATION = $00000048; - PRODUCT_ENTERPRISE_N = $0000001B; - PRODUCT_ENTERPRISE_N_EVALUATION = $00000054; - PRODUCT_ENTERPRISE_S = $0000007D; - PRODUCT_ENTERPRISE_S_EVALUATION = $00000081; - PRODUCT_ENTERPRISE_S_N = $0000007E; - PRODUCT_ENTERPRISE_S_N_EVALUATION = $00000082; - PRODUCT_ENTERPRISE_SERVER = $0000000A; - PRODUCT_ENTERPRISE_SERVER_CORE = $0000000E; - PRODUCT_ENTERPRISE_SERVER_CORE_V = $00000029; - PRODUCT_ENTERPRISE_SERVER_IA64 = $0000000F; - PRODUCT_ENTERPRISE_SERVER_V = $00000026; - PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL = $0000003C; - PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC = $0000003E; - PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT = $0000003B; - PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC = $0000003D; - PRODUCT_HOME_BASIC = $00000002; - PRODUCT_HOME_BASIC_E = $00000043; - PRODUCT_HOME_BASIC_N = $00000005; - PRODUCT_HOME_PREMIUM = $00000003; - PRODUCT_HOME_PREMIUM_E = $00000044; - PRODUCT_HOME_PREMIUM_N = $0000001A; - PRODUCT_HOME_PREMIUM_SERVER = $00000022; - PRODUCT_HOME_SERVER = $00000013; - PRODUCT_HYPERV = $0000002A; - PRODUCT_IOTENTERPRISE = $000000BC; - PRODUCT_IOTENTERPRISE_S = $000000BF; - PRODUCT_IOTUAP = $0000007B; - PRODUCT_IOTUAPCOMMERCIAL = $00000083; - PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = $0000001E; - PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = $00000020; - PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = $0000001F; - PRODUCT_MOBILE_CORE = $00000068; - PRODUCT_MOBILE_ENTERPRISE = $00000085; - PRODUCT_MULTIPOINT_PREMIUM_SERVER = $0000004D; - PRODUCT_MULTIPOINT_STANDARD_SERVER = $0000004C; - PRODUCT_PRO_WORKSTATION = $000000A1; - PRODUCT_PRO_WORKSTATION_N = $000000A2; - PRODUCT_PROFESSIONAL = $00000030; - PRODUCT_PROFESSIONAL_E = $00000045; - PRODUCT_PROFESSIONAL_N = $00000031; - PRODUCT_PROFESSIONAL_WMC = $00000067; - PRODUCT_SB_SOLUTION_SERVER = $00000032; - PRODUCT_SB_SOLUTION_SERVER_EM = $00000036; - PRODUCT_SERVER_FOR_SB_SOLUTIONS = $00000033; - PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM = $00000037; - PRODUCT_SERVER_FOR_SMALLBUSINESS = $00000018; - PRODUCT_SERVER_FOR_SMALLBUSINESS_V = $00000023; - PRODUCT_SERVER_FOUNDATION = $00000021; - PRODUCT_SMALLBUSINESS_SERVER = $00000009; - PRODUCT_SMALLBUSINESS_SERVER_PREMIUM = $00000019; - PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE = $0000003F; - PRODUCT_SOLUTION_EMBEDDEDSERVER = $00000038; - PRODUCT_STANDARD_EVALUATION_SERVER = $0000004F; - PRODUCT_STANDARD_SERVER = $00000007; - PRODUCT_STANDARD_SERVER_CORE = $0000000D; - PRODUCT_STANDARD_SERVER_CORE_V = $00000028; - PRODUCT_STANDARD_SERVER_V = $00000024; - PRODUCT_STANDARD_SERVER_SOLUTIONS = $00000034; - PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE = $00000035; - PRODUCT_STARTER = $0000000B; - PRODUCT_STARTER_E = $00000042; - PRODUCT_STARTER_N = $0000002F; - PRODUCT_STORAGE_ENTERPRISE_SERVER = $00000017; - PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE = $0000002E; - PRODUCT_STORAGE_EXPRESS_SERVER = $00000014; - PRODUCT_STORAGE_EXPRESS_SERVER_CORE = $0000002B; - PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER = $00000060; - PRODUCT_STORAGE_STANDARD_SERVER = $00000015; - PRODUCT_STORAGE_STANDARD_SERVER_CORE = $0000002C; - PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER = $0000005F; - PRODUCT_STORAGE_WORKGROUP_SERVER = $00000016; - PRODUCT_STORAGE_WORKGROUP_SERVER_CORE = $0000002D; - PRODUCT_ULTIMATE = $00000001; - PRODUCT_ULTIMATE_E = $00000047; - PRODUCT_ULTIMATE_N = $0000001C; - PRODUCT_UNDEFINED = $00000000; - PRODUCT_WEB_SERVER = $00000011; - PRODUCT_WEB_SERVER_CORE = $0000001D; - PRODUCT_UNLICENSED = $ABCDABCD; + // ** and the Windows 11 24H2 SDK + PRODUCT_UNDEFINED = $00000000; + PRODUCT_ULTIMATE = $00000001; + PRODUCT_HOME_BASIC = $00000002; + PRODUCT_HOME_PREMIUM = $00000003; + PRODUCT_ENTERPRISE = $00000004; + PRODUCT_HOME_BASIC_N = $00000005; + PRODUCT_BUSINESS = $00000006; + PRODUCT_STANDARD_SERVER = $00000007; + PRODUCT_DATACENTER_SERVER = $00000008; + PRODUCT_SMALLBUSINESS_SERVER = $00000009; + PRODUCT_ENTERPRISE_SERVER = $0000000A; + PRODUCT_STARTER = $0000000B; + PRODUCT_DATACENTER_SERVER_CORE = $0000000C; + PRODUCT_STANDARD_SERVER_CORE = $0000000D; + PRODUCT_ENTERPRISE_SERVER_CORE = $0000000E; + PRODUCT_ENTERPRISE_SERVER_IA64 = $0000000F; + PRODUCT_BUSINESS_N = $00000010; + PRODUCT_WEB_SERVER = $00000011; + PRODUCT_CLUSTER_SERVER = $00000012; + PRODUCT_HOME_SERVER = $00000013; + PRODUCT_STORAGE_EXPRESS_SERVER = $00000014; + PRODUCT_STORAGE_STANDARD_SERVER = $00000015; + PRODUCT_STORAGE_WORKGROUP_SERVER = $00000016; + PRODUCT_STORAGE_ENTERPRISE_SERVER = $00000017; + PRODUCT_SERVER_FOR_SMALLBUSINESS = $00000018; + PRODUCT_SMALLBUSINESS_SERVER_PREMIUM = $00000019; + PRODUCT_HOME_PREMIUM_N = $0000001A; + PRODUCT_ENTERPRISE_N = $0000001B; + PRODUCT_ULTIMATE_N = $0000001C; + PRODUCT_WEB_SERVER_CORE = $0000001D; + PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = $0000001E; + PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = $0000001F; + PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = $00000020; + PRODUCT_SERVER_FOUNDATION = $00000021; + PRODUCT_HOME_PREMIUM_SERVER = $00000022; + PRODUCT_SERVER_FOR_SMALLBUSINESS_V = $00000023; + PRODUCT_STANDARD_SERVER_V = $00000024; + PRODUCT_DATACENTER_SERVER_V = $00000025; + PRODUCT_ENTERPRISE_SERVER_V = $00000026; + PRODUCT_DATACENTER_SERVER_CORE_V = $00000027; + PRODUCT_STANDARD_SERVER_CORE_V = $00000028; + PRODUCT_ENTERPRISE_SERVER_CORE_V = $00000029; + PRODUCT_HYPERV = $0000002A; + PRODUCT_STORAGE_EXPRESS_SERVER_CORE = $0000002B; + PRODUCT_STORAGE_STANDARD_SERVER_CORE = $0000002C; + PRODUCT_STORAGE_WORKGROUP_SERVER_CORE = $0000002D; + PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE = $0000002E; + PRODUCT_STARTER_N = $0000002F; + PRODUCT_PROFESSIONAL = $00000030; + PRODUCT_PROFESSIONAL_N = $00000031; + PRODUCT_SB_SOLUTION_SERVER = $00000032; + PRODUCT_SERVER_FOR_SB_SOLUTIONS = $00000033; + PRODUCT_STANDARD_SERVER_SOLUTIONS = $00000034; + PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE = $00000035; + PRODUCT_SB_SOLUTION_SERVER_EM = $00000036; + PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM = $00000037; + PRODUCT_SOLUTION_EMBEDDEDSERVER = $00000038; + PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE = $00000039; // * + PRODUCT_PROFESSIONAL_EMBEDDED = $0000003A; // * + PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT = $0000003B; + PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL = $0000003C; + PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC = $0000003D; + PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC = $0000003E; + PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE = $0000003F; + PRODUCT_CLUSTER_SERVER_V = $00000040; + PRODUCT_EMBEDDED = $00000041; // * + PRODUCT_STARTER_E = $00000042; + PRODUCT_HOME_BASIC_E = $00000043; + PRODUCT_HOME_PREMIUM_E = $00000044; + PRODUCT_PROFESSIONAL_E = $00000045; + PRODUCT_ENTERPRISE_E = $00000046; + PRODUCT_ULTIMATE_E = $00000047; + PRODUCT_ENTERPRISE_EVALUATION = $00000048; + PRODUCT_MULTIPOINT_STANDARD_SERVER = $0000004C; + PRODUCT_MULTIPOINT_PREMIUM_SERVER = $0000004D; + PRODUCT_STANDARD_EVALUATION_SERVER = $0000004F; + PRODUCT_DATACENTER_EVALUATION_SERVER = $00000050; + PRODUCT_ENTERPRISE_N_EVALUATION = $00000054; + PRODUCT_EMBEDDED_AUTOMOTIVE = $00000055; // * + PRODUCT_EMBEDDED_INDUSTRY_A = $00000056; // * + PRODUCT_THINPC = $00000057; // * + PRODUCT_EMBEDDED_A = $00000058; // * + PRODUCT_EMBEDDED_INDUSTRY = $00000059; // * + PRODUCT_EMBEDDED_E = $0000005A; // * + PRODUCT_EMBEDDED_INDUSTRY_E = $0000005B; // * + PRODUCT_EMBEDDED_INDUSTRY_A_E = $0000005C; // * + PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER = $0000005F; + PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER = $00000060; + PRODUCT_CORE_ARM = $00000061; + PRODUCT_CORE_N = $00000062; + PRODUCT_CORE_COUNTRYSPECIFIC = $00000063; + PRODUCT_CORE_SINGLELANGUAGE = $00000064; + PRODUCT_CORE = $00000065; + PRODUCT_PROFESSIONAL_WMC = $00000067; + PRODUCT_MOBILE_CORE = $00000068; + PRODUCT_EMBEDDED_INDUSTRY_EVAL = $00000069; // * + PRODUCT_EMBEDDED_INDUSTRY_E_EVAL = $0000006A; // * + PRODUCT_EMBEDDED_EVAL = $0000006B; // * + PRODUCT_EMBEDDED_E_EVAL = $0000006C; // * + PRODUCT_NANO_SERVER = $0000006D; // * + PRODUCT_CLOUD_STORAGE_SERVER = $0000006E; // * + PRODUCT_CORE_CONNECTED = $0000006F; // * + PRODUCT_PROFESSIONAL_STUDENT = $00000070; // * + PRODUCT_CORE_CONNECTED_N = $00000071; // * + PRODUCT_PROFESSIONAL_STUDENT_N = $00000072; // * + PRODUCT_CORE_CONNECTED_SINGLELANGUAGE = $00000073; // * + PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC = $00000074; // * + PRODUCT_CONNECTED_CAR = $00000075; // * + PRODUCT_INDUSTRY_HANDHELD = $00000076; // * + PRODUCT_PPI_PRO = $00000077; // * + PRODUCT_ARM64_SERVER = $00000078; // * + PRODUCT_EDUCATION = $00000079; + PRODUCT_EDUCATION_N = $0000007A; + PRODUCT_IOTUAP = $0000007B; + PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER = $0000007C; // * + PRODUCT_ENTERPRISE_S = $0000007D; + PRODUCT_ENTERPRISE_S_N = $0000007E; + PRODUCT_PROFESSIONAL_S = $0000007F; // * + PRODUCT_PROFESSIONAL_S_N = $00000080; // * + PRODUCT_ENTERPRISE_S_EVALUATION = $00000081; + PRODUCT_ENTERPRISE_S_N_EVALUATION = $00000082; + PRODUCT_IOTUAPCOMMERCIAL = $00000083; + PRODUCT_MOBILE_ENTERPRISE = $00000085; + PRODUCT_HOLOGRAPHIC = $00000087; // * + PRODUCT_HOLOGRAPHIC_BUSINESS = $00000088; // * + PRODUCT_PRO_SINGLE_LANGUAGE = $0000008A; // * + PRODUCT_PRO_CHINA = $0000008B; // * + PRODUCT_ENTERPRISE_SUBSCRIPTION = $0000008C; // * + PRODUCT_ENTERPRISE_SUBSCRIPTION_N = $0000008D; // * + PRODUCT_DATACENTER_NANO_SERVER = $0000008F; + PRODUCT_STANDARD_NANO_SERVER = $00000090; + PRODUCT_DATACENTER_A_SERVER_CORE = $00000091; + PRODUCT_STANDARD_A_SERVER_CORE = $00000092; + PRODUCT_DATACENTER_WS_SERVER_CORE = $00000093; + PRODUCT_STANDARD_WS_SERVER_CORE = $00000094; + PRODUCT_UTILITY_VM = $00000095; // * + PRODUCT_DATACENTER_EVALUATION_SERVER_CORE = $0000009F; // * + PRODUCT_STANDARD_EVALUATION_SERVER_CORE = $000000A0; // * + PRODUCT_PRO_WORKSTATION = $000000A1; + PRODUCT_PRO_WORKSTATION_N = $000000A2; + PRODUCT_PRO_FOR_EDUCATION = $000000A4; + PRODUCT_PRO_FOR_EDUCATION_N = $000000A5; // * + PRODUCT_AZURE_SERVER_CORE = $000000A8; // * + PRODUCT_AZURE_NANO_SERVER = $000000A9; // * + PRODUCT_ENTERPRISEG = $000000AB; // * + PRODUCT_ENTERPRISEGN = $000000AC; // * + PRODUCT_SERVERRDSH = $000000AF; + PRODUCT_CLOUD = $000000B2; // * + PRODUCT_CLOUDN = $000000B3; // * + PRODUCT_HUBOS = $000000B4; // * + PRODUCT_ONECOREUPDATEOS = $000000B6; // * + PRODUCT_CLOUDE = $000000B7; // * + PRODUCT_IOTOS = $000000B9; // * + PRODUCT_CLOUDEN = $000000BA; // * + PRODUCT_IOTEDGEOS = $000000BB; // * + PRODUCT_IOTENTERPRISE = $000000BC; + PRODUCT_LITE = $000000BD; // * + PRODUCT_IOTENTERPRISE_S = $000000BF; + PRODUCT_XBOX_SYSTEMOS = $000000C0; // * + PRODUCT_XBOX_GAMEOS = $000000C2; // * + PRODUCT_XBOX_ERAOS = $000000C3; // * + PRODUCT_XBOX_DURANGOHOSTOS = $000000C4; // * + PRODUCT_XBOX_SCARLETTHOSTOS = $000000C5; // * + PRODUCT_XBOX_KEYSTONE = $000000C6; // * + PRODUCT_AZURE_SERVER_CLOUDHOST = $000000C7; // * + PRODUCT_AZURE_SERVER_CLOUDMOS = $000000C8; // * + PRODUCT_CLOUDEDITIONN = $000000CA; // * + PRODUCT_CLOUDEDITION = $000000CB; // * + PRODUCT_VALIDATION = $000000CC; // * + PRODUCT_IOTENTERPRISESK = $000000CD; // * + PRODUCT_IOTENTERPRISEK = $000000CE; // * + PRODUCT_IOTENTERPRISESEVAL = $000000CF; // * + PRODUCT_AZURE_SERVER_AGENTBRIDGE = $000000D0; // * + PRODUCT_AZURE_SERVER_NANOHOST = $000000D1; // * + PRODUCT_WNC = $000000D2; // * + PRODUCT_AZURESTACKHCI_SERVER_CORE = $00000196; // * + PRODUCT_DATACENTER_SERVER_AZURE_EDITION = $00000197; + PRODUCT_DATACENTER_SERVER_CORE_AZURE_EDITION = $00000198; // * + PRODUCT_UNLICENSED = $ABCDABCD; // These constants are required for use with GetSystemMetrics to detect // certain editions. GetSystemMetrics returns non-zero when passed these flags @@ -454,6 +546,17 @@ interface bmSafeModeNetwork // Booted in safe node with networking ); +type + // Various Windows 10 & 11 release versions + TPJWin10PlusVersion = ( + win10plusNA, + win10plusUnknown, + win10v1507, win10v1511, win10v1607, win10v1703, win10v1709, win10v1803, + win10v1809, win10v1903, win10v1909, win10v2004, win10v20H2, win10v21H1, + win10v21H2, win10v22H2, + win11v21H2, win11v22H2, win11v23H2, win11v24H2 + ); + type /// Class of exception raised by code in this unit. EPJSysInfo = class(Exception); @@ -473,10 +576,13 @@ TPJOSInfo = class(TObject) /// True if suite is installed, False if not installed or not an /// NT platform OS. class function CheckSuite(const Suite: Integer): Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} + + /// Gets product edition from registry for NT4 pre SP6. + class function NTEditionFromReg: string; - /// Gets product edition from registry. - /// Needed to get edition for NT4 pre SP6. - class function EditionFromReg: string; + /// Gets edition ID from registry. + class function EditionIDFromReg: string; /// Checks registry to see if NT4 Service Pack 6a is installed. /// @@ -498,6 +604,18 @@ TPJOSInfo = class(TObject) class function IsReallyWindowsVersionOrGreater(MajorVersion, MinorVersion, ServicePackMajor: Word): Boolean; + /// Checks if the operating system is Windows 10 or later, with a + /// version identifier the same or later than the given version identifier. + /// + /// + /// WARNING: Windows 11 versions are always considered to be later + /// Windows 10 versions, even if the Windows 10 version was released after + /// the Windows 11 version. + /// AVersion must not be one of win10plusNA or + /// win10plusUnknown. + class function IsWindows10PlusVersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; + public /// Checks if the OS can be "spoofed" by specifying a @@ -534,17 +652,21 @@ TPJOSInfo = class(TObject) /// Checks if Windows Media Center is installed. class function IsMediaCenter: Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} /// Checks if the program is running on a tablet PC OS. class function IsTabletPC: Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} /// Checks if the program is running under Windows Terminal Server /// as a client session. class function IsRemoteSession: Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} /// Checks of the host operating system has pen extensions /// installed. class function HasPenExtensions: Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} /// Returns the host OS platform identifier. class function Platform: TPJOSPlatform; @@ -605,6 +727,9 @@ TPJOSInfo = class(TObject) /// Returns the Windows product ID of the host OS. class function ProductID: string; + /// Returns the digital product ID of the host OS. + class function DigitalProductID: TBytes; + /// Organisation to which Windows is registered, if any. class function RegisteredOrganisation: string; @@ -740,6 +865,46 @@ TPJOSInfo = class(TObject) class function IsReallyWindows10OrGreater: Boolean; {$IFDEF INLINEMETHODS}inline;{$ENDIF} + /// Returns an identifier representing a Windows 10 or 11 + /// version. + /// If the OS is earlier than Windows 10 then win10plusNA + /// is returned. If the OS is Windows 10 or later but is a dev, beta etc. + /// build whose version can't be detected then win10plusUnknown is + /// returned. + class function Windows10PlusVersion: TPJWin10PlusVersion; + + /// Returns the version name of a the current operating system, if + /// it is Windows 10 or later. + /// + /// NOTE: some Windows 10 and 11 versions have the same string. + /// + /// If the OS is earlier than Windows 10 then an empty string is + /// returned. If the OS is Windows 10 or later but is a dev, beta etc. + /// build whose version can't be detected then 'Unknown' is returned. + /// + /// + class function Windows10PlusVersionName: string; + + /// Checks if the operating system is Windows 10 or later, with a + /// version identifier the same or later than AVersion. + /// + /// AVersion must be a valid Windows 10 version + /// identifier, with a name that begins with win10v. + /// EPJSysInfo raised if AVersion is not a valid + /// Windows 10 version identifier. + class function IsWindows10VersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; + + /// Checks if the operating system is Windows 11 or later, with a + /// version identifier the same or later than AVersion. + /// + /// AVersion must be a valid Windows 11 version + /// identifier, with a name that begins with win11v. + /// EPJSysInfo raised if AVersion is not a valid + /// Windows 11 version identifier. + class function IsWindows11VersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; + /// Checks if the OS is a server version. /// /// For Windows 2000 and later the result always relates to the @@ -758,6 +923,12 @@ TPJOSInfo = class(TObject) /// that this value could be spoofed. /// class function RevisionNumber: Integer; + + /// Returns the repository branch from which the OS release was] + /// built. + /// Returns the empty string if no build branch information is + /// available. + class function BuildBranch: string; end; type @@ -810,6 +981,7 @@ TPJComputerInfo = class(TObject) /// Checks if a network is present on host computer. class function IsNetworkPresent: Boolean; + {$IFDEF INLINEMETHODS}inline;{$ENDIF} /// Returns the OS mode used when host computer was last booted. /// @@ -984,6 +1156,7 @@ implementation sUnknownProduct = 'Unrecognised operating system product'; sBadRegType = 'Unsupported registry type'; sBadRegIntType = 'Integer value expected in registry'; + sBadRegBinType = 'Binary value expected in registry'; sBadProcHandle = 'Bad process handle'; @@ -994,13 +1167,14 @@ implementation UInt64 = Int64; {$ENDIF} - const // Map of product codes per GetProductInfo API to product names + // Names are not available for all PRODUCT_xxx values. // ** Laurent Pierre supplied original code on which this map is based // It has been modified and extended using MSDN documentation at - // https://msdn.microsoft.com/en-us/library/ms724358 - cProductMap: array[1..99] of record + // https://msdn.microsoft.com/en-us/library/ms724358 and + // https://tinyurl.com/5684558v (learn.microsoft.com) + cProductMap: array[1..107] of record Id: Cardinal; // product ID Name: string; // product name end = ( @@ -1200,6 +1374,22 @@ implementation Name: 'Web Server (full installation)';), (Id: PRODUCT_WEB_SERVER_CORE; Name: 'Web Server (core installation)';), + (Id: PRODUCT_CORE_ARM; + Name: 'Windows RT';), + (Id: PRODUCT_DATACENTER_NANO_SERVER; + Name: 'Windows Server Datacenter Edition (Nano Server installation)';), + (Id: PRODUCT_STANDARD_NANO_SERVER; + Name: 'Windows Server Standard Edition (Nano Server installation)';), + (Id: PRODUCT_DATACENTER_WS_SERVER_CORE; + Name: 'Windows Server Datacenter Edition (Server Core installation)';), + (Id: PRODUCT_STANDARD_WS_SERVER_CORE; + Name: 'Windows Server Standard Edition (Server Core installation)';), + (Id: PRODUCT_PRO_FOR_EDUCATION; + Name: 'Windows 10 Pro Education';), + (Id: PRODUCT_SERVERRDSH; + Name: 'Windows 10 Enterprise for Virtual Desktops';), + (Id: PRODUCT_DATACENTER_SERVER_AZURE_EDITION; + Name: 'Windows Server Datacenter: Azure Edition';), (Id: Cardinal(PRODUCT_UNLICENSED); Name: 'Unlicensed product';) ); @@ -1220,8 +1410,11 @@ TBuildNameMap = record LoRev: Integer; HiRev: Integer; Name: string; + Version: Word; end; + TWin10PlusVersionSet = set of TPJWin10PlusVersion; + const { Known windows build numbers. @@ -1265,14 +1458,14 @@ TBuildNameMap = record // Windows Vista ------------------------------------------------------------- - WinVistaBaseBuild = 6000; + WinVista_Base_Build = 6000; // Windows 7 ----------------------------------------------------------------- - Win7BaseBuild = 7600; + Win7_Base_Build = 7600; // Windows 8 ----------------------------------------------------------------- - Win8Build = 9200; // Build number used for all Win 8/Svr 2012 - Win8Point1Build = 9600; // Build number used for all Win 8.1/Svr 2012 R2 + Win8_Build = 9200; // Build number used for all Win 8/Svr 2012 + Win8Point1_Build = 9600; // Build number used for all Win 8.1/Svr 2012 R2 // Windows 10 ---------------------------------------------------------------- @@ -1364,7 +1557,7 @@ TBuildNameMap = record ); { - End of support information for Windows 10 versions (as of 2023-05-01). + End of support information for Windows 10 versions (as of 2024-10-01). GAC = General Availablity Channel. LTSC = Long Term Support Channel. @@ -1382,51 +1575,63 @@ TBuildNameMap = record 2004 | ended | N/a 20H2 | ended | N/a 21H1 | ended | N/a - 21H2 | 2024-06-11 | 2032-01-13 + 21H2 | ended | 2032-01-13 22H2 | 2025-10-14 | N/a } + // Win 10 release build numbers + Win10_1507_Build = 10240; + Win10_1511_Build = 10586; + Win10_1607_Build = 14393; + Win10_1703_Build = 15063; + Win10_1709_Build = 16299; + Win10_1803_Build = 17134; + Win10_1809_Build = 17763; + Win10_1903_Build = Win10_19XX_Shared_Build; + Win10_1909_Build = 18363; + Win10_2004_Build = 19041; + Win10_20H2_Build = 19042; + Win10_21H1_Build = 19043; // See **REF3** End of service @ rev 2364 + Win10_21H2_Build = 19044; // See **REF4** + Win10_22H2_Build = 19045; // See **REF5** + // Map of Win 10 builds from 1st release (version 1507) to version 20H2 + // Later Win 10 releases have special handling and aren't in the build map // // NOTE: The following versions that are still being maintained per the above // table have HiRev = MaxInt while the unsupported versions have HiRev set to // the final build number. - Win10BuildMap: array[0..10] of TBuildNameMap = ( - (Build: 10240; LoRev: 16484; HiRev: MaxInt; - Name: 'Version 1507'), - (Build: 10586; LoRev: 0; HiRev: 1540; - Name: 'Version 1511: November Update'), - (Build: 14393; LoRev: 0; HiRev: MaxInt; - Name: 'Version 1607: Anniversary Update'), - (Build: 15063; LoRev: 0; HiRev: 2679; - Name: 'Version 1703: Creators Update'), - (Build: 16299; LoRev: 15; HiRev: 2166; - Name: 'Version 1709: Fall Creators Update'), - (Build: 17134; LoRev: 1; HiRev: 2208; - Name: 'Version 1803: April 2018 Update'), - (Build: 17763; LoRev: 1; HiRev: MaxInt; - Name: 'Version 1809: October 2018 Update'), - (Build: Win10_19XX_Shared_Build; LoRev: 116; HiRev: 1256; - Name: 'Version 1903: May 2019 Update'), - (Build: 18363; LoRev: 327; HiRev: 2274; - Name: 'Version 1909: November 2019 Update'), - (Build: 19041; LoRev: 264; HiRev: 1415; - Name: 'Version 2004: May 2020 Update'), - (Build: 19042; LoRev: 572; HiRev: 2965; - Name: 'Version 20H2: October 2020 Update') + Win10_BuildMap: array[0..10] of TBuildNameMap = ( + (Build: Win10_1507_Build; LoRev: 16484; HiRev: MaxInt; + Name: 'Version 1507'; Version: Ord(win10v1507)), + (Build: Win10_1511_Build; LoRev: 0; HiRev: 1540; + Name: 'Version 1511: November Update'; Version: Ord(win10v1511)), + (Build: Win10_1607_Build; LoRev: 0; HiRev: MaxInt; + Name: 'Version 1607: Anniversary Update'; Version: Ord(win10v1607)), + (Build: Win10_1703_Build; LoRev: 0; HiRev: 2679; + Name: 'Version 1703: Creators Update'; Version: Ord(win10v1703)), + (Build: Win10_1709_Build; LoRev: 15; HiRev: 2166; + Name: 'Version 1709: Fall Creators Update'; Version: Ord(win10v1709)), + (Build: Win10_1803_Build; LoRev: 1; HiRev: 2208; + Name: 'Version 1803: April 2018 Update'; Version: Ord(win10v1803)), + (Build: Win10_1809_Build; LoRev: 1; HiRev: MaxInt; + Name: 'Version 1809: October 2018 Update'; Version: Ord(win10v1809)), + (Build: Win10_1903_Build; LoRev: 116; HiRev: 1256; + Name: 'Version 1903: May 2019 Update'; Version: Ord(win10v1903)), + (Build: Win10_1909_Build; LoRev: 327; HiRev: 2274; + Name: 'Version 1909: November 2019 Update'; Version: Ord(win10v1909)), + (Build: Win10_2004_Build; LoRev: 264; HiRev: 1415; + Name: 'Version 2004: May 2020 Update'; Version: Ord(win10v2004)), + (Build: Win10_20H2_Build; LoRev: 572; HiRev: 2965; + Name: 'Version 20H2: October 2020 Update'; Version: Ord(win10v20H2)) ); - // Additional information is available for Win 10 builds from version 21H1, - // as follows: - - // Windows 10 version 21H1 - see **REF3** in implementation for details - Win1021H1Build = 19043; // ** End of service 2022-12-13, rev 2364 - - // Windows 10 version 21H2 - see **REF4** in implementation for details - Win1021H2Build = 19044; - - // Windows 10 version 22H2 - see **REF5** in implementation for details - Win1022H2Build = 19045; + // Set of Windows 10 version identifiers + Win10_Versions: TWin10PlusVersionSet = [ + win10v1507, win10v1511, win10v1607, win10v1703, win10v1709, win10v1803, + win10v1809, win10v1903, win10v1909, win10v2004, win10v20H2, win10v21H1, + win10v21H2, win10v22H2 + ]; // Windows 10 slow ring, fast ring and skip-ahead channels were all expired // well before 2022-12-31 and are not detected. (In fact there was never any @@ -1437,50 +1642,59 @@ TBuildNameMap = record // NOTE: All releases of Windows 11 report version 10.0 { - End of support (EOS) information for Windows 11 versions (as of 2022-12-31). + End of support (EOS) information for Windows 11 versions (as of 2024-10-01). Version | Home, Pro | Education, | etc EOS | Enterprise | | etc EOS --------|------------|------------ - 21H2 | 2023-10-10 | 2024-10-08 + 21H2 | ENDED | 2024-10-08 22H2 | 2024-10-08 | 2025-10-14 23H2 | 2025-11-11 | 2026-11-10 + 24H2 | 2026-10-13 | 2027-10-12 } // 1st build released branded as Windows 11 // Insider version, Dev channel, v10.0.21996.1 - Win11DevBuild = 21996; + Win11_Dev_Build = 21996; // Windows 11 version 21H2 - see **REF6** in implementation for details - Win11v21H2Build = 22000; + Win11_21H2_Build = 22000; // Windows 11 version 22H2 // // Build 22621 was the original beta build. Same build used for releases and // various other channels. // See **REF1** in implementation - Win11v22H2Build = 22621; + Win11_22H2_Build = 22621; // Windows 11 version 22H3 // See **REF10** in implementation - Win11v23H2Build = 22631; + Win11_23H2_Build = 22631; + + // Windows 11 version 22H4 + // See **REF11** in implementation + Win11_24H2_Build = 26100; // "Preview Builds of October 2022 component update in Beta Channel" // See **REF2** in implementation - Win11Oct22ComponentBetaChannelBuild = 22622; + Win11_Oct22Component_BetaChannel_Build = 22622; // "Preview Builds of February 2023 component update in Beta Channel" // See **REF7** in implementation - Win11Feb23ComponentBetaChannelBuild = 22623; + Win11_Feb23Component_BetaChannel_Build = 22623; // "Preview builds of May 2023 component update in Beta Channel" // See **REF8** in implementation - Win11May23ComponentBetaChannelBuild = 22624; + Win11_May23Component_BetaChannel_Build = 22624; // "Preview builds of future component update in Beta Channel" // See **REF9** in implementation - Win11FutureComponentBetaChannelBuild = 22635; + Win11_FutureComponent_BetaChannel_Build = 22635; + + // "Preview builds of future component update in Dev Channel" + // See **REF12** in implementation + Win11_FutureComponent_DevChannel_Build = 26120; // Windows 11 Dev channel releases with version string "Dev" [^2] // pre Win 11 release (expired 2021/10/31): @@ -1492,63 +1706,61 @@ TBuildNameMap = record // 22526, 22533, 22538, 22543, 22557, 22563, // Windows 11 Dev channel releases with version string "22H2" [^2] - Win1122H2DevChannelDevBuilds: array[0..20] of Integer = ( - // expired 2022/09/15 (pre Win 11 22H2 beta release): - // 22567, 22572, 22579 - // expired 2022/09/15 (post Win 11 22H2 beta release): - // 25115, 25120, 25126, 25131, 25136, 25140, 25145, 25151, 25158, 25163, - // 25169, 25174, 25179, - // expired 2023/09/15 (post Win 11 22H2 beta release): - // 25182, 25188, 25193, 25197, 25201, 25206, 25211, - // expired 2023/09/15 (post Win 11 22H2 release): - // 25217, 25227, 25231, 25236, 25247, 25252, 25262, 25267, 25272, 25276, - // 25281, 25284, 25290, 25295, 25300, 25309, 23403, 23419, 23424, 23430, - // 23435, 23440, 23451, 23466, 23471, 23475, 23481, 23486, 23493, 23506, - // 23511, 23516, 23521, - // expiring 2024-09-15: - 23526, 23531, 23536, 23541, 23545, 23550, 23555, 23560, 23565, 23570, 23575, - 23580, 23585, 23590, 23595, 23601, 23606, 23612, 23615, 23619, 23620 - ); - - // Win 11 Dev channel releases with version string "24H2" [^2] - Win1124H2DevChannelDevBuilds: array[0..4] of Integer = ( - // expiring 2024-09-15: - 26052, 26058, 26080, 26085, 26090 - ); + // pre Win 11 22H2 beta release (expired 2022/09/15): + // 22567, 22572, 22579 + // post Win 11 22H2 beta release (expired 2022/09/15): + // 25115, 25120, 25126, 25131, 25136, 25140, 25145, 25151, 25158, 25163, + // 25169, 25174, 25179, + // post Win 11 22H2 beta release (expired 2023/09/15): + // 25182, 25188, 25193, 25197, 25201, 25206, 25211, + // post Win 11 22H2 release, ni_release string (expired 2023/09/15): + // 25217, 25227, 25231, 25236, 25247, 25252, 25262, 25267, 25272, 25276, + // 25281, 25284, 25290, 25295, 25300, 25309, + // post Win 11 22H2 release, ni_prerelease string (expired 2023/09/15): + // 23403, 23419, 23424, 23430, 23435, 23440, 23451, 23466, 23471, 23475, + // 23481, 23486, 23493, 23506, 23511, 23516, 23521, + // post Win 11 22H2 release, ni_prerelease string (expired 2024-09-15): + // 23526, 23531, 23536, 23541, 23545, 23550, 23555, 23560, 23565, 23570, + // 23575, 23580, 23585, 23590, 23595, 23601, 23606, 23612, 23615, 23619, + // 23620 // Preview builds of Windows 11 in the Canary Channel with version string // "22H2" [^2] - // (expired 2023-09-15): + // expired 2023-09-15: // 25314, 25324, 25330, 25336, 25346, 25352, 25357, 25370, // Preview builds of Windows 11 in the Canary Channel with version string // "23H2" [^2] - Win11Canary23H2PreviewBuilds: array[0..15] of Integer = ( - // expired 2023-09-15: - // 25375, 25381, 25387, 25393, 25905, 25915, 25921, 25926, - // expires 2024-09-15: - 25931, 25936, 25941, 25947, 25951, 25967, 25977, 25982, 25987, 25992, 25997, - 26002, 26010, 26016, 26020, 26040 - ); - - // Preview builds of Windows 11 in the Canary Channel with version string - // "24H2" [^2] - Win11Canary24H2PreviewBuilds: array[0..5] of Integer = ( - // expires 2024-09-15: - 26052, 26058, 26063, 26080, 26085, - // expiry date unknown - 26090 - ); + // Expired 2023-09-15: + // 25375, 25381, 25387, 25393, 25905, 25915, 25921, 25926, + // Expired 2024-09-15: + // 25931, 25936, 25941, 25947, 25951, 25967, 25977, 25982, 25987, 25992, + // 25997, 26002, 26010, 26016, 26020, 26040, 26063, 26200, 26212, 26217, + // 26227, 26231, 26236, 26241, 26244, 26252, 26257, 27686. // Windows 11 Dev & Beta channel builds with version string "22H2" [^2] - Win11DevBetaChannels22H2Builds: array[0..1] of Integer = ( + Win11_22H2_DevAndBetaChannel_Builds: array[0..1] of Integer = ( // Expired 2022/09/15: // 22581, 22593, 22598 // Unknown expiry date: 22610, 22616 ); - Win11FirstBuild = Win11DevBuild; // First build number of Windows 11 + // Windows 11 Preview, Dev & Canary channel builds with version "24H2" [^2] + Win11_24H2_DevAndCanaryChannel_Builds: array[0..1] of Integer = ( + // Expired 2024-09-15: + // 26052, 26058, 26080, 26085, + // Unknown expiry date: + 26090 {Dev revs:1,112; Canary revs: 1}, + 26100 {Dev revs:1,268; Canary revs: 1} + ); + + Win11_24H2_CanaryChannel_Builds: array[0..0] of Integer = ( + // expiring 2025-09-15: + 27695 + ); + + Win11_First_Build = Win11_Dev_Build; // First build number of Windows 11 // Windows server v10.0 version ---------------------------------------------- @@ -1556,9 +1768,14 @@ TBuildNameMap = record // version 10.0. There's always an exception with Windows versioning! // Last build numbers of each "major" release before moving on to the next - Win2016LastBuild = 17134; - Win2019LastBuild = 18363; - WinServerLastBuild = 19042; + Win2016_Last_Build = 17134; + Win2019_Last_Build = 18363; + WinServer_Last_Build = 19042; + + // Set of Windows 10 version identifiers + Win11_Versions: TWin10PlusVersionSet = [ + win11v21H2, win11v22H2, win11v23H2, win11v24H2 + ]; { End of support information for all Windows Server versions. @@ -1590,31 +1807,38 @@ TBuildNameMap = record // Map of Windows server releases that are named straightforwardly WinServerSimpleBuildMap: array[0..12] of TBuildNameMap = ( // Windows Server 2016 - (Build: 10074; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 2'), - (Build: 10514; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 3'), - (Build: 10586; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 4'), - (Build: 14300; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 5'), - (Build: 14393; LoRev: 0; HiRev: MaxInt; Name: 'Version 1607'), - (Build: 16299; LoRev: 0; HiRev: MaxInt; Name: 'Version 1709'), - (Build: Win2016LastBuild; LoRev: 0; HiRev: MaxInt; Name: 'Version 1803'), + (Build: 10074; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 2'; + Version: 0), + (Build: 10514; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 3'; + Version: 0), + (Build: 10586; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 4'; + Version: 0), + (Build: 14300; LoRev: 0; HiRev: MaxInt; Name: 'Technical Preview 5'; + Version: 0), + (Build: 14393; LoRev: 0; HiRev: MaxInt; Name: 'Version 1607'; Version: 0), + (Build: 16299; LoRev: 0; HiRev: MaxInt; Name: 'Version 1709'; Version: 0), + (Build: Win2016_Last_Build; LoRev: 0; HiRev: MaxInt; Name: 'Version 1803'; + Version: 0), // Windows Server 2019 - (Build: 17763; LoRev: 0; HiRev: MaxInt; Name: 'Version 1809'), - (Build: 18362; LoRev: 0; HiRev: MaxInt; Name: 'Version 1903'), - (Build: Win2019LastBuild; LoRev: 0; HiRev: MaxInt; Name: 'Version 1909'), + (Build: 17763; LoRev: 0; HiRev: MaxInt; Name: 'Version 1809'; Version: 0), + (Build: 18362; LoRev: 0; HiRev: MaxInt; Name: 'Version 1903'; Version: 0), + (Build: Win2019_Last_Build; LoRev: 0; HiRev: MaxInt; Name: 'Version 1909'; + Version: 0), // Windows Server (no year number) - (Build: 19041; LoRev: 0; HiRev: MaxInt; Name: 'Version 2004'), - (Build: WinServerLastBuild; LoRev: 0; HiRev: MaxInt; Name: 'Version 20H2'), + (Build: 19041; LoRev: 0; HiRev: MaxInt; Name: 'Version 2004'; Version: 0), + (Build: WinServer_Last_Build; LoRev: 0; HiRev: MaxInt; + Name: 'Version 20H2'; Version: 0), // Windows Server 2022 - (Build: 20348; LoRev: 0; HiRev: MaxInt; Name: 'Version 21H2') + (Build: 20348; LoRev: 0; HiRev: MaxInt; Name: 'Version 21H2'; Version: 0) ); // Windows server releases needing special handling // Server 2016 Technical Preview 1: reports version 6.4 instead of 10.0! - Win2016TP1Build = 9841; + Win2016_TP1_Build = 9841; // Server 2019 Insider Preview builds: require format strings in names - Win2019IPBuilds: array[0..9] of Integer = ( + Win2019_IP_Builds: array[0..9] of Integer = ( 17623, 17627, 17666, 17692, 17709, 17713, 17723, 17733, 17738, 17744 ); @@ -1663,6 +1887,8 @@ TBuildNameMap = record // ** At present this variable is only used for Windows 10. InternalExtraUpdateInfo: string = ''; + InternalWin1011Version: TPJWin10PlusVersion = win10plusNA; + // Flag required when opening registry with specified access flags {$IFDEF REGACCESSFLAGS} const @@ -1781,7 +2007,8 @@ function FindBuildNumberFrom(const BNs: array of Integer; var FoundBN: Integer): // parameters respectively. Otherwise False is returned, FoundBN is set to 0 and // FoundExtra is set to ''. function FindBuildNameAndExtraFrom(const Infos: array of TBuildNameMap; - var FoundBN: Integer; var FoundExtra: string): Boolean; + var FoundBN: Integer; var FoundExtra: string; var FoundVersion: Word): + Boolean; var I: Integer; begin @@ -1795,6 +2022,7 @@ function FindBuildNameAndExtraFrom(const Infos: array of TBuildNameMap; begin FoundBN := Infos[I].Build; FoundExtra := Infos[I].Name; + FoundVersion := Infos[I].Version; Result := True; Break; end; @@ -2031,6 +2259,33 @@ function GetRegistryInt(const RootKey: HKEY; const SubKey, Name: string): end; end; +function GetRegistryBytes(const RootKey: HKEY; const SubKey, Name: string): + TBytes; +var + Reg: TRegistry; // registry access object + ValueInfo: TRegDataInfo; // info about registry value +begin + SetLength(Result, 0); + // Open registry at required root key + Reg := RegCreate; + try + Reg.RootKey := RootKey; + if RegOpenKeyReadOnly(Reg, SubKey) and Reg.ValueExists(Name) then + begin + // Check if registry value is integer + Reg.GetDataInfo(Name, ValueInfo); + if ValueInfo.RegData <> rdBinary then + raise EPJSysInfo.Create(sBadRegBinType); + SetLength(Result, ValueInfo.DataSize); + Reg.ReadBinaryData(Name, Result[0], Length(Result)); + end; + finally + // Close registry + Reg.CloseKey; + Reg.Free; + end; +end; + // Gets string info for given value from Windows current version key in // registry. function GetCurrentVersionRegStr(ValName: string): string; @@ -2056,6 +2311,7 @@ procedure InitPlatformIdEx; GetVersionEx: TGetVersionEx; // pointer to GetVersionEx API function GetProductInfo: TGetProductInfo; // pointer to GetProductInfo API function SI: TSystemInfo; // structure from GetSystemInfo API call + VersionEx: Word; // gets extra version info (Win 10/11) // Get OS's revision number from registry. function GetOSRevisionNumber(const IsNT: Boolean): Integer; @@ -2120,24 +2376,24 @@ procedure InitPlatformIdEx; case InternalMinorVersion of 0: // Vista - InternalBuildNumber := WinVistaBaseBuild + Win32ServicePackMajor; + InternalBuildNumber := WinVista_Base_Build + Win32ServicePackMajor; 1: // Windows 7 - InternalBuildNumber := Win7BaseBuild + Win32ServicePackMajor; + InternalBuildNumber := Win7_Base_Build + Win32ServicePackMajor; 2: // Windows 8 (no known SPs) if Win32ServicePackMajor = 0 then - InternalBuildNumber := Win8Build; + InternalBuildNumber := Win8_Build; 3: // Windows 8.1 (no known SPs) if Win32ServicePackMajor = 0 then - InternalBuildNumber := Win8Point1Build; + InternalBuildNumber := Win8Point1_Build; 4: if (Win32ProductType = VER_NT_DOMAIN_CONTROLLER) or (Win32ProductType = VER_NT_SERVER) then begin // Windows 2016 Server tech preview 1 - InternalBuildNumber := Win2016TP1Build; + InternalBuildNumber := Win2016_TP1_Build; InternalExtraUpdateInfo := 'Technical Preview 6'; end; end; @@ -2162,15 +2418,18 @@ procedure InitPlatformIdEx; and (Win32ProductType <> VER_NT_SERVER) then begin if FindBuildNameAndExtraFrom( - Win10BuildMap, InternalBuildNumber, InternalExtraUpdateInfo + Win10_BuildMap, InternalBuildNumber, InternalExtraUpdateInfo, + VersionEx ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := + TPJWin10PlusVersion(VersionEx); end - else if IsBuildNumber(Win1021H1Build) then + else if IsBuildNumber(Win10_21H1_Build) then begin // **REF3** - InternalBuildNumber := Win1021H1Build; + InternalBuildNumber := Win10_21H1_Build; + InternalWin1011Version := win10v21H1; case InternalRevisionNumber of 985, 1023, 1052, 1055, 1081, 1082, 1083, 1110, 1151, 1165, 1202, 1237, 1266, 1288, 1320, 1348, 1387, 1415, 1466, 1469, 1503, @@ -2195,19 +2454,20 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win1021H2Build) then + else if IsBuildNumber(Win10_21H2_Build) then begin // **REF4** // From 21H2 Windows 10 moves from a 6 monthly update cycle to a // yearly cycle - InternalBuildNumber := Win1021H2Build; + InternalBuildNumber := Win10_21H2_Build; + InternalWin1011Version := win10v21H2; case InternalRevisionNumber of 1288, 1348, 1387, 1415, 1466, 1469, 1503, 1526, 1566, 1586, 1620, 1645, 1682, 1706, 1708, 1741, 1766, 1767, 1806, 1826, 1865, 1889, 1949, 2006, 2075, 2130, 2132, 2193, 2194, 2251, 2311, 2364, 2486, 2546, 2604, 2673, 2728, 2788, 2846, 2965, 3086, 3208, 3324, 3448, 3570, 3693, 3803, 3930, 4046, - 4170 .. MaxInt: + 4170, 4291, 4412, 4529, 4651, 4780, 4894 .. MaxInt: InternalExtraUpdateInfo := 'Version 21H2'; 1147, 1149, 1151, 1165, 1200, 1202, 1237, 1263, 1266, 1319, 1320, 1379, 1381, 1499, 1618, 1679, 1737, 1739, 1862, @@ -2223,22 +2483,31 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win1022H2Build) then + else if IsBuildNumber(Win10_22H2_Build) then begin // **REF5** - InternalBuildNumber := Win1022H2Build; + InternalBuildNumber := Win10_22H2_Build; + InternalWin1011Version := win10v22H2; case InternalBuildNumber of 2006, 2130, 2132, 2193, 2194, 2251, 2311, 2364, 2486, 2546, 2604, 2673, 2728, 2788, 2846, 2913, 2965, 3031, 3086, 3208, 3271, 3324, 3393, 3448, 3516, 3570, 3636, 3693, 3758, 3803, - 3930, 3996, 4046, 4123, 4170, 4239 .. MaxInt: + 3930, 3996, 4046, 4123, 4170, 4239, 4291, 4355, 4412, 4474, + 4529, 4598, 4651, 4717, 4780, 4842, 4894, 4957 .. MaxInt: InternalExtraUpdateInfo := 'Version 22H2'; 1865, 1889, 1949, 2075, 2301, 2670, 2787, 2908, 3030, 3154, - 3155, 3269, 3391, 3513, 3754, 3757, 3992, 4116, 4233, 4235: + 3155, 3269, 3391, 3513, 3754, 3757, 3992, 4116, 4233, 4235, + 4353, 4472: InternalExtraUpdateInfo := Format( 'Version 22H2 [Release Preview Channel v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); + 4593, 4713, 4955: + InternalExtraUpdateInfo := Format( + 'Version 22H2 ' + + '[Beta and Release Preview Channels v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); else InternalExtraUpdateInfo := Format( 'Version 22H2 [Unknown release v10.0.%d.%d]', @@ -2248,15 +2517,16 @@ procedure InitPlatformIdEx; end // Win 11 releases are reporting v10.0 // Details taken from: https://tinyurl.com/usupsz4a - else if IsBuildNumber(Win11DevBuild) then + else if IsBuildNumber(Win11_Dev_Build) then begin - InternalBuildNumber := Win11DevBuild; + InternalBuildNumber := Win11_Dev_Build; + InternalWin1011Version := win10plusUnknown; InternalExtraUpdateInfo := Format( 'Dev [Insider v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ) end - else if IsBuildNumber(Win11v21H2Build) then + else if IsBuildNumber(Win11_21H2_Build) then begin // **REF6** // There are several Win 11 releases with this build number @@ -2264,14 +2534,15 @@ procedure InitPlatformIdEx; // number. // *** Amazingly one of them, revision 194, is the 1st public // release of Win 11 -- well hidden eh?! - InternalBuildNumber := Win11v21H2Build; + InternalBuildNumber := Win11_21H2_Build; + InternalWin1011Version := win11v21H2; case InternalRevisionNumber of 194, 258, 282, 348, 376, 434, 438, 469, 493, 527, 556, 593, 613, 652, 675, 708, 739, 740, 778, 795, 832, 856, 918, 978, 1042, 1098, 1100, 1165, 1219, 1281, 1335, 1455, 1516, 1574, 1641, 1696, 1761, 1817, 1880, 1936, 2003, 2057, 2124, 2176, 2245, 2295, 2360, 2416, 2482, 2538, 2600, 2652, 2713, 2777, - 2836 .. MaxInt: + 2836, 2899, 2960, 3019, 3079, 3147, 3197 .. MaxInt: // Public releases of Windows 11 InternalExtraUpdateInfo := 'Version 21H2'; 51, 65, 71: @@ -2303,15 +2574,18 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11v22H2Build) then + else if IsBuildNumber(Win11_22H2_Build) then begin // **REF1** - InternalBuildNumber := Win11v22H2Build; + InternalBuildNumber := Win11_22H2_Build; + InternalWin1011Version := win11v22H2; case InternalRevisionNumber of 382, 521, 525, 608, 674, 675, 755, 819, 900, 963, 1105, 1194, 1265, 1344, 1413, 1485, 1555, 1635, 1702, 1778, 1848, 1926, 1928, 1992, 2070, 2134, 2215, 2283, 2361, 2428, 2506, 2715, - 2792, 2861, 3007, 3085, 3155, 3235, 3296, 3374 .. MaxInt: + 2792, 2861, 3007, 3085, 3155, 3235, 3296, 3374, 3447, 3527, + 3593, 3672, 3737, 3810, 3880, 3958, 4037, 4112, 4169, 4249 + .. MaxInt: begin InternalExtraUpdateInfo := 'Version 22H2'; case InternalRevisionNumber of @@ -2328,7 +2602,8 @@ procedure InitPlatformIdEx; [InternalBuildNumber, InternalRevisionNumber] ); 105, 169, 232, 317, 457, 607, 754, 898, 1192, 1343, 1483, 1631, - 1776, 2066, 2213, 2359, 2500, 2787, 3078, 3227, 3371: + 1776, 2066, 2213, 2359, 2500, 2787, 3078, 3227, 3371, 3520, + 3668, 3807, 3951, 4108, 4247: InternalExtraUpdateInfo := Format( 'Version 22H2 [Release Preview v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] @@ -2349,25 +2624,32 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11v23H2Build) then + else if IsBuildNumber(Win11_23H2_Build) then begin // **REF10** - InternalBuildNumber := Win11v23H2Build; + InternalBuildNumber := Win11_23H2_Build; + InternalWin1011Version := win11v23H2; case InternalRevisionNumber of - 2428, 2506, 2715, 2792, 2861, 3007, 3085, 3155, 3235 {Moment 5}, 3296, 3374 .. MaxInt: + 2428, 2506, 2715, 2792, 2861, 3007, 3085, 3155, 3235 {Moment 5}, + 3296, 3374, 3447, 3527, 3593, 3672, 3737, 3810, 3880, 3958, + 4037, 4112, 4169, 4249 .. MaxInt: InternalExtraUpdateInfo := 'Version 23H2'; 1825, 1830, 1835, 1900, 1906, 1972: + begin // revisions 1825..1972 had version string "22H2" + InternalWin1011Version := win11v22H2; InternalExtraUpdateInfo := Format( 'Version 22H2 [Beta v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); + end; 2048, 2050, 2115, 2129, 2191, 2199, 2262, 2265, 2271, 2338: InternalExtraUpdateInfo := Format( 'Version 23H2 [Beta v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); - 2361, 2787, 3078, 3227, 3371: + 2361, 2787, 3078, 3227, 3371, 3520, 3668, 3807, 3951, 4108, + 4247: InternalExtraUpdateInfo := Format( 'Version 23H2 [Release Preview v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] @@ -2379,77 +2661,93 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11Oct22ComponentBetaChannelBuild) then + else if IsBuildNumber(Win11_24H2_Build) then begin - // **REF2** - InternalBuildNumber := Win11Oct22ComponentBetaChannelBuild; + // **REF11** + InternalBuildNumber := Win11_24H2_Build; + InternalWin1011Version := win11v24H2; case InternalRevisionNumber of - 290, 436, 440, 450, 575, 586, 590, 598, 601: + 1742, 1882 .. MaxInt: + InternalExtraUpdateInfo := 'Version 24H2'; + 560, 712, 863, 994, 1000, 1150, 1297, 1301, 1457, 1586, 1591: InternalExtraUpdateInfo := Format( - 'Version 22H2 [October Component Update v10.0.%d.%d]', + 'Version 24H2 [Release Preview v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + 1: + InternalExtraUpdateInfo := Format( + 'Version 24H2 [Dev & Canary Channel v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + 268: + InternalExtraUpdateInfo := Format( + 'Version 24H2 [Dev Channel v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] ); else InternalExtraUpdateInfo := Format( - 'Version 22H2 [Unknown release v10.0.%d.%d]', + 'Version 24H2 [Unknown release v10.0.%d.%d]', [InternalBuildNumber, InternalRevisionNumber] ); end; end else if FindBuildNumberFrom( - Win1122H2DevChannelDevBuilds, InternalBuildNumber + Win11_24H2_DevAndCanaryChannel_Builds, InternalBuildNumber ) then begin - // Win11 Dev Channel builds with version string "22H2" + // Win11 builds in Canary, Dev & Preview channels with version + // string "24H2" + InternalWin1011Version := win10plusUnknown; InternalExtraUpdateInfo := Format( - 'Dev Channel Version 22H2 v10.0.%d.%d', + 'Dev or Canary Channel Version 24H2 v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] ); end else if FindBuildNumberFrom( - Win1124H2DevChannelDevBuilds, InternalBuildNumber + Win11_24H2_CanaryChannel_Builds, InternalBuildNumber ) then begin - // Win11 Dev Channel builds with version string "22H2" + // Win11 builds in Canary channel with version string "24H2" + InternalWin1011Version := win10plusUnknown; InternalExtraUpdateInfo := Format( - 'Dev Channel Version 24H2 v10.0.%d.%d', - [InternalBuildNumber, InternalRevisionNumber] - ); - end - else if FindBuildNumberFrom( - Win11Canary23H2PreviewBuilds, InternalBuildNumber - ) then - begin - // Win11 Canary Channel builds with version string "23H2" - InternalExtraUpdateInfo := Format( - 'Canary Channel Version 23H2 v10.0.%d.%d', + 'Canary Channel Version 24H2 v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] ); end - else if FindBuildNumberFrom( - Win11Canary24H2PreviewBuilds, InternalBuildNumber - ) then + else if IsBuildNumber(Win11_Oct22Component_BetaChannel_Build) then begin - // Win11 Canary Channel builds with version string "24H2" - InternalExtraUpdateInfo := Format( - 'Canary Channel Version 24H2 v10.0.%d.%d', - [InternalBuildNumber, InternalRevisionNumber] - ); + // **REF2** + InternalBuildNumber := Win11_Oct22Component_BetaChannel_Build; + InternalWin1011Version := win10plusUnknown; + case InternalRevisionNumber of + 290, 436, 440, 450, 575, 586, 590, 598, 601: + InternalExtraUpdateInfo := Format( + 'Version 22H2 [October Component Update v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + else + InternalExtraUpdateInfo := Format( + 'Version 22H2 [Unknown release v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + end; end else if FindBuildNumberFrom( - Win11DevBetaChannels22H2Builds, InternalBuildNumber + Win11_22H2_DevAndBetaChannel_Builds, InternalBuildNumber ) then begin // Win 11 Dev & Beta channel builds with version string "22H2" + InternalWin1011Version := win10plusUnknown; InternalExtraUpdateInfo := Format( 'Dev & Beta Channels v10.0.%d.%d (22H2)', [InternalBuildNumber, InternalRevisionNumber] ); end - else if IsBuildNumber(Win11Feb23ComponentBetaChannelBuild) then + else if IsBuildNumber(Win11_Feb23Component_BetaChannel_Build) then begin // **REF7** - InternalBuildNumber := Win11Feb23ComponentBetaChannelBuild; + InternalBuildNumber := Win11_Feb23Component_BetaChannel_Build; + InternalWin1011Version := win10plusUnknown; case InternalRevisionNumber of 730, 741, 746, 870, 875, 885, 891, 1020, 1028, 1037, 1095, 1180, 1245, 1250, 1255, 1325 .. MaxInt: @@ -2464,10 +2762,11 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11May23ComponentBetaChannelBuild) then + else if IsBuildNumber(Win11_May23Component_BetaChannel_Build) then begin // **REF8** - InternalBuildNumber := Win11May23ComponentBetaChannelBuild; + InternalBuildNumber := Win11_May23Component_BetaChannel_Build; + InternalWin1011Version := win10plusUnknown; case InternalRevisionNumber of 1391, 1465, 1470, 1537, 1546, 1610, 1616, 1680, 1690, 1755 .. MaxInt: @@ -2482,14 +2781,17 @@ procedure InitPlatformIdEx; ); end; end - else if IsBuildNumber(Win11FutureComponentBetaChannelBuild) then + else if IsBuildNumber(Win11_FutureComponent_BetaChannel_Build) then begin // **REF9** - InternalBuildNumber := Win11FutureComponentBetaChannelBuild; + InternalBuildNumber := Win11_FutureComponent_BetaChannel_Build; + InternalWin1011Version := win10plusUnknown; case InternalRevisionNumber of 2419, 2483, 2486, 2552, 2700, 2771, 2776, 2841, 2850, 2915, 2921, 3061, 3066, 3130, 3139, 3140, 3209, 3212, 3276, 3286, - 3350, 3420 .. MaxInt: + 3350, 3420, 3430, 3495, 3500, 3566, 3570, 3575, 3640, 3646, + 3720, 3785, 3790, 3858, 3930, 3936, 4000, 4005, 4010, 4076, + 4082, 4145, 4225, 4291 .. MaxInt: InternalExtraUpdateInfo := Format( 'Future Component Update Beta v10.0.%d.%d', [InternalBuildNumber, InternalRevisionNumber] @@ -2501,6 +2803,25 @@ procedure InitPlatformIdEx; ); end; end + else if IsBuildNumber(Win11_FutureComponent_DevChannel_Build) then + begin + // **REF12** + InternalBuildNumber := Win11_FutureComponent_DevChannel_Build; + InternalWin1011Version := win10plusUnknown; + case InternalRevisionNumber of + 461, 470, 670, 751, 770, 961, 1252, 1330, 1340, 1350, 1542, + 1843, 1912 .. MaxInt: + InternalExtraUpdateInfo := Format( + 'Future Component Update Dev Channel v10.0.%d.%d', + [InternalBuildNumber, InternalRevisionNumber] + ); + else + InternalExtraUpdateInfo := Format( + 'Future Component Update [Unknown Beta v10.0.%d.%d]', + [InternalBuildNumber, InternalRevisionNumber] + ); + end; + end // End with some much less likely cases // NOTE: All the following tests MUST come after the last call to // FindBuildNameAndExtraFrom() for non-server OSs because some @@ -2512,14 +2833,14 @@ procedure InitPlatformIdEx; InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v20H2; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_2004_Preview_Builds, '2004', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v2004; end else if IsBuildNumber(Win10_19XX_Shared_Build) then begin @@ -2527,57 +2848,63 @@ procedure InitPlatformIdEx; // preview of Version 1903 or 1909 InternalBuildNumber := Win10_19XX_Shared_Build; if IsInRange(InternalRevisionNumber, 0, 113) then + begin + InternalWin1011Version := win10v1903; InternalExtraUpdateInfo := Format( 'Version 1903 Preview Build %d.%d', [InternalBuildNumber, InternalRevisionNumber] ) + end else if IsInRange(InternalRevisionNumber, 10000, 10024) then + begin + InternalWin1011Version := win10v1909; InternalExtraUpdateInfo := Format( 'Version 1909 Preview Build %d.%d', [InternalBuildNumber, InternalRevisionNumber] ); + end; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1903_Preview_Builds, '1903', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1903; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1809_Preview_Builds, '1809', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1809; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1803_Preview_Builds, '1803', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1803; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1709_Preview_Builds, '1709', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1709; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1703_Preview_Builds, '1703', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1703; end else if FindWin10PreviewBuildNameAndExtraFrom( Win10_1607_Preview_Builds, '1607', InternalBuildNumber, InternalExtraUpdateInfo ) then begin - // Nothing to do: required internal variables set in function call + InternalWin1011Version := win10v1607; end end else // Win32ProductType in [VER_NT_DOMAIN_CONTROLLER, VER_NT_SERVER] @@ -2587,13 +2914,14 @@ procedure InitPlatformIdEx; if FindBuildNameAndExtraFrom( WinServerSimpleBuildMap, InternalBuildNumber, - InternalExtraUpdateInfo + InternalExtraUpdateInfo, + VersionEx // unused ) then begin // Nothing to do: required internal variables set in function call end else if FindBuildNumberFrom( - Win2019IPBuilds, InternalBuildNumber + Win2019_IP_Builds, InternalBuildNumber ) then begin // Windows 2019 Insider preview builds require build number in @@ -2687,6 +3015,13 @@ procedure InitPlatformIdEx; { TPJOSInfo } +class function TPJOSInfo.BuildBranch: string; +begin + Result := GetRegistryString( + HKEY_LOCAL_MACHINE, CurrentVersionRegKeys[IsWinNT], 'BuildBranch' + ); +end; + class function TPJOSInfo.BuildNumber: Integer; begin Result := InternalBuildNumber; @@ -2752,6 +3087,13 @@ class function TPJOSInfo.Description: string; end; end; +class function TPJOSInfo.DigitalProductID: TBytes; +begin + Result := GetRegistryBytes( + HKEY_LOCAL_MACHINE, CurrentVersionRegKeys[IsWinNT], 'DigitalProductId' + ); +end; + class function TPJOSInfo.Edition: string; begin // This method is based on sample C++ code from MSDN @@ -2766,7 +3108,11 @@ class function TPJOSInfo.Edition: string; // For v6.0 and later we ignore the suite mask and use the new // PRODUCT_ flags from the GetProductInfo() function to determine the // edition + // 1st try to find edition name from lookup table Result := EditionFromProductInfo; + if Result = '' then + // no matching entry in lookup: get from registry + Result := EditionIDFromReg; // append 64-bit if 64 bit system if InternalProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64 then Result := Result + ' (64-bit)'; @@ -2870,7 +3216,7 @@ class function TPJOSInfo.Edition: string; end else // NT before SP6: we read required info from registry - Result := EditionFromReg; + Result := NTEditionFromReg; end; end; end; @@ -2890,19 +3236,10 @@ class function TPJOSInfo.EditionFromProductInfo: string; end; end; -class function TPJOSInfo.EditionFromReg: string; -var - EditionCode: string; // OS product edition code stored in registry +class function TPJOSInfo.EditionIDFromReg: string; begin - EditionCode := ProductTypeFromReg; - if CompareText(EditionCode, 'WINNT') = 0 then - Result := 'WorkStation' - else if CompareText(EditionCode, 'LANMANNT') = 0 then - Result := 'Server' - else if CompareText(EditionCode, 'SERVERNT') = 0 then - Result := 'Advanced Server'; - Result := Result + Format( - ' %d.%d', [InternalMajorVersion, InternalMinorVersion] + Result := GetRegistryString( + HKEY_LOCAL_MACHINE, CurrentVersionRegKeys[IsWinNT], 'EditionID' ); end; @@ -3125,6 +3462,29 @@ class function TPJOSInfo.IsWin9x: Boolean; Result := Platform = ospWin9x; end; +class function TPJOSInfo.IsWindows10PlusVersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; +begin + Assert(not (AVersion in [win10plusNA, win10plusUnknown])); + Result := IsReallyWindows10OrGreater and (Windows10PlusVersion >= AVersion); +end; + +class function TPJOSInfo.IsWindows10VersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; +begin + if not (AVersion in Win10_Versions) then + raise EPJSysInfo.Create('Invalid Windows 10 version: can''t compare'); + Result := IsWindows10PlusVersionOrLater(AVersion); +end; + +class function TPJOSInfo.IsWindows11VersionOrLater( + const AVersion: TPJWin10PlusVersion): Boolean; +begin + if not (AVersion in Win11_Versions) then + raise EPJSysInfo.Create('Invalid Windows 11 version: can''t compare'); + Result := IsWindows10PlusVersionOrLater(AVersion); +end; + class function TPJOSInfo.IsWindowsServer: Boolean; var OSVI: TOSVersionInfoEx; @@ -3182,6 +3542,22 @@ class function TPJOSInfo.MinorVersion: Integer; Result := InternalMinorVersion; end; +class function TPJOSInfo.NTEditionFromReg: string; +var + EditionCode: string; // OS product edition code stored in registry +begin + EditionCode := ProductTypeFromReg; + if CompareText(EditionCode, 'WINNT') = 0 then + Result := 'WorkStation' + else if CompareText(EditionCode, 'LANMANNT') = 0 then + Result := 'Server' + else if CompareText(EditionCode, 'SERVERNT') = 0 then + Result := 'Advanced Server'; + Result := Result + Format( + ' %d.%d', [InternalMajorVersion, InternalMinorVersion] + ); +end; + class function TPJOSInfo.Platform: TPJOSPlatform; begin case InternalPlatform of @@ -3303,7 +3679,7 @@ class function TPJOSInfo.Product: TPJOSProduct; 0: if not IsServer then begin - if TestBuildNumber(VER_LESS, Win11FirstBuild) then + if TestBuildNumber(VER_LESS, Win11_First_Build) then Result := osWin10 else // ** As of 2021-10-05 Win 11 is reporting version 10.0! @@ -3311,11 +3687,17 @@ class function TPJOSInfo.Product: TPJOSProduct; end else begin - if TestBuildNumber(VER_LESS_EQUAL, Win2016LastBuild) then + if TestBuildNumber( + VER_LESS_EQUAL, Win2016_Last_Build + ) then Result := osWin10Svr - else if TestBuildNumber(VER_LESS_EQUAL, Win2019LastBuild) then + else if TestBuildNumber( + VER_LESS_EQUAL, Win2019_Last_Build + ) then Result := osWinSvr2019 - else if TestBuildNumber(VER_LESS_EQUAL, WinServerLastBuild) then + else if TestBuildNumber( + VER_LESS_EQUAL, WinServer_Last_Build + ) then Result := osWinServer else Result := osWinSvr2022; @@ -3457,6 +3839,29 @@ class function TPJOSInfo.ServicePackMinor: Integer; Result := Win32ServicePackMinor; end; +class function TPJOSInfo.Windows10PlusVersion: TPJWin10PlusVersion; +begin + Result := InternalWin1011Version; +end; + +class function TPJOSInfo.Windows10PlusVersionName: string; +const + cVersions: array[TPJWin10PlusVersion] of string = ( + // Not windows 10+ + '', + // Windows 10+ with unknown version string + 'Unknown', + // Windows 10 + '1507', '1511', '1607', '1703', '1709', + '1803', '1809', '1903', '1909', '2004', + '20H2', '21H1', '21H2', '22H2', + // Windows 11 + '21H2', '22H2', '23H2', '24H2' + ); +begin + Result := cVersions[Windows10PlusVersion]; +end; + { TPJComputerInfo } class function TPJComputerInfo.BiosVendor: string; @@ -3642,18 +4047,17 @@ class function TPJComputerInfo.MACAddress: string; if NetBiosSucceeded(Netbios(@Ncb)) then begin // we have a MAC address: return it - with Adapter.Adapt do - Result := Format( - '%.2x-%.2x-%.2x-%.2x-%.2x-%.2x', - [ - Ord(adapter_address[0]), - Ord(adapter_address[1]), - Ord(adapter_address[2]), - Ord(adapter_address[3]), - Ord(adapter_address[4]), - Ord(adapter_address[5]) - ] - ); + Result := Format( + '%.2x-%.2x-%.2x-%.2x-%.2x-%.2x', + [ + Ord(Adapter.Adapt.adapter_address[0]), + Ord(Adapter.Adapt.adapter_address[1]), + Ord(Adapter.Adapt.adapter_address[2]), + Ord(Adapter.Adapt.adapter_address[3]), + Ord(Adapter.Adapt.adapter_address[4]), + Ord(Adapter.Adapt.adapter_address[5]) + ] + ); Exit; end; end; From 82b16ca6917f6e84d4bedd879d6f1d3b31d1a90d Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:37:29 +0100 Subject: [PATCH 21/23] Bump version number to v4.24.0 build 272 --- Src/VersionInfo.vi-inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/VersionInfo.vi-inc b/Src/VersionInfo.vi-inc index c9ae9dae3..70615f76f 100644 --- a/Src/VersionInfo.vi-inc +++ b/Src/VersionInfo.vi-inc @@ -1,8 +1,8 @@ # CodeSnip Version Information Macros for Including in .vi files # Version & build numbers -version=4.23.0 -build=271 +version=4.24.0 +build=272 # String file information copyright=Copyright © P.D.Johnson, 2005-. From 7572fdad75cd510d5229ecd9c7b3971d34337e2b Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:32:52 +0100 Subject: [PATCH 22/23] Update change log with details of release v4.24.0 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aeb246be..456f8baa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ Releases are listed in reverse version number order. > Note that _CodeSnip_ v4 was developed in parallel with v3 for a while. As a consequence some v3 releases have later release dates than early v4 releases. +## Release v4.24.0 of 23 October 2024 + +* Compilers with which a snippet has not been tested are now omitted from snippet information that is copied to the clipboard and included in print outs [issue #143]. +* Reversed order of compilers in the snippets editor's _Compile Results_ tab so that later compilers are display first. This change was accidentally left out of release v4.22.0 when similar changes were made in other parts of the UI [issue #135]. +* Release version number is now displayed in the program title bar [issue #122]. +* Fixed incorrect copyright date displayed in About Box [issue #129]. +* Fixed bug when checking for correct preamble bytes (BOMs) in UTF-8 and UTF-16 format text files [issue #139]. +* Portable and Standard edition now use the same program names. Portable edition was previously declaring itself as _DelphiDabbler CodeSnip-p_ instead of _DelphiDabbler CodeSnip_ [issue #130]. +* Updated operating system detection code [issues #126 and #144]. +* Added `Deploy.bat` script to create and package both the CodeSnip standard and portable releases [issue #128]. +* Documentation changes: + * CodeSnip standard and portable releases now each have their own release read-me files instead of both releases being shipped with the same read-me [issue #127]. Updated `Build.html` and `README.md` re this change. + * Updated and corrected REML documentation and REML help topic. Those documents and others that discuss REML were also changed to link to authoritative REML definitions in the `delphidabbler/reml` repository. [issues #131, #133 & #134]. + * Updated `Build.html` with alternative, more secure, download link for `zip.exe` program that is required to package releases [issue #137]. + ## Release v4.23.0 of 02 April 2024 * Removed marketing names (e.g. "Athens" or "Rio") from Delphi compiler names to save space when the compiler names are displayed in the UI [issue #125]. From 7a6ef7fb397c186b021759e3e56cebbcba7a7aa9 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:51:15 +0100 Subject: [PATCH 23/23] Update copyright date in header comments to 2024 --- Src/UIOUtils.pas | 2 +- Src/URTFSnippetDoc.pas | 2 +- Src/USnippetDoc.pas | 2 +- Src/UTextSnippetDoc.pas | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/UIOUtils.pas b/Src/UIOUtils.pas index 09b02879d..8c6ab2154 100644 --- a/Src/UIOUtils.pas +++ b/Src/UIOUtils.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2024, Peter Johnson (gravatar.com/delphidabbler). * * Provides a container for assisting with common file operations. } diff --git a/Src/URTFSnippetDoc.pas b/Src/URTFSnippetDoc.pas index 0fe04c353..4bb6399c1 100644 --- a/Src/URTFSnippetDoc.pas +++ b/Src/URTFSnippetDoc.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2008-2023, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2008-2024, Peter Johnson (gravatar.com/delphidabbler). * * Implements a class that renders a document that describes a snippet as rich * text. diff --git a/Src/USnippetDoc.pas b/Src/USnippetDoc.pas index 35cd8e94a..e11245322 100644 --- a/Src/USnippetDoc.pas +++ b/Src/USnippetDoc.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2008-2023, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2008-2024, Peter Johnson (gravatar.com/delphidabbler). * * Implements an abstract base class that renders a text document that describes * a snippet. Should be overridden by classes that generate actual documents in diff --git a/Src/UTextSnippetDoc.pas b/Src/UTextSnippetDoc.pas index 4ea009d9d..923637950 100644 --- a/Src/UTextSnippetDoc.pas +++ b/Src/UTextSnippetDoc.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2023, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2024, Peter Johnson (gravatar.com/delphidabbler). * * Implements a class that renders a document that describes a snippet as plain * text.