101 lines
6.1 KiB
Markdown
101 lines
6.1 KiB
Markdown
---
|
||
source_files:
|
||
- DataPRO/Modules/InstallerCustomActions/RegAddProductCode/AddProductCode.cs
|
||
generated_at: "2026-04-16T04:43:28.721583+00:00"
|
||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||
schema_version: 1
|
||
sha256: "c22465a70e9ccf40"
|
||
---
|
||
|
||
# RegAddProductCode
|
||
|
||
## Documentation: `AddProductCode.cs` — Installer Custom Action for Registry Policy Configuration
|
||
|
||
---
|
||
|
||
### 1. Purpose
|
||
|
||
This module implements a Windows Installer custom action (`AddProductCode`) responsible for configuring Windows Installer security policies in the registry prior to or during DataPRO installation. Specifically, it enforces the `SecureRepairPolicy` setting and adds the DataPRO product code to the `SecureRepairWhitelist`, enabling the installer to perform repair operations on `DataPRO.exe` and its dependencies without being blocked by Windows Installer’s secure repair restrictions. It also validates architecture compatibility between the installer process (32- or 64-bit) and the operating system, terminating the installation with a user-facing error if a mismatch is detected (e.g., 32-bit installer on 64-bit OS), to prevent downstream failures such as incompatible SQL LocalDB version conflicts.
|
||
|
||
---
|
||
|
||
### 2. Public Interface
|
||
|
||
The class `AddProductCode` exposes only one public entry point:
|
||
|
||
#### `static int Main(string[] args)`
|
||
- **Signature**: `private static int Main(string[] args)`
|
||
- **Behavior**:
|
||
Entry point for the custom action. Parses `args[0]` as `architectureVersion` (`"x86"` or `"x64"`), logs environment details (process bitness, OS bitness), and performs architecture validation:
|
||
- If `architectureVersion == "x86"` and OS is 64-bit → shows error message and returns `1`.
|
||
- If `architectureVersion == "x64"` and OS is 32-bit → shows error message and returns `1`.
|
||
- Otherwise, calls `AddCodeToRegistry()` and returns `0` (success) or `1` (failure if registry write fails).
|
||
- Logging is performed via `EventLog.WriteEntry` under source `"DataPROInstaller"`.
|
||
|
||
> **Note**: Though declared `private`, this method is the *de facto* public entry point for the custom action as invoked by the Windows Installer (e.g., via `CustomAction` table). It is not called internally.
|
||
|
||
---
|
||
|
||
### 3. Invariants
|
||
|
||
- **Architecture Consistency**:
|
||
The installer process bitness must match the target OS bitness *relative to the requested architecture* (`args[0]`). Mismatches cause early termination with exit code `1`.
|
||
- **Registry Hive & View**:
|
||
All registry modifications target `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer` using `RegistryView.Registry64`, ensuring writes occur in the 64-bit view even on 64-bit systems (i.e., not redirected to `WOW6432Node`).
|
||
- **Policy Values**:
|
||
The following must be set:
|
||
- `SecureRepairPolicy` (DWORD) = `2` (enables secure repair for whitelisted products).
|
||
- `SecureRepairWhitelist\{C4889149-0CAF-44C1-B226-8F6E73684DF4}` (DWORD) = `0` (adds product code to whitelist).
|
||
- **Exit Codes**:
|
||
- `0`: Success (registry updated or already correct).
|
||
- `1`: Failure due to architecture mismatch or registry write error.
|
||
|
||
---
|
||
|
||
### 4. Dependencies
|
||
|
||
#### Imports / External Dependencies:
|
||
- `System.Diagnostics.EventLog` – for installer logging.
|
||
- `Microsoft.Win32.RegistryKey`, `RegistryHive`, `RegistryView`, `RegistryValueKind` – for registry access.
|
||
- `System.Windows.Forms.MessageBox` – for user-facing error messages.
|
||
- `RegAddProductCode.Properties.Settings.Default` – static configuration values:
|
||
- `Settings.Default.InstallerKey`: Registry path `"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer"`
|
||
- `Settings.Default.SecureRepairPolicy`: Policy value name `"SecureRepairPolicy"`
|
||
- `Settings.Default.SecureRepairWhitelistKey`: Subkey path `"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer\\SecureRepairWhitelist"`
|
||
- `Settings.Default.ProductCode`: Product code GUID `"{C4889149-0CAF-44C1-B226-8F6E73684DF4}"`
|
||
- `Settings.Default.MissingKey`: Format string for error messages (e.g., `"Registry key missing: {0}"`).
|
||
|
||
#### Usage:
|
||
- **Called by**: Windows Installer as a custom action (likely in a WiX or similar MSI project).
|
||
- **Depends on**:
|
||
- Administrative privileges (required to write to `HKLM\SOFTWARE\Policies\...`).
|
||
- `EventLog` source `"DataPROInstaller"` must exist or be creatable by the installer process.
|
||
|
||
---
|
||
|
||
### 5. Gotchas
|
||
|
||
- **Hardcoded Product Code**:
|
||
The product code `"{C4889149-0CAF-44C1-B226-8F6E73684DF4}"` is hardcoded in both `Settings.Default.ProductCode` and the whitelist logic. Changing the product code in the installer project requires updating this value manually—no dynamic injection is present.
|
||
|
||
- **Silent Failure Risk in 32-bit on 64-bit**:
|
||
The comment explains that allowing a 32-bit installer on 64-bit OS could cause the SQL LocalDB 2014 prerequisite to fail silently, leading to runtime DB version incompatibility. This is a *preventive guard*, but the fix is architectural (blocking 32-bit installers entirely on 64-bit OS), not a workaround.
|
||
|
||
- **Registry View Hardcoded to 64-bit**:
|
||
`RegistryView.Registry64` is used unconditionally—even on 32-bit OSes. While `OpenBaseKey` with `Registry64` on a 32-bit OS is safe (it falls back to the native view), this may be unintentional or legacy. No explicit fallback to `RegistryView.Registry32` exists.
|
||
|
||
- **No Rollback Handling**:
|
||
If `AddCodeToRegistry()` succeeds but the main installer fails later, the registry changes are *not* rolled back. The policy settings remain in place.
|
||
|
||
- **Commented-Out UI Error**:
|
||
The line `// MessageBox.Show(result);` is commented out in `Main()`, meaning registry errors are *only* logged to Event Log, not shown to the user. This may reduce visibility during silent installs.
|
||
|
||
- **No Exception Handling Granularity**:
|
||
`AddCodeToRegistry()` catches all exceptions generically and returns only the message string. Specific exceptions (e.g., `UnauthorizedAccessException`, `SecurityException`) are not differentiated.
|
||
|
||
- **Assumes Installer Runs with Admin Rights**:
|
||
The code assumes the custom action runs with elevated privileges. If not, registry writes will fail silently (or with generic error), and no fallback is implemented.
|
||
|
||
---
|
||
|
||
*End of documentation.* |