VSoft Technologies Blogs

rss

VSoft Technologies Blogs - posts about our products and software development.

Introduction

Almost every modern Delphi application needs a way to store structured data: configuration settings, runtime options, or even project metadata. For decades, developers have relied on INI files for simple key/value storage, XML for deeply structured documents, or JSON as a lightweight alternative.

But there’s another option that has grown into the de facto standard in DevOps and configuration management: YAML.

This article introduces VSoft.YAML - an open-source Delphi library that makes working with YAML straightforward. We’ll cover why you might choose YAML over INI, XML, or JSON, then dive into practical Delphi code examples for parsing, editing, and writing YAML.

Why YAML?

Human-Readability

YAML is designed for humans first. Compare the following formats describing a database configuration:

YAML

database:
  host: localhost
  port: 5432
  user: admin
  password: secret

            

JSON

{
    "database": {
    "host": "localhost",
    "port": 5432,
    "user": "admin",
    "password": "secret"
    }
}

            

XML


    localhost
    5432
    admin
    secret

            

The YAML version is the least noisy, especially when configurations get larger.

Expressiveness

  • Supports mappings (key/value), sequences (lists), and scalars (strings, numbers, booleans).
  • Handles deeply nested structures naturally.
  • More flexible than INI files, which are limited to sections and key/value pairs.

Adoption

  • YAML is everywhere, especially in DevOps.

Introducing VSoft.YAML

VSoft.YAML is a pure Delphi implementation of a YAML 1.2 parser and emitter:

  • No external dependencies — just add the source to your project.
  • Round-trip capable — parse, modify, and write YAML without losing formatting.
  • Supports multiple documents in a single YAML stream.
  • Supports reading/writing JSON
  • Extensive JSONPath support

Getting Started

Installation

Clone the repo or add it as a Git submodule:

git clone https://github.com/VSoftTechnologies/VSoft.YAML.git
            

Then add the VSoft.YAML\Source folder to your Delphi project’s search path.

Parsing YAML

The most common scenario is loading YAML from a string or file.

uses
  VSoft.YAML;

var
  Doc: IYAMLDocument;
  YamlText: string;
begin
  YamlText :=
    'database:' + sLineBreak +
    ' host: localhost' + sLineBreak +
    ' port: 5432' + sLineBreak +
    ' user: admin' + sLineBreak +
    ' password: secret';

    Doc := TYAML.LoadFromString(YamlText);
    Writeln('Database host: ', Doc.Root.Values['database'].Values['host'].AsString);
    Writeln('Database port: ', Doc.Root.Values['database'].Values['port'].AsInteger);
end;
            

To load from a file, just call TYAML.LoadFromFile

Traversing Sequences

YAML supports lists (called sequences).

Example YAML

servers:
- name: web01
  ip: 10.0.0.1
- name: web02
  ip: 10.0.0.2
            

Delphi Code

var
    doc : IYAMLDocument;
    servers: IYAMLSequence;
    server : IYAMLMapping; 
    i: integer;
begin
    doc := TYAML.LoadFromFile('c:\test\config.yaml');
    servers := Doc.Root.Values['servers'].AsSequence;
    for i := 0 to servers.Count - 1 do
    begin
        server := Servers.Items[I].AsMapping;
        Writeln(Format('%s (%s)',[server.Values['name'].AsString, Server.Values['ip'].AsString]));
    end;
end;
          

Modifying YAML

Once parsed, you can update values and write them back out:

var
    root: IYAMLMapping;
begin
    root := Doc.Root.AsMapping;
    root.AddOrSetValue('environment', 'production');
    TYAML.WriteToFile(doc, 'c:\test\config.yaml')
end;
            

Practical Use Cases for Delphi Developers

  • Configuration files — ship your app with config.yaml instead of INI or XML.
  • DevOps integration — generate or consume YAML configs for devops tools.
  • Structured data — when you need nested lists or dictionaries without verbosity.
  • Collaboration — YAML files are easy for non-developers to read and edit.

Tips and Best Practices

  • Use JSON when machine-only consumption is expected.
  • Use YAML when humans will edit the config.

YAML Rules to be aware of

