diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 3877548419e..3d4690384c3 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -106,6 +106,22 @@ jobs:
run: |
python ./ci_tools/create_signing_metadata.py "${{ env.DMDF }}" "${{ env.AZURE_ENDPOINT }}" "${{ env.AZURE_CODE_SIGNING_NAME }}" "${{ env.AZURE_CERT_PROFILE_NAME }}"
+ - name: Install MsiVal2 and Orca tools
+ if: success() && matrix.type == 'msbuild' && !startsWith(github.ref, 'refs/tags/')
+ shell: powershell
+ run: |
+ md -Force ./temp
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\MsiVal2-x86_en-us.msi' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\8e4755178e6b5bcba8d3538c3630b7a5.cab' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\b80b7321357f7c9f281536f9440dfe68.cab' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\c2aabf6ea5c1d348ec22f3aeb92f8656.cab' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\Orca-x86_en-us.msi' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\838060235bcd28bf40ef7532c50ee032.cab' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\a35cd6c9233b6ba3da66eecaa9190436.cab' ./temp/
+ cp 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\fe38b2fd0d440e3c6740b626f51a22fc.cab' ./temp/
+ Start-Process "msiexec.exe" -ArgumentList "/i temp\MsiVal2-x86_en-us.msi" -Wait
+ Start-Process "msiexec.exe" -ArgumentList "/i temp\Orca-x86_en-us.msi" -Wait
+
- name: Build
if: success() && matrix.type == 'msbuild'
run: msbuild win_build\boinc.sln -p:Configuration=${{matrix.configuration}} -p:Platform=${{matrix.platform}} -p:VcpkgTripletConfig=ci -p:DLIB=${{ env.DLIB }} -p:DMDF=${{ env.DMDF }} -m
@@ -119,6 +135,10 @@ jobs:
working-directory: win_build\Build\${{matrix.platform}}\${{matrix.configuration}}
run: ${{github.workspace}}\temp\OpenCppCoverage\OpenCppCoverage-x64\OpenCppCoverage.exe --cover_children --optimized_build --sources ${{github.workspace}} --export_type=cobertura:cobertura.xml -- unittests.exe --gtest_output=xml:gtest.xml
+ - name: Verify MSI file
+ if: success() && matrix.type == 'msbuild' && !startsWith(github.ref, 'refs/tags/')
+ run: python ./tests/msi_validation.py "C:\Program Files (x86)\MsiVal2\MsiVal2.exe" "${{github.workspace}}\win_build\Build\${{matrix.platform}}\${{matrix.configuration}}\boinc.msi" "${{github.workspace}}\installer\darice.cub"
+
- name: Run installation
if: success() && matrix.platform == 'x64' && matrix.type == 'msbuild'
shell: powershell
diff --git a/ci_tools/source_code_check.py b/ci_tools/source_code_check.py
index 0bd2ca88f5d..a75ec4f51de 100644
--- a/ci_tools/source_code_check.py
+++ b/ci_tools/source_code_check.py
@@ -97,6 +97,7 @@ def check(directory, bytes_to_check, exclude_dirs, exclude_extensions, exclude_f
exclude_extensions = [
".a",
".bmp",
+ ".cub",
".dll",
".exe",
".gif",
diff --git a/ci_tools/trailing_whitespaces_check.py b/ci_tools/trailing_whitespaces_check.py
index 88c668618dc..60dd58b6ccc 100644
--- a/ci_tools/trailing_whitespaces_check.py
+++ b/ci_tools/trailing_whitespaces_check.py
@@ -158,6 +158,7 @@ def check(directory, exclude_dirs, exclude_extensions, exclude_files, fix_errors
exclude_extensions = [
".a",
".bmp",
+ ".cub",
".dll",
".exe",
".gif",
diff --git a/clientsetup/win/boinccas.cpp b/clientsetup/win/boinccas.cpp
index efda5e0a943..d6044582ea6 100644
--- a/clientsetup/win/boinccas.cpp
+++ b/clientsetup/win/boinccas.cpp
@@ -1,6 +1,6 @@
// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
-// Copyright (C) 2005 University of California
+// Copyright (C) 2025 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
@@ -841,6 +841,11 @@ UINT BOINCCABase::GetComponentKeyFilename(
// Save the string
strComponentKeyFilename = szBuffer;
+ // strComponentKeyFilename format is [strComponentName]_[filename]
+ // Remove the component name from the string
+ strComponentKeyFilename =
+ strComponentKeyFilename.substr(strComponentName.size() + 1);
+
strMessage = _T("The key filename for component '");
strMessage += strComponentName;
strMessage += _T("' is '");
diff --git a/installer/ActionText.cpp b/installer/ActionText.cpp
index 1f87905dc64..03727a4744b 100644
--- a/installer/ActionText.cpp
+++ b/installer/ActionText.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -15,8 +15,6 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see .
-#include
-
#include "ActionText.h"
#include "MsiHelper.h"
#include "JsonHelper.h"
diff --git a/installer/ActionTextTable.cpp b/installer/ActionTextTable.cpp
index 49585a30b14..1a6ed4a04ad 100644
--- a/installer/ActionTextTable.cpp
+++ b/installer/ActionTextTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -20,11 +20,56 @@
#include "ActionTextTable.h"
ActionTextTable::ActionTextTable(const nlohmann::json& json,
- InstallerStrings& installerStrings) {
+ InstallerStrings& installerStrings,
+ std::shared_ptr validationTable) {
std::cout << "Loading ActionTextTable..." << std::endl;
for (const auto& item : json) {
values.emplace_back(item, installerStrings);
}
+ const auto tableName = std::string("ActionText");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/actiontext-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Action",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("Name of the action.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Description",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryText,
+ "",
+ DescriptionWithUrl("Localized description that is displayed in "
+ "the progress dialog box, or written to the log when the "
+ "action is executing.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Template",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryTemplate,
+ "",
+ DescriptionWithUrl("A localized format template that is used to "
+ "format action data records to display during action "
+ "execution.", url)
+ ));
+ }
}
bool ActionTextTable::generate(MSIHANDLE hDatabase) {
diff --git a/installer/ActionTextTable.h b/installer/ActionTextTable.h
index 10e0ee89e1d..60a21c2cd5d 100644
--- a/installer/ActionTextTable.h
+++ b/installer/ActionTextTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -22,11 +22,13 @@
#include "ActionText.h"
#include "InstallerStrings.h"
#include "Generator.h"
+#include "ValidationTable.h"
class ActionTextTable : public Generator{
public:
explicit ActionTextTable(const nlohmann::json& json,
- InstallerStrings& installerStrings);
+ InstallerStrings& installerStrings,
+ std::shared_ptr validationTable);
~ActionTextTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/AdminExecuteSequenceTable.cpp b/installer/AdminExecuteSequenceTable.cpp
index 0d0c9d910f4..d9a0a281242 100644
--- a/installer/AdminExecuteSequenceTable.cpp
+++ b/installer/AdminExecuteSequenceTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -18,12 +18,55 @@
#include "AdminExecuteSequenceTable.h"
AdminExecuteSequenceTable::AdminExecuteSequenceTable(
- const nlohmann::json& json) {
+ const nlohmann::json& json,
+ std::shared_ptr validationTable) {
std::cout << "Loading AdminExecuteSequenceTable..." << std::endl;
for (const auto& value : json) {
actions.emplace_back(value);
}
+
+ const auto tableName = std::string("AdminExecuteSequence");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/adminexecutesequence-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Action",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("Name of the action to execute.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("Logical expression.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Sequence",
+ true,
+ -4,
+ 32767,
+ "",
+ MSI_NULL_INTEGER,
+ "",
+ "",
+ DescriptionWithUrl("A positive value indicates the "
+ "sequence position of the action.", url)
+ ));
+ }
}
bool AdminExecuteSequenceTable::generate(MSIHANDLE hDatabase) {
diff --git a/installer/AdminExecuteSequenceTable.h b/installer/AdminExecuteSequenceTable.h
index f9c064c3e8a..38f76594bea 100644
--- a/installer/AdminExecuteSequenceTable.h
+++ b/installer/AdminExecuteSequenceTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -22,10 +22,12 @@
#include "Action.h"
#include "Generator.h"
+#include "ValidationTable.h"
class AdminExecuteSequenceTable : public Generator{
public:
- explicit AdminExecuteSequenceTable(const nlohmann::json& json);
+ explicit AdminExecuteSequenceTable(const nlohmann::json& json,
+ std::shared_ptr validationTable);
~AdminExecuteSequenceTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/AdminUISequenceTable.cpp b/installer/AdminUISequenceTable.cpp
index 0037708c878..1e4df8e8689 100644
--- a/installer/AdminUISequenceTable.cpp
+++ b/installer/AdminUISequenceTable.cpp
@@ -17,11 +17,56 @@
#include "AdminUISequenceTable.h"
-AdminUISequenceTable::AdminUISequenceTable(const nlohmann::json& json) {
+AdminUISequenceTable::AdminUISequenceTable(const nlohmann::json& json,
+ std::shared_ptr validationTable) {
std::cout << "Loading AdminUISequenceTable..." << std::endl;
for (const auto& value : json) {
actions.emplace_back(value);
}
+
+ const auto tableName = std::string("AdminUISequence");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/adminuisequence-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Action",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("Name of the action to execute.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("Logical expression.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Sequence",
+ true,
+ -4,
+ 32767,
+ "",
+ MSI_NULL_INTEGER,
+ "",
+ "",
+ DescriptionWithUrl("A positive value indicates the sequence "
+ "position of the action. The negative values indicate that "
+ "the action is called if the installer returns the "
+ "termination flag.", url)
+ ));
+ }
}
bool AdminUISequenceTable::generate(MSIHANDLE hDatabase) {
diff --git a/installer/AdminUISequencetable.h b/installer/AdminUISequencetable.h
index 3c99c28c307..a8ed418c1b7 100644
--- a/installer/AdminUISequencetable.h
+++ b/installer/AdminUISequencetable.h
@@ -19,10 +19,12 @@
#include "Action.h"
#include "Generator.h"
+#include "ValidationTable.h"
class AdminUISequenceTable : public Generator {
public:
- explicit AdminUISequenceTable(const nlohmann::json& json);
+ explicit AdminUISequenceTable(const nlohmann::json& json,
+ std::shared_ptr validationTable);
~AdminUISequenceTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/AdvtExecuteSequenceTable.cpp b/installer/AdvtExecuteSequenceTable.cpp
index 113915a922d..fae22373fac 100644
--- a/installer/AdvtExecuteSequenceTable.cpp
+++ b/installer/AdvtExecuteSequenceTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -18,12 +18,58 @@
#include "AdvtExecuteSequenceTable.h"
AdvtExecuteSequenceTable::AdvtExecuteSequenceTable(
- const nlohmann::json& json) {
+ const nlohmann::json& json,
+ std::shared_ptr validationTable) {
std::cout << "Loading AdvtExecuteSequenceTable..." << std::endl;
for (const auto& value : json) {
actions.emplace_back(value);
}
+
+ const auto tableName = std::string("AdvtExecuteSequence");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/advtexecutesequence-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Action",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("Name of the standard action the installer is "
+ "to execute.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("Logical expression.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Sequence",
+ true,
+ -4,
+ 32767,
+ "",
+ MSI_NULL_INTEGER,
+ "",
+ "",
+ DescriptionWithUrl("A positive value indicates the sequence "
+ "position of the action. The negative values indicate that "
+ "the action is called if the installer returns the "
+ "termination flag.", url)
+ ));
+ }
}
bool AdvtExecuteSequenceTable::generate(MSIHANDLE hDatabase) {
diff --git a/installer/AdvtExecuteSequenceTable.h b/installer/AdvtExecuteSequenceTable.h
index 8b711bb1d13..4f7a6593861 100644
--- a/installer/AdvtExecuteSequenceTable.h
+++ b/installer/AdvtExecuteSequenceTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -19,10 +19,12 @@
#include "Action.h"
#include "Generator.h"
+#include "ValidationTable.h"
class AdvtExecuteSequenceTable : public Generator {
public:
- explicit AdvtExecuteSequenceTable(const nlohmann::json& json);
+ explicit AdvtExecuteSequenceTable(const nlohmann::json& json,
+ std::shared_ptr validationTable);
~AdvtExecuteSequenceTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/BinaryTable.cpp b/installer/BinaryTable.cpp
index cac5ee517cf..6bda524ac7d 100644
--- a/installer/BinaryTable.cpp
+++ b/installer/BinaryTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -19,19 +19,50 @@
BinaryTable::BinaryTable(const nlohmann::json& json,
const std::filesystem::path& path, const std::string& platform,
- const std::string& configuration) {
+ const std::string& configuration,
+ std::shared_ptr validationTable) {
std::cout << "Loading BinaryTable..." << std::endl;
for (const auto& element : json) {
binaries.emplace_back(element, path, platform, configuration);
}
+
+ const auto tableName = std::string("Binary");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/binary-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Name",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("A unique key that identifies the particular "
+ "binary data.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Data",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryBinary,
+ "",
+ DescriptionWithUrl("The unformatted binary data.", url)
+ ));
+ }
}
bool BinaryTable::generate(MSIHANDLE hDatabase) {
std::cout << "Generating BinaryTable..." << std::endl;
const auto sql_create = "CREATE TABLE `Binary` (`Name` CHAR(72) NOT NULL, "
- "`Data` OBJECT PRIMARY KEY `Name`)";
+ "`Data` OBJECT NOT NULL PRIMARY KEY `Name`)";
const auto sql_insert = "INSERT INTO `Binary` (`Name`, `Data`) "
"VALUES (?, ?)";
diff --git a/installer/BinaryTable.h b/installer/BinaryTable.h
index 3b4611bc0d9..145c3e0180d 100644
--- a/installer/BinaryTable.h
+++ b/installer/BinaryTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -21,12 +21,14 @@
#include "Generator.h"
#include "Binary.h"
+#include "ValidationTable.h"
class BinaryTable : public Generator {
public:
explicit BinaryTable(const nlohmann::json& json,
const std::filesystem::path& path, const std::string& platform,
- const std::string& configuration);
+ const std::string& configuration,
+ std::shared_ptr validationTable);
~BinaryTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/CheckboxTable.cpp b/installer/CheckboxTable.cpp
index 24ccd3142d6..7e9a0b02654 100644
--- a/installer/CheckboxTable.cpp
+++ b/installer/CheckboxTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -17,20 +17,52 @@
#include "CheckboxTable.h"
-CheckboxTable::CheckboxTable(const nlohmann::json& json) {
+CheckboxTable::CheckboxTable(const nlohmann::json& json,
+ std::shared_ptr validationTable) {
std::cout << "Loading CheckboxTable..." << std::endl;
for (const auto& item : json) {
properties.emplace_back(item);
}
+
+ const auto tableName = std::string("CheckBox");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/checkbox-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Property",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("A named property to be tied to this item.",
+ url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Value",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryFormatted,
+ "",
+ DescriptionWithUrl("The value string associated with this item.",
+ url)
+ ));
+ }
}
bool CheckboxTable::generate(MSIHANDLE hDatabase) {
std::cout << "Generating CheckboxTable..." << std::endl;
- const auto sql_create = "CREATE TABLE `Checkbox` "
+ const auto sql_create = "CREATE TABLE `CheckBox` "
"(`Property` CHAR(72) NOT NULL, `Value` CHAR(64) "
"PRIMARY KEY `Property`)";
- const auto sql_insert = "INSERT INTO `Checkbox` "
+ const auto sql_insert = "INSERT INTO `CheckBox` "
"(`Property`, `Value`) VALUES (?, ?)";
return Generator::generate(hDatabase, sql_create, sql_insert, properties);
diff --git a/installer/CheckboxTable.h b/installer/CheckboxTable.h
index b08ef2bdaf0..ef09e81d4e3 100644
--- a/installer/CheckboxTable.h
+++ b/installer/CheckboxTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -19,10 +19,12 @@
#include "Generator.h"
#include "Checkbox.h"
+#include "ValidationTable.h"
class CheckboxTable : public Generator {
public:
- explicit CheckboxTable(const nlohmann::json& json);
+ explicit CheckboxTable(const nlohmann::json& json,
+ std::shared_ptr validationTable);
~CheckboxTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/Component.cpp b/installer/Component.cpp
index 2649db12017..aa77c49a09c 100644
--- a/installer/Component.cpp
+++ b/installer/Component.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -33,7 +33,7 @@ Component::Component(const nlohmann::json& json, const std::string& directory,
component = parent + "_" + directory;
}
JsonHelper::handle(json, "Files", [&](const auto& file) {
- files.emplace_back(file, component);
+ files.emplace_back(file, component, directory);
});
JsonHelper::handle(json, "Registry", [&](const auto& registry) {
registries.emplace_back(registry, component);
@@ -99,3 +99,11 @@ std::vector Component::getServiceInstalls() const {
std::vector Component::getShortcuts() const {
return shortcuts;
}
+
+std::string Component::getComponentName() const {
+ return component;
+}
+
+std::string Component::getDirectory() const {
+ return directory;
+}
diff --git a/installer/Component.h b/installer/Component.h
index a6da3cb8780..0c4c4a8dc05 100644
--- a/installer/Component.h
+++ b/installer/Component.h
@@ -45,6 +45,8 @@ class Component : public Record {
std::vector getServiceControls() const;
std::vector getServiceInstalls() const;
std::vector getShortcuts() const;
+ std::string getComponentName() const;
+ std::string getDirectory() const;
private:
std::string component{};
std::string componentId{};
diff --git a/installer/ComponentTable.cpp b/installer/ComponentTable.cpp
index f486ab4ade8..cab93e0d933 100644
--- a/installer/ComponentTable.cpp
+++ b/installer/ComponentTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -17,12 +17,96 @@
#include "ComponentTable.h"
-ComponentTable::ComponentTable(const std::vector& directories) {
+ComponentTable::ComponentTable(const std::vector& directories,
+ std::shared_ptr validationTable) {
for (const auto& directory : directories) {
for (const auto& component : directory.getComponents()) {
components.push_back(component);
}
}
+
+ const auto tableName = std::string("Component");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/component-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Component",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("Identifies the component record.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "ComponentId",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryGuid,
+ "",
+ DescriptionWithUrl("A string GUID unique to this component, "
+ "version, and language.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Directory_",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "Directory",
+ 1,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("External key of an entry in the Directory "
+ "table.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Attributes",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ "",
+ "",
+ DescriptionWithUrl("This column contains a bit flag that "
+ "specifies options for remote execution.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("This column contains a conditional statement "
+ "that can control whether a component is installed.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "KeyPath",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "File;Registry",
+ 1,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("This value points to a file or folder "
+ "belonging to the component that the installer uses to detect "
+ "the component.", url)
+ ));
+ }
}
bool ComponentTable::generate(MSIHANDLE hDatabase) {
diff --git a/installer/ComponentTable.h b/installer/ComponentTable.h
index 23d0047bc5f..76c96614d2a 100644
--- a/installer/ComponentTable.h
+++ b/installer/ComponentTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -20,10 +20,12 @@
#include "Generator.h"
#include "Component.h"
#include "Directory.h"
+#include "ValidationTable.h"
class ComponentTable : public Generator {
public:
- explicit ComponentTable(const std::vector& directories);
+ explicit ComponentTable(const std::vector& directories,
+ std::shared_ptr validationTable);
~ComponentTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/ControlConditionTable.cpp b/installer/ControlConditionTable.cpp
index a669a610984..159095c6d96 100644
--- a/installer/ControlConditionTable.cpp
+++ b/installer/ControlConditionTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -19,7 +19,65 @@
#include "ControlConditionTable.h"
ControlConditionTable::ControlConditionTable(
- const std::vector& controls) noexcept : controls(controls) {}
+ const std::vector& controls,
+ std::shared_ptr validationTable) : controls(controls) {
+ const auto tableName = std::string("ControlCondition");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/controlcondition-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Dialog_",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "Dialog",
+ 1,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("An external key to the first column of the "
+ "Dialog table.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Control_",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "Control",
+ 2,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("An external key to the second column of the "
+ "Control table.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Action",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryText,
+ "Default;Disable;Enable;Hide;Show",
+ DescriptionWithUrl("The action that is to be taken on the "
+ "control.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("A conditional statement that specifies under "
+ "which conditions the action should be triggered.", url)
+ ));
+ }
+}
bool ControlConditionTable::generate(MSIHANDLE hDatabase)
{
diff --git a/installer/ControlConditionTable.h b/installer/ControlConditionTable.h
index 8351dd281c1..698a8d8e488 100644
--- a/installer/ControlConditionTable.h
+++ b/installer/ControlConditionTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -22,11 +22,13 @@
#include "Generator.h"
#include "ControlCondition.h"
#include "Control.h"
+#include "ValidationTable.h"
class ControlConditionTable : public Generator {
public:
explicit ControlConditionTable(
- const std::vector& controls) noexcept;
+ const std::vector& controls,
+ std::shared_ptr validationTable);
~ControlConditionTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/ControlEventTable.cpp b/installer/ControlEventTable.cpp
index b2cfa55d5bd..139bd221aeb 100644
--- a/installer/ControlEventTable.cpp
+++ b/installer/ControlEventTable.cpp
@@ -18,7 +18,93 @@
#include "ControlEventTable.h"
ControlEventTable::ControlEventTable(
- const std::vector& controls) noexcept : controls(controls) {}
+ const std::vector& controls,
+ std::shared_ptr validationTable) : controls(controls) {
+ const auto tableName = std::string("ControlEvent");
+ const auto url = "https://learn.microsoft.com/en-us/windows/win32/msi/controlevent-table";
+ if (validationTable != nullptr) {
+ validationTable->add(Validation(
+ tableName,
+ "Dialog_",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "Dialog",
+ 1,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("An external key to the first column of the "
+ "Dialog table.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Control_",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "Control",
+ 2,
+ ValidationCategoryIdentifier,
+ "",
+ DescriptionWithUrl("An external key to the second column of the "
+ "Control table.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Event",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryFormatted,
+ "",
+ DescriptionWithUrl("An identifier that specifies the type of "
+ "event that should take place when the user interacts with "
+ "the control specified by Dialog_ and Control_.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Argument",
+ false,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryFormatted,
+ "",
+ DescriptionWithUrl("A value used as a modifier when triggering a "
+ "particular event.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Condition",
+ true,
+ MSI_NULL_INTEGER,
+ MSI_NULL_INTEGER,
+ "",
+ MSI_NULL_INTEGER,
+ ValidationCategoryCondition,
+ "",
+ DescriptionWithUrl("A conditional statement that determines "
+ "whether the installer activates the event in the Event "
+ "column.", url)
+ ));
+ validationTable->add(Validation(
+ tableName,
+ "Ordering",
+ true,
+ 0,
+ 2147483647,
+ "",
+ MSI_NULL_INTEGER,
+ "",
+ "",
+ DescriptionWithUrl("An integer used to order several events tied "
+ "to the same control.", url)
+ ));
+ }
+}
bool ControlEventTable::generate(MSIHANDLE hDatabase) {
std::cout << "Generating ControlEventTable..." << std::endl;
diff --git a/installer/ControlEventTable.h b/installer/ControlEventTable.h
index 6e947f3185f..92f53b61aa4 100644
--- a/installer/ControlEventTable.h
+++ b/installer/ControlEventTable.h
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -20,10 +20,12 @@
#include "ControlEvent.h"
#include "Generator.h"
#include "Control.h"
+#include "ValidationTable.h"
class ControlEventTable : public Generator {
public:
- explicit ControlEventTable(const std::vector& controls) noexcept;
+ explicit ControlEventTable(const std::vector& controls,
+ std::shared_ptr validationTable);
~ControlEventTable() = default;
bool generate(MSIHANDLE hDatabase) override;
private:
diff --git a/installer/ControlTable.cpp b/installer/ControlTable.cpp
index 57b7fd43004..e2ff33bddb3 100644
--- a/installer/ControlTable.cpp
+++ b/installer/ControlTable.cpp
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// https://boinc.berkeley.edu
-// Copyright (C) 2024 University of California
+// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@@ -23,8 +23,170 @@
#include "RadioButtonTable.h"
#include "ControlTable.h"
-ControlTable::ControlTable(const std::vector