Files

78 lines
7.1 KiB
Markdown
Raw Permalink Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- DataPRO/Modules/InstallerCustomActions/Common/PreviousInstall.cs
generated_at: "2026-04-16T04:43:42.723638+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "d9773bfec96b878e"
---
# Common
### **Purpose**
This module provides utilities for detecting and retrieving information about previously installed versions of the *DataPRO* application during Windows Installer custom actions. Specifically, it locates the most recent *lower* version of DataPRO installed on the system (i.e., older than the version being installed) by querying the Windows Registry under `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall` (and fallback to `Components`), enabling upgrade or migration logic in the installer. It is intended for use in Windows Installer custom actions to support version-aware installation scenarios.
---
### **Public Interface**
#### `static string GetMostRecentlyInstalledSubKeyName(Version installingVersion, out string mostRecentlyInstalledLowerVersion)`
- **Behavior**: Scans the registry under the `Uninstall` key (path defined by `Settings.Default.RegistrySoftwareInstalledProducts`) for installed products whose display name matches `Settings.Default.RegistryDataPRO`. Among those with a version *strictly less than* `installingVersion`, it returns the registry subkey name (`ProductCode` or similar identifier) of the *most recent* (i.e., highest) version.
- **Output parameter `mostRecentlyInstalledLowerVersion`**: Contains the string representation (e.g., `"1.2.3.4"`) of the highest version found that is less than `installingVersion`, or `string.Empty` if none found.
- **Returns**: The registry subkey name (e.g., `"DataPRO-abc123"`) of the most recent lower version, or `string.Empty` if no matching product is found or registry access fails.
#### `static string GetMostRecentlyInstalledPath(string mostRecentlyInstalledSubKeyName)`
- **Behavior**: Given a registry subkey name (typically obtained from `GetMostRecentlyInstalledSubKeyName`), this method attempts to resolve the installation path (`InstallLocation`) of that product.
- **Logic**:
1. First searches in `RegistrySoftwareInstalledProducts` (`Uninstall`), looking for the matching subkey and retrieving `InstallLocation` (`Settings.Default.RegistryInstallLocation`).
2. If not found, falls back to searching in `RegistrySoftwareInstalledComponents`, where the value named `mostRecentlyInstalledSubKeyName` is expected to hold a full path ending with `Settings.Default.RegistryDataPROExeConfig` (e.g., `"DataPRO.exe.config"`), from which the directory is extracted.
3. Appends `Settings.Default.RegistryDataPRO + "\\"` to the path *if* the path contains `Settings.Default.DTSSuite`, to ensure correct nesting (e.g., for suite-based installations).
- **Returns**: The resolved installation directory path (e.g., `"C:\Program Files\DTSSuite\DataPRO\\"`), or `string.Empty` if not found or invalid.
#### `static bool IsGreaterThan(this Version leftString, Version rightString)`
- **Behavior**: Extension method for `System.Version`. Returns `true` if `leftString` is greater than `rightString` (i.e., `leftString.CompareTo(rightString) > 0`).
#### `static bool IsLessThan(this Version leftString, Version rightString)`
- **Behavior**: Extension method for `System.Version`. Returns `true` if `leftString` is less than `rightString` (i.e., `leftString.CompareTo(rightString) < 0`).
---
### **Invariants**
- Only products with `DisplayName` equal to `Settings.Default.RegistryDataPRO` are considered.
- Only versions *strictly less than* the `installingVersion` are considered in `GetMostRecentlyInstalledSubKeyName`.
- The method `GetMostRecentlyInstalledSubKeyName` returns the *highest* version among qualifying products (not the most recently *installed* chronologically, but the highest version number).
- Registry access is performed exclusively on `RegistryHive.LocalMachine` with `RegistryView.Registry64`.
- Paths retrieved via `GetMostRecentlyInstalledPath` are normalized by stripping the trailing `Settings.Default.RegistryDataPROExeConfig` (e.g., `"DataPRO.exe.config"`) when sourced from the `Components` key.
- If a registry value is missing or evaluates to `"-1"` (as a string), it is treated as invalid and skipped.
---
### **Dependencies**
- **External**:
- `Microsoft.Win32.RegistryKey` (for registry access).
- `System.Version` (for version comparison).
- `System.Diagnostics.EventLog` (used internally for logging in `GetMostRecentlyInstalledPath`).
- **Internal**:
- `Common.Properties.Settings.Default`:
- `RegistrySoftwareInstalledProducts` (e.g., `"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"`).
- `RegistrySoftwareInstalledComponents` (e.g., `"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Installation\\Components"`).
- `RegistryInstallProperties` (e.g., `"InstallProperties"` subkey).
- `RegistryDisplayName` (e.g., `"DisplayName"`).
- `RegistryDisplayVersion` (e.g., `"DisplayVersion"`).
- `RegistryInstallLocation` (e.g., `"InstallLocation"`).
- `RegistryDataPRO` (e.g., `"DataPRO"`).
- `RegistryDataPROExeConfig` (e.g., `"DataPRO.exe.config"`).
- `DTSSuite` (e.g., `"DTSSuite"`).
- **Used by**: Windows Installer custom actions (not visible in source, but implied by namespace `Installer.Common` and method naming).
---
### **Gotchas**
- **Hardcoded fallback to `"-1"`**: Registry values are read with `RegistryValueOptions.None`, and if the result is `"-1"` (string), it is treated as invalid. This assumes `"-1"` is used as a sentinel for missing/invalid values—a convention not guaranteed by Windows Installer or .NET.
- **No error handling for registry access**: If `OpenSubKey` or `GetValue` throws (e.g., due to permissions or corruption), the exception propagates unhandled. Logging in `GetMostRecentlyInstalledPath` uses `EventLog`, but only for informational paths, not errors.
- **Assumes 64-bit registry view only**: Uses `RegistryView.Registry64`, which may miss 32-bit installations on 64-bit systems (though `HKEY_LOCAL_MACHINE\SOFTWARE` redirection would apply).
- **Ambiguity in `mostRecentlyInstalledSubKeyName`**: The term "most recently installed" in the method name is misleading—it actually returns the *highest version*, not the one with the latest install timestamp.
- **Path normalization quirk**: The suffix stripping in `GetMostRecentlyInstalledPath` assumes `mostRecentlyInstalledSubKeyName` ends *exactly* with `RegistryDataPROExeConfig`. If the value is malformed or truncated, substring logic may yield incorrect paths.
- **No fallback for missing `InstallLocation`**: If `InstallLocation` is absent in `Uninstall`, and the `Components` lookup fails or yields an invalid path, the method returns `string.Empty` without indicating *why*.
- **Event log source must exist**: `log.Source = "DataPROInstaller"` assumes the event source is pre-registered; otherwise, `WriteEntry` may throw on first use (though this is rare in installer contexts).
- **No handling for `Version` parsing failures**: If `RegistryDisplayVersion` contains a non-parsable string (e.g., `"1.2.3-beta"`), `new Version(strThisVersion)` throws `FormatException`.