Even though YAML is designed to be human-friendly, it comes with its own quirks. Here are a few issues you might run into when using YAML with Delphi:

1. Indentation Rules

  • YAML is indentation-sensitive.
  • Spaces only — tabs are not allowed for indentation.
  • A single misplaced space can completely change the structure.
valid:
  key1: value
  key2: value

invalid:
  key1: value
   key1: value # wrong indentation
            

2. Duplicate Keys

Unlike JSON, YAML technically allows duplicate keys in mappings, but this can lead to unexpected behavior. YAML Parsers will typically keep only the last occurrence.

settings:
  timeout: 10
  timeout: 30 # overwrites the first one
            

VSoft.YAML has a parser option to control the handling of duplicate keys

options := TYAML.CreateParserOptions;
options.DuplicateKeyBehavior := TYAMLDuplicateKeyBehavior.dkError; // default is dkOverwrite
doc := TYAML.LoadFromFile(fileName, options);
            

Best practice: avoid duplicates in configs, and add validation in your app if needed.

3. Scalars and Quoting

YAML is permissive about quoting strings, which can sometimes surprise you:

path: C:\Temp\file.txt # might be misinterpreted due to backslashes
             

Safer to quote unusual strings:

path: "C:\Temp\file.txt"
            

4. Boolean Values

YAML recognizes a wide set of values as booleans: yes/no, true/false, on/off.

feature_enabled: yes # interpreted as boolean true
            

If you actually mean the literal string "yes", you must quote it:

feature_enabled: "yes"
                        

5. Comments

While YAML supports # comments, they are not part of the data model and usually not preserved

VSoft.YAML does attempt to preserve comments. This is non standard. Generally, comments before mappings (objects) and sequences(arrays) are preserved, comments after scalar values may be preserved.

Blank lines will not be preserved.

6. Large or Complex Documents

  • For deeply nested structures, readability can drop.
  • Split configs into multiple YAML files if possible.
  • Use ---(start) and ...(end) to separate documents in a single file if needed.

7. Type Guessing

By default, YAML interprets unquoted values:

  • 42 → integer
  • 3.14 → float
  • 3.1.4 → string
  • true → boolean

If you want to enforce string semantics, quote the value:

port: "5432"
            

8. CRLF vs LF Line Endings

VSoft.YAML is tolerant of Windows vs Unix line endings, but if your configs are shared across systems, normalize line endings in your source control to avoid “it works on my machine” issues.

Conclusion

YAML offers developers a unique combination of human readability, expressiveness, and versatility. Unlike INI files, it can represent complex nested structures; unlike XML, it remains clean and concise; and unlike JSON, it's easier for humans to read and maintain.

The full list of features support by VSoft.YAML is available here.

When deploying dotnet applications to production, the difference between a development build and a properly configured production build can be significant. MSBuild provides numerous properties that allow you to optimize your builds for performance, security, and reliability. This article explores essential MSBuild properties for production builds, with particular focus on configuration for CI servers like Continua CI.

Core Production Build Properties

Release Configuration

The foundation of any production build starts with the Release configuration:


  true
  portable
  true

Optimize enables compiler optimizations that improve runtime performance by inlining methods, removing dead code, and performing other optimizations. DebugType set to "portable" creates portable PDB files that work across platforms while maintaining debugging capabilities. DebugSymbols should remain true even in production to enable proper stack traces and debugging when issues occur.

Deterministic Builds

Deterministic builds ensure that identical source code produces identical binaries, which is crucial for security auditing and reproducible deployments:


  true
  path1=sourcePath1,path2=sourcePath2

PathMap removes replaces the actual paths in your projects, so instead of having "c:\CI_AWS\WS\123\srs\myclass.cs" baked into your executable, you get something like ".\src\myclass.cs" .

The syntax is straightforward - path1 is the real path on your build machine, and sourcePath1 is what you want it to look like in the output. If you need to map multiple paths, just separate them with commas.

CI-Specific Properties

ContinuousIntegrationBuild

One of the most important properties for CI environments is ContinuousIntegrationBuild. According to the Microsoft documentation, this property enables several CI-specific optimizations:

  • Suppresses certain warnings that are only relevant during development
  • Enables deterministic builds by default
  • Optimizes source link generation for better debugging experience
  • Improves build reproducibility

For Continua CI, you can configure this property using the built-in ContinuaCI.Version environment variable:


  true

Alternatively, you can set it in your CI build configuration by adding ContinuousIntegrationBuild=true to the MSBuild Action Properties list.

Build Metadata

Including build metadata helps with traceability and debugging:


  $(ContinuaCI.Build.LatestChangeset.Revision)
  $(ContinuaCI.Build.Version)+$(SourceRevisionId)

This embeds the source control revision ID into the assembly, making it easier to identify which exact code version is running in production. Most CI servers provide information like revision, version etc using environment variables.

Security and Code Quality Properties

Treat Warnings as Errors

In production builds, warnings often indicate potential issues that should be addressed:


  true
  CS1591

TreatWarningsAsErrors ensures your production code has no compiler warnings. WarningsNotAsErrors allows you to exclude specific warnings (like CS1591 for missing XML documentation) that shouldn't break the build.

Code Analysis

Enable static code analysis for production builds:


  true
  latest
  true

Nullable Reference Types

For C# 8.0+ projects, enable nullable reference types to catch potential null reference exceptions:


  enable
  true

This option can be quite painful to enable on an existing code base - be prepared for a lot of clean up work!

Performance Optimization Properties

Ahead-of-Time Compilation

For applications where startup performance is critical, consider enabling ReadyToRun:


  true

Note that ReadyToRun is platform specific - you must specify a Runtime Identifier (e.g., win-x64, linux-arm64) when publishing with PublishReadyToRun=true.

Trimming for Self-Contained Deployments

For self-contained deployments, enable IL trimming to reduce application size:


  true
  partial

Be cautious with trimming as it can break applications that rely heavily on reflection.

Assembly and Versioning Properties

Strong Naming

For libraries that will be consumed by other applications:


  true
  key.snk

Version Information

Properly configure version information for easier identification:


  1.0.0.0
  $(BuildNumber)
  $(ProductVersion)

Output and Packaging Properties

Symbol Package Generation

For NuGet packages, include symbol packages for debugging:


  true
  snupkg

Enable Source Link for better debugging experience:


  true
  true

Platform-Specific Optimizations

Reference the Microsoft documentation on code generation options for platform-specific optimizations:


  x64
  false

Conclusion

Properly configuring MSBuild properties for production builds is essential for creating robust, performant, and maintainable applications. The ContinuousIntegrationBuild property provides an excellent foundation for CI-optimized builds. Remember to test these configurations thoroughly in your CI environment before deploying to production, as some optimizations may have unexpected effects on applications that rely heavily on reflection or dynamic code generation.

We recently had a report from a customer that code-signing using Signotaur was taking a long time — in this case, around a minute to sign one file. This is obviously far too slow for practical use. 

The customer provided us with logs from two machines, which showed different results. When comparing the logs, the only thing that stood out was the Certificate Chain elements — the bad log only showed one element, whilst the good log showed three. 

These chain elements make up the certificate path — each certificate is signed by another, and up the path we go until there are no more.

Certificate Path

Seeing only one certificate in the path is a red flag. You would not typically see a code-signing certificate that is signed by a root certificate; there would be one or more intermediate certificates in the path.

Installing the Certificate Authority's intermediate certificates solved that part — the chain was complete. It did not solve the timing issue.

So we added more debug logging, and after much head scratching, we realised the issue was that the delay was due to the fact that, by default, when building the certificate chain, the .NET X509Chain class performs online checks of the Certificate Revocation Lists (CRLs).

Each certificate includes a CRL Distribution Points field that points to the CRLs. These CRLs are used to check if the certificate has been revoked.

CRL Distribution Point

Performing Online CRL checks (the default) can run into problems. In our customer's case, they were being blocked by their firewall — so each HTTP request timed out, resulting in signing taking longer than expected. Note it didn't fail the signing, since the CRLs were not retrieved. After the customer allowed those URLs in their firewall configuration, code signing was fast again.

If your internet connection is slow or has high latency to the CRL hosts, that will also impact code signing time.

The X509Chain class has two alternatives to online checks: Offline and NoCheck.

