From faa6b1be666470075becd0d5c2deabfe359eac2b Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 16:51:41 +0200 Subject: [PATCH 01/12] #1869: Extract process of running reg into private method --- .../devonfw/tools/ide/os/WindowsHelperImpl.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index ee6d55eb2f..29bc0b9a7a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -60,13 +60,23 @@ public String getUserEnvironmentValue(String key) { @Override public String getRegistryValue(String path, String key) { - ProcessResult result = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable("reg").addArgs("query", path, "/v", key) + List out = runReg("query", path, "/v", key); + if (out != null) { + return retrieveRegString(key, out); + } + return null; + } + + private List runReg(String... args) { + ProcessResult result = this.context.newProcess() + .errorHandling(ProcessErrorHandling.LOG_WARNING) + .executable("reg") + .addArgs(args) .run(ProcessMode.DEFAULT_CAPTURE); if (!result.isSuccessful()) { return null; } - List out = result.getOut(); - return retrieveRegString(key, out); + return result.getOut(); } /** From 53fff3f7d11a73096caa327a11240c6882d44214 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:15:58 +0200 Subject: [PATCH 02/12] #1869: Extend WindowsHelper interface with methods to retrieve entries from registry --- .../devonfw/tools/ide/os/WindowsHelper.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java index 266b4f8179..29bc8ebfa8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java @@ -32,6 +32,30 @@ public interface WindowsHelper { */ String getRegistryValue(String path, String key); + /** + * @param appName the application name to search for in the Windows registry. + * @return the DisplayName entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getDisplayNameFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the DisplayIcon entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getDisplayIconFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the UninstallString entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getUninstallStringFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the InstallLocation entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getInstallLocationFromRegistry(String appName); + /** * @param context the {@link IdeContext}. * @return the instance of {@link WindowsHelper}. From d1230886657c27f19b59fabe9aa225675b7a8985 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:20:58 +0200 Subject: [PATCH 03/12] #1869: Add interface methods to WindowsHelperImpl --- .../tools/ide/os/WindowsHelperImpl.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 29bc0b9a7a..46354626e5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -21,6 +21,13 @@ public class WindowsHelperImpl implements WindowsHelper { /** Registry key for the users environment variables. */ public static final String HKCU_ENVIRONMENT = "HKCU\\Environment"; + /** Common Windows registry base paths containing (uninstall) information for installed applications (system-wide and per-user). */ + private static final String[] REGISTRY_BASE_PATHS = { + "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" + }; + private final IdeContext context; /** @@ -67,7 +74,44 @@ public String getRegistryValue(String path, String key) { return null; } - private List runReg(String... args) { + @Override + public String getDisplayNameFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayName"); + } + + @Override + public String getDisplayIconFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayIcon"); + } + + @Override + public String getUninstallStringFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "UninstallString"); + } + + @Override + public String getInstallLocationFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "InstallLocation"); + } + + private String getRegistryValueBySearch(String appName, String key) { + + for (String registryBasePath : REGISTRY_BASE_PATHS) { + List out = runReg("query", registryBasePath, "/s", "/f", appName); + if (out != null) { + return retrieveRegString(key, out); + } + } + return null; + } + + /** + * Executes a Windows registry command and returns its output. + * + * @param args the registry command arguments. + * @return the command output lines, or {@code null} if the command failed + */ + protected List runReg(String... args) { ProcessResult result = this.context.newProcess() .errorHandling(ProcessErrorHandling.LOG_WARNING) .executable("reg") From 7c8f432c3b682320f25ed86bef2d88f40e5a15c6 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:21:33 +0200 Subject: [PATCH 04/12] #1869: Add interface methods to WindowsHelperMock --- .../tools/ide/os/WindowsHelperMock.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index f2245f40ef..dc8539a38f 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.os; +import java.util.List; import java.util.Properties; import com.devonfw.tools.ide.context.IdeContext; @@ -11,6 +12,18 @@ public class WindowsHelperMock extends WindowsHelperImpl { private final Properties env; + private static final String MOCK_APP_NAME = "TestApp"; + + private static final String MOCK_DISPLAY_NAME = "Test Application"; + + private static final String MOCK_INSTALL_LOCATION = "C:\\Program Files\\TestApp"; + + private static final String MOCK_DISPLAY_ICON = + "C:\\Program Files\\TestApp\\testapp.exe,0"; + private static final String MOCK_UNINSTALL_STRING = + "\"C:\\Program Files\\TestApp\\uninstall.exe\""; + + /** * The constructor. */ @@ -41,6 +54,26 @@ public String getUserEnvironmentValue(String key) { return this.env.getProperty(key); } + @Override + public String getDisplayNameFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_NAME : null; + } + + @Override + public String getDisplayIconFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_ICON : null; + } + + @Override + public String getUninstallStringFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_UNINSTALL_STRING : null; + } + + @Override + public String getInstallLocationFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_INSTALL_LOCATION : null; + } + @Override public String getRegistryValue(String path, String key) { @@ -51,4 +84,35 @@ public String getRegistryValue(String path, String key) { } return null; } + + private boolean matchesApp(String appName) { + return appName != null + && appName.equalsIgnoreCase(MOCK_APP_NAME); + } + + @Override + protected List runReg(String... args) { + + // simulate reg.exe filtering: "/f " + String searchValue = null; + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + searchValue = args[i + 1]; + break; + } + } + + // Only return output if searched app matches + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); // same behavior as reg.exe: no results + } + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ Test Application", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } } From 9693b955657abaa8b634284b47bfee3a0307747c Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:22:27 +0200 Subject: [PATCH 05/12] #1869: Add test-seam class to simulate running of reg.exe command --- .../ide/os/WindowsHelperImplTestable.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java new file mode 100644 index 0000000000..54037f6813 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -0,0 +1,48 @@ +package com.devonfw.tools.ide.os; + +import java.util.List; + +import com.devonfw.tools.ide.context.IdeContext; + +/** + * Test-specific subclass of {@link WindowsHelperImpl}. + * + *

