--- 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`.