- Offline will use cached CRLs if available, and will not attempt to retrieve the CRLs online.
- NoCheck does what it says — skips the CRL/OCSP checks — and really should only be used in an emergency.

Signotaur v1.0.0.444 adds a new -rm option, which allows the values: Online (default), Offline, and NoCheck.

It's no secret that we are always working on the next version of FinalBuilder and Automise - that's the nature of software development. That said, the next major versions are taking much longer than we would like - the reason for this is dealing with serious technical debt (active scripting).

With this extended development cycle, there are changes that we finished some time ago that we decided just could not wait for the next major version. We backported those changes to the FinalBuilder 8.x and Automise 5.x - but since some of these changes break project compatibility (with the x.0 releases) we are releasing FinalBuilder 8.5 and Automise 5.5

Encryption Algorithm

FinalBuilder & Automise have always used the Blowfish algorithm (with a 160 bit key) for encrypting passwords, apikeys etc.

FinalBuilder 8.5/Automise 5.5 adds the AES 256 Algorithm (with a 256 bit key) as an option.

There is a new IDE option to specify the default project encryption for new projects - this defaults to AES256. Note this only affects New projects. To change the algorithm used for existing projects, open the project info window from the Project Menu, change the setting and click ok and then save the project.

Note that these options will be removed in FinalBuilder 9/Automise 6, when AES 256 will become the default (and projects still using Blowfish will be upgraded automatically).

It's should be noted that Blowfish is still considered reasonably secure, this change is to enabled the continued use of FinalBuilder in organisations must comply with NIST (US) or ASD (Australia) requirements.

Password Variables

In FinalBuilder & Automise we have always attempted to mask passwords from the logs, but occasionally that can slip through.

Any passwords stored in variables were visible in plain text in project project files. For this reason, we have added a new Password variable type. These variables will be encrypted in project files. The backing type is string.

Password UI

All password fields on all now utilise a new password edit control that allows peeking at the value (like the windows 11 password box).

Windows Credential Manager Actions

The Windows Credential Manager actions enable you to read/write/delete credentials stored in the credential manager.

Project file compatibility Warning

FinalBuilder 8.5 project files are NOT downward compatible with FinalBuilder 8.0 - or put another way, FinalBuilder 8.0 cannot load FinalBuilder 8.5 projects. The same applies to Automise 5.5 projects. This is due to some of the changes outlined above.

Inno Setup has long supported code signing (since v5.2.4). Fortunately, the way the authors of Inno Setup implemented this feature makes it really easy to use custom tools to do the code signing. In this post we'll take a look at how to use Signotaur with Inno Setup.

There are a few different ways to specify which command to run during signing with Inno Setup.

Sign Tool settings in Inno Setup IDE

The first is to define "Sign Tool" commands in the Inno Setup IDE. You can create multiple commands - for example if you have multiple certificates  - and then in your scripts you can point to the "Sign Tool" you want your setup script to use.

To define a "Sign Tool" command in the IDE - tools menu, Configure Sign Tools...

Add sign tool in Inno Setup

The name can be anything you want, however if you are including other third party inno scripts you should make sure the name is unique to make sure those scripts cannot redefine the command (this is pointed out in the docs)

Imagine if a third party script did this

[Setup]
SignTool=Default cmd /c format.com z: $f

If a Sign Tool named Default is defined in the inno IDE, that would be used. Of course you should always review any third party code before using it!

After providing a name,  you are prompted for a command. This is where we can provide our Signotaur command line.

Inno Setup will replace a few placeholders

$f, replaced by the quoted file name of the file to be signed. (required)

$p, replaced by the Sign Tool parameters (more on this later).

$q, replaced by a quote, useful for defining a Sign Tool which contains quotes from the command line.

$$, replaced by a single $ character.

So with that, lets build our command line (adjust to suite your scenario).

e:\SignotaurClient\SignotaurTool.exe sign --sign-server https://mysignotaurhost:91/ --api-key *** --thumbprint YOURCERT-THUMBPRINT --fd "SHA256" --description "My Application" --tr http://timestamp.sectigo.com --td "SHA256" --allow-untrusted $f

Signotaur command line