+ * Mainly used as a test seam to simulate the reg.exe command for test purposes. + *

+ */ +public class WindowsHelperImplTestable extends WindowsHelperImpl { + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public WindowsHelperImplTestable(IdeContext context) { + + super(context); + } + + @Override + protected List runReg(String... args) { + + // simulate reg.exe filtering: "/f " + String searchValue = null; + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + searchValue = args[i + 1]; + break; + } + } + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); + } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ Test Application", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } +} From ccb14d315494888bafd765ba557cb2352a99aabc Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:43:16 +0200 Subject: [PATCH 06/12] #1869: Add tests for WindowsHelperImpl --- .../tools/ide/os/WindowsHelperImplTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java index 85935fb72a..bbe84a040a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java @@ -14,6 +14,10 @@ */ class WindowsHelperImplTest extends AbstractIdeContextTest { + private static final String TEST_APP_NAME = "TestApp"; + + private static final String UNKNOWN_TEST_APP_NAME = "UnknownApp"; + /** * Tests if the USER_PATH registry entry can be parsed properly. */ @@ -48,4 +52,47 @@ void testWindowsHelperParseEmptyRegStringReturnsNull() { assertThat(regString).isNull(); } + /** + * Tests if correct keys can be found in registry output for app name filter. + */ + @Test + void testRegistryLookupReturnsCorrectEntryIfFound() { + // arrange + AbstractIdeTestContext context = new IdeTestContext(); + WindowsHelperImpl helper = new WindowsHelperImplTestable(context); + + // act + String displayName = helper.getDisplayNameFromRegistry(TEST_APP_NAME); + String icon = helper.getDisplayIconFromRegistry(TEST_APP_NAME); + String uninstall = helper.getUninstallStringFromRegistry(TEST_APP_NAME); + String location = helper.getInstallLocationFromRegistry(TEST_APP_NAME); + + // assert + assertThat(displayName).isEqualTo("Test Application"); + assertThat(icon).isEqualTo("C:\\Program Files\\TestApp\\testapp.exe,0"); + assertThat(uninstall).isEqualTo("\"C:\\Program Files\\TestApp\\uninstall.exe\""); + assertThat(location).isEqualTo("C:\\Program Files\\TestApp"); + } + + /** + * Tests if registry lookup return nulls on unknown app name filter. + */ + @Test + void testRegistryLookupReturnsNullIfNotFound() { + // arrange + AbstractIdeTestContext context = new IdeTestContext(); + WindowsHelperImpl helper = new WindowsHelperImplTestable(context); + + // act + String displayName = helper.getDisplayNameFromRegistry(UNKNOWN_TEST_APP_NAME); + String icon = helper.getDisplayIconFromRegistry(UNKNOWN_TEST_APP_NAME); + String uninstall = helper.getUninstallStringFromRegistry(UNKNOWN_TEST_APP_NAME); + String location = helper.getInstallLocationFromRegistry(UNKNOWN_TEST_APP_NAME); + + // assert + assertThat(displayName).isNull(); + assertThat(icon).isNull(); + assertThat(uninstall).isNull(); + assertThat(location).isNull(); + } } From 8b4d51d834a880b58f52b3807a851ae8c5d830a9 Mon Sep 17 00:00:00 2001 From: jakozian Date: Mon, 4 May 2026 10:12:36 +0200 Subject: [PATCH 07/12] Update cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java Co-authored-by: MarvMa --- .../main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 46354626e5..1d40730354 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -98,7 +98,7 @@ private String getRegistryValueBySearch(String appName, String key) { for (String registryBasePath : REGISTRY_BASE_PATHS) { List out = runReg("query", registryBasePath, "/s", "/f", appName); - if (out != null) { + if (out != null && !out.isEmpty()) { return retrieveRegString(key, out); } } From a35910b288759f9189274bc92571557b97b1500c Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 5 May 2026 15:09:11 +0200 Subject: [PATCH 08/12] #1869: Replace string with constant --- .../test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index dc8539a38f..66af992ac4 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -103,7 +103,7 @@ protected List runReg(String... args) { } // Only return output if searched app matches - if (!"TestApp".equalsIgnoreCase(searchValue)) { + if (!MOCK_APP_NAME .equalsIgnoreCase(searchValue)) { return List.of(); // same behavior as reg.exe: no results } From 6ed1306df795d7a0eb5ff9ac629a8f0c1704915c Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 09:35:08 +0200 Subject: [PATCH 09/12] #1869: Change display name to display version --- .../java/com/devonfw/tools/ide/os/WindowsHelper.java | 2 +- .../com/devonfw/tools/ide/os/WindowsHelperImpl.java | 4 ++-- .../devonfw/tools/ide/os/WindowsHelperImplTest.java | 8 ++++---- .../tools/ide/os/WindowsHelperImplTestable.java | 2 +- .../com/devonfw/tools/ide/os/WindowsHelperMock.java | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java index 29bc8ebfa8..765a452f93 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java @@ -36,7 +36,7 @@ public interface WindowsHelper { * @param appName the application name to search for in the Windows registry. * @return the DisplayName entry if the application is found in the Windows registry or {@code null} if nothing was found. */ - String getDisplayNameFromRegistry(String appName); + String getDisplayVersionFromRegistry(String appName); /** * @param appName the application name to search for in the Windows registry. diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 1d40730354..c78b60af72 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -75,8 +75,8 @@ public String getRegistryValue(String path, String key) { } @Override - public String getDisplayNameFromRegistry(String appName) { - return getRegistryValueBySearch(appName, "DisplayName"); + public String getDisplayVersionFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayVersion"); } @Override diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java index bbe84a040a..47a5a1557c 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java @@ -62,13 +62,13 @@ void testRegistryLookupReturnsCorrectEntryIfFound() { WindowsHelperImpl helper = new WindowsHelperImplTestable(context); // act - String displayName = helper.getDisplayNameFromRegistry(TEST_APP_NAME); + String displayVersion = helper.getDisplayVersionFromRegistry(TEST_APP_NAME); String icon = helper.getDisplayIconFromRegistry(TEST_APP_NAME); String uninstall = helper.getUninstallStringFromRegistry(TEST_APP_NAME); String location = helper.getInstallLocationFromRegistry(TEST_APP_NAME); // assert - assertThat(displayName).isEqualTo("Test Application"); + assertThat(displayVersion).isEqualTo("1.1.1"); assertThat(icon).isEqualTo("C:\\Program Files\\TestApp\\testapp.exe,0"); assertThat(uninstall).isEqualTo("\"C:\\Program Files\\TestApp\\uninstall.exe\""); assertThat(location).isEqualTo("C:\\Program Files\\TestApp"); @@ -84,13 +84,13 @@ void testRegistryLookupReturnsNullIfNotFound() { WindowsHelperImpl helper = new WindowsHelperImplTestable(context); // act - String displayName = helper.getDisplayNameFromRegistry(UNKNOWN_TEST_APP_NAME); + String displayVersion = helper.getDisplayVersionFromRegistry(UNKNOWN_TEST_APP_NAME); String icon = helper.getDisplayIconFromRegistry(UNKNOWN_TEST_APP_NAME); String uninstall = helper.getUninstallStringFromRegistry(UNKNOWN_TEST_APP_NAME); String location = helper.getInstallLocationFromRegistry(UNKNOWN_TEST_APP_NAME); // assert - assertThat(displayName).isNull(); + assertThat(displayVersion).isNull(); assertThat(icon).isNull(); assertThat(uninstall).isNull(); assertThat(location).isNull(); diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java index 54037f6813..a63946e71a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -39,7 +39,7 @@ protected List runReg(String... args) { } return List.of( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ Test Application", + " DisplayVersion REG_SZ 1.1.1", " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", " InstallLocation REG_SZ C:\\Program Files\\TestApp", " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index 66af992ac4..e96fef6652 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -14,7 +14,7 @@ public class WindowsHelperMock extends WindowsHelperImpl { private static final String MOCK_APP_NAME = "TestApp"; - private static final String MOCK_DISPLAY_NAME = "Test Application"; + private static final String MOCK_DISPLAY_VERSION = "1.1.1"; private static final String MOCK_INSTALL_LOCATION = "C:\\Program Files\\TestApp"; @@ -55,8 +55,8 @@ public String getUserEnvironmentValue(String key) { } @Override - public String getDisplayNameFromRegistry(String appName) { - return matchesApp(appName) ? MOCK_DISPLAY_NAME : null; + public String getDisplayVersionFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_VERSION : null; } @Override @@ -103,13 +103,13 @@ protected List runReg(String... args) { } // Only return output if searched app matches - if (!MOCK_APP_NAME .equalsIgnoreCase(searchValue)) { + if (!MOCK_APP_NAME.equalsIgnoreCase(searchValue)) { return List.of(); // same behavior as reg.exe: no results } return List.of( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ Test Application", + " DisplayName REG_SZ 1.1.1", " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", " InstallLocation REG_SZ C:\\Program Files\\TestApp", " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" From 2d40318a66864b2da76f7a3eed444fce669e78e7 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 11:24:28 +0200 Subject: [PATCH 10/12] #1869: Change search to find exact path first and the keys in this path --- .../tools/ide/os/WindowsHelperImpl.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index c78b60af72..a69a588d10 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -96,13 +96,33 @@ public String getInstallLocationFromRegistry(String appName) { private String getRegistryValueBySearch(String appName, String key) { + String uninstallKey = findUninstallKey(appName); + if (uninstallKey == null) { + return null; + } + List out = runReg("query", uninstallKey); + if (out != null) { + return retrieveRegString(key, out); + } + return null; + } + + private String findUninstallKey(String appName) { + for (String registryBasePath : REGISTRY_BASE_PATHS) { List out = runReg("query", registryBasePath, "/s", "/f", appName); - if (out != null && !out.isEmpty()) { - return retrieveRegString(key, out); + if (out == null) { + continue; + } + for (String line : out) { + line = line.trim(); + if (line.startsWith("HKEY_")) { + return line; // exact registry path (key) for tool + } } } return null; + } /** From e05473ad9d2ee7ee07ccde83ed0c0f50dc269022 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 12:57:34 +0200 Subject: [PATCH 11/12] #1870: Fix mock logic of runReg --- .../ide/os/WindowsHelperImplTestable.java | 48 ++++++++++++------ .../tools/ide/os/WindowsHelperMock.java | 50 ++++++++++++------- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java index a63946e71a..6e43b0fdd4 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -26,23 +26,43 @@ public WindowsHelperImplTestable(IdeContext context) { @Override protected List runReg(String... args) { - // simulate reg.exe filtering: "/f " - String searchValue = null; + String searchValue = extractFilterValue(args); + // Case: reg query /s /f + if (searchValue != null) { + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); + } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp" + ); + } + // Case: reg query + if (args.length >= 2 && + args[0].equalsIgnoreCase("query") && + args[1].endsWith("\\Uninstall\\TestApp")) { + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp", + " DisplayVersion REG_SZ 1.1.1", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } + + return List.of(); + } + + + private static String extractFilterValue(String[] args) { + for (int i = 0; i < args.length - 1; i++) { if ("/f".equalsIgnoreCase(args[i])) { - searchValue = args[i + 1]; - break; + return args[i + 1]; } } - if (!"TestApp".equalsIgnoreCase(searchValue)) { - return List.of(); - } - return List.of( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayVersion REG_SZ 1.1.1", - " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", - " InstallLocation REG_SZ C:\\Program Files\\TestApp", - " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" - ); + return null; } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index e96fef6652..1f1148271b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -93,26 +93,42 @@ private boolean matchesApp(String appName) { @Override protected List runReg(String... args) { - // simulate reg.exe filtering: "/f " - String searchValue = null; - for (int i = 0; i < args.length - 1; i++) { - if ("/f".equalsIgnoreCase(args[i])) { - searchValue = args[i + 1]; - break; + String searchValue = extractFilterValue(args); + // Case: reg query /s /f + if (searchValue != null) { + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp" + ); } - - // Only return output if searched app matches - if (!MOCK_APP_NAME.equalsIgnoreCase(searchValue)) { - return List.of(); // same behavior as reg.exe: no results + // Case: reg query + if (args.length >= 2 && + args[0].equalsIgnoreCase("query") && + args[1].endsWith("\\Uninstall\\TestApp")) { + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp", + " DisplayVersion REG_SZ 1.1.1", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); } - return List.of( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ 1.1.1", - " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", - " InstallLocation REG_SZ C:\\Program Files\\TestApp", - " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" - ); + return List.of(); + } + + private static String extractFilterValue(String[] args) { + + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + return args[i + 1]; + } + } + return null; } } From af4ddbc7dae6da675bfaa61f6fc5232b21428b4d Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 12 May 2026 08:31:51 +0200 Subject: [PATCH 12/12] Update cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index a69a588d10..102c9b680a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -23,8 +23,8 @@ public class WindowsHelperImpl implements WindowsHelper { /** Common Windows registry base paths containing (uninstall) information for installed applications (system-wide and per-user). */ private static final String[] REGISTRY_BASE_PATHS = { - "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" };