Note - in Inno Setup 6.3.3 and earlier, the Sign Tool command line limit is 256 chars, which is a problem when using signotaur - this was fixed in 6.4.0. The work around for 6.3.3 or earlier is to modify the registry - under HKEY_CURRENT_USER\Software\Jordan Russell\Inno Setup\SignTools find the value for the Sign Tool you added and enter the full command line there.

Make sure to restart the Inno Setup IDE after making the registry change.

Now we can define which Sign Tool to use in our Innosetup project.

[Setup]
SignTool=signotaur

That's all we need for now - you should be able to see Signotaur being invoked to sign the uninstaller and the installer.

One downside to this configuration is that there is no way to parameterize the Sign Tool command, so no way to avoid hard coding the ApiKey in the command (which is stored insecurely in the registry). For that reason alone, I do not recommend configuring the Sign Tool this way.

Sign Tool settings in the setup project

So lets look at the second option, which is to fully define the SignTool command in the [Setup] section. IMPORTANT : Firstly, to keep the IDE happy, define your signtool as above, but with a command of

$p

then in your innotsetup project :

[Setup]
SignTool=signotaur e:\SignotaurClient\SignotaurTool.exe sign --sign-server https://mysignotaurhost:91/ --api-key **** --thumbprint YOURCERT-THUMBPRINT --fd "SHA256" --description "My Application" --tr http://timestamp.sectigo.com --td "SHA256" --allow-untrusted $f

Note that Signtool name in the project must be one that is defined in the IDE

That works fine, but of course now instead of hard coding the ApiKey in the registry, we have it in our project file - which is potentially worse since it's likely checked into version control (perhaps even in a public repo on GitHub!).

Sign Tool settings on the command line

To get around this, we need to change how we compile our Inno Setup projects. Using the Inno command line compiler lets us get around all of the issues.

To do this, remove the Sign Tool configurations from the IDE - we won't be using them.

In our project, we can use the pre-processor to only run the SignTool when Release is defined.

[Setup]
#ifdef Release
SignTool=signotaur SignotaurTool.exe sign --sign-server {#signotaurServer} --api-key {#apiKey} --thumbprint {#thumbprint} --fd "SHA256" --description {#MyAppName} --tr {#timeStampServer} --td "SHA256" -v --allow-untrusted $f
#endif

On the command line we can can provide values for the pre-processor defines

/DRelease /DapiKey=abcdef ...

That now moves the ApiKey to your build script.

There is one last option we can explore here - that is to provide the entire Sign Tool command on the command line for the iscc compiler

 
/Sname=command

Sets a SignTool with the specified name and command

e.g.

/Ssignotaur e:\SignotaurClient\SignotaurTool.exe sign --sign-server https://mysignotaurhost:91/ --api-key **** --thumbprint YOURCERT-THUMBPRINT --fd "SHA256" --description "My Application" --tr http://timestamp.sectigo.com --td "SHA256" --allow-untrusted $f

This is what the FinalBuilder InnoSetup action uses when you use the SignTool property. Of course you can use FinalBuilder variables for the ApiKey and other parts that might change. If you are calling FinalBuilder from a CI server, those variables can be provided from the CI tool - so the secret is stored and secured in one place.

One last comment before I wrap this up - why would I need Innosetup to handle Code Signing, can't I just sign the resulting setup.exe myself? The main reason for letting Innosetup handle code signing the installer is that it also signs the uninstaller.

Over the last few years, code signing has changed somewhat. With the requirement that private keys be secured, many developers have run into the issues that USB tokens present, or the limitations and costs associated with cloud-based signing solutions. Gone are the days of sharing a PFX file around the dev team or with the CI server (unless you managed to snag a 3-year renewal just before the new requirements were enforced).

Signotaur

Signotaur is a self-hosted code signing server that makes sharing certificates simple, all whilst maintaining the security of your private keys. Signing can be done (using the client) from any machine that has network access to the server.

Secure Code Signing

Private keys never leave the server, or the USB token or HSM for that matter. The client/server both support TLS (and can generate a self-signed certificate during the install), and administrators can configure access controls to limit who can use certificates for signing. Signing uses API keys rather than passwords, so no more dreaded SafeNet or YubiKey password prompts!

Supported Certificates

We have tested with PFX files, SafeNet, Certum and YubiKey USB tokens, and Windows certificate stores. Signotaur may work with other USB tokens or HSMs that have 64-bit PKCS#11 drivers.

Lightweight

Signotaur Server uses very little memory, CPU, or disk space. It uses SQLite for its database. Installing Signotaur takes a few minutes at most.

Signotaur Client is a single native Windows executable (around 15MB). It's installed with the server and can be downloaded from the server's home page. The command-line interface is very similar to SignTool.

How does it work

In simple terms, the client calculates a digest of the files you want to sign, sends that to the server, which then uses the private key to create the signature and sends that back to the client. The client then writes the signatures to the files.

Supported Platforms

For this initial release, Signotaur (client and server) runs on 64-bit Windows 10+, Windows Server 2016, or later. Linux support for the server is in development.

Affordable

Unlike cloud-based services, we don't charge per signing, and the price isn't "available on application" like some "enterprise" products. The introductory price is USD $199 per server, and with the Black Friday Sale extended to midnight 8th December, that makes it USD $119.40 (discount applied at checkout). The price includes 12 months of updates and support. Renewals after 12 months are 30% of the new purchase price.

Download it here. After installation, login and browse to the admin\licenses page and request a 14 day trial license key.

Black Friday Sale - 40% off all new licenses until midnight (utc) 4th December 2024. Extended to midnight (utc) 8th December 2024.

No coupon code required, the store will apply the discount automatically.

The problem

Windows 11 24H2 breaks scripting in FinalBuilder and Automise. You will see a range of different errors depending on your scripts or the actions you use (some actions use jscript).

The cause

Windows 24h2 enables a policy by default that causes JScript.dll (the com dll) to load JScript9Legacy.dll rather than JScript9.dll

JScript9Legacy.dll is a replacement engine using Chakra - which is an odd choice since it seems abandoned since Edge moved to using chromium. The reason they did this was because of a security issue - which is understandable - but unfortnately it introduces a whole host of bugs they do not seem to interested in fixing (I guess it works for them). 

This issue even affects some of Microsoft's own applications (like Visual Studio)

The work around

The workaround is to disable the policy

Run regedit.

navigate to (for all users) :

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Internet Explorer\Main

or (for the current user only)

HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\Internet Explorer\Main

Note these keys did not exist on my machine, so I added them.

Right click the Main key and select New DWORD (32-bit) Value, name the new value JScriptReplacement and the value to 0.

Restart FinalBuilder (no need to reboot).

Obviously, this is not ideal - we have been looking to replace JScript for some time - unfortunately so far our efforts have not resulted in something that is 100% backwards compatible - so we still have some work to do in this area.

Throughout the lifespan of FinalBuilder and Automise, we have worked very hard to avoid breaking changes - however sometimes they are unavoidable.

Today's updates to FinalBuilder 8 and Automise 5 have a breaking change in the SSH Batch Execute action. Previously, this action would manage it's own connect/disconnect - the breaking change is this action now requires separate SSH Connect/Disconnect actions. 

The reason for this is complicated, but it was brought about by us changing the client library we use for the SSH actions. The previous client library had too many issues that we were unable to work around. The most annoying example - the actions would not work correctly/reliably with openssh running on windows servers. We did try to fix this issue, but in the end the only viable option was to replace the library (something we were planning to do in the future anyway).  The new library (Rebex) is much more stable and performant. We plan to re-implement the SFTP actions (which have issues with some servers) with this library in a future update.

We have been using a build with these changes in production for some time now to dogfood these changes. 

To use the  SSH Batch Execute action, add an SSH Connect action before it and an SSH Disconnect after, set the connection name on the SSH Batch Execute and SSH Disconnect to the name of the new SSH Connect action's connection name and you should be all set.

If you experience any issues with the SSH actions in these new updates let us know (with as much info as you can about the server and action settings). 

50% OFF. No, that’s not a typo! Our first ever Black Friday sale - 50% off all new licenses - valid to midnight Tues 28th Nov (UTC).

No coupon code required, the store will apply the discount automatically.