IJPL-35262 updater: sending "associations changed" notification to the shell

GitOrigin-RevId: f575a9df916979dab1accd9bb417a3b88aa9016a
This commit is contained in:
Roman Shevchenko
2025-11-10 20:47:26 +00:00
committed by intellij-monorepo-bot
parent 8e5e11275a
commit afed925d30
@@ -1,8 +1,10 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.updater;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*;
import com.sun.jna.win32.StdCallLibrary;
import mslinks.ShellLink;
import org.jetbrains.annotations.Nullable;
@@ -108,14 +110,18 @@ final class PostUpdateTasks {
try {
LOG.info("updateContextMenuEntries for: " + targetPath);
var rootKeys = List.of(WinReg.HKEY_CURRENT_USER, WinReg.HKEY_LOCAL_MACHINE);
var updated = false;
for (var rootKey : rootKeys) {
// file association target
processContextMenuKey(rootKey, "Software\\Classes", true, targetPath);
updated |= processContextMenuKey(rootKey, "Software\\Classes", true, targetPath);
// "edit with" context menu
processContextMenuKey(rootKey, "Software\\Classes\\*\\shell", false, targetPath);
updated |= processContextMenuKey(rootKey, "Software\\Classes\\*\\shell", false, targetPath);
// folder context menu
processContextMenuKey(rootKey, "Software\\Classes\\Directory\\shell", false, targetPath);
processContextMenuKey(rootKey, "Software\\Classes\\Directory\\Background\\shell", false, targetPath);
updated |= processContextMenuKey(rootKey, "Software\\Classes\\Directory\\shell", false, targetPath);
updated |= processContextMenuKey(rootKey, "Software\\Classes\\Directory\\Background\\shell", false, targetPath);
}
if (updated) {
notifyShellAboutChangedAssociations();
}
}
catch (Throwable t) {
@@ -123,7 +129,8 @@ final class PostUpdateTasks {
}
}
private static void processContextMenuKey(WinReg.HKEY rootKey, String baseKey, boolean fileAssociation, String targetPath) {
private static boolean processContextMenuKey(WinReg.HKEY rootKey, String baseKey, boolean fileAssociation, String targetPath) {
var updated = false;
for (var subKey : getRegistryGetKeys(rootKey, baseKey)) {
if (fileAssociation && (baseKey.startsWith(".") || baseKey.startsWith("ms-") || baseKey.startsWith("microsoft"))) continue;
try {
@@ -137,6 +144,7 @@ final class PostUpdateTasks {
if (!name.equals(newName)) {
LOG.info("key: " + formatKey(rootKey, baseKey, subKey));
Advapi32Util.registrySetStringValue(rootKey, key, "", newName);
updated = true;
}
}
}
@@ -144,6 +152,7 @@ final class PostUpdateTasks {
LOG.log(Level.FINE, e, () -> "processContextMenuKey: " + formatKey(rootKey, baseKey, subKey));
}
}
return updated;
}
private static String[] getRegistryGetKeys(WinReg.HKEY rootKey, String key) {
@@ -166,6 +175,24 @@ final class PostUpdateTasks {
return sb.toString();
}
private static void notifyShellAboutChangedAssociations() {
try {
var shell32 = Native.load("shell32", Shell32.class);
shell32.SHChangeNotify(new WinDef.LONG(Shell32.SHCNE_ASSOCCHANGED), new WinDef.UINT(Shell32.SHCNF_IDLIST), null, null);
}
catch (Throwable t) {
LOG.log(Level.WARNING, "notifyShellAboutChangedAssociations failed", t);
}
}
@SuppressWarnings("SpellCheckingInspection")
private interface Shell32 extends StdCallLibrary {
long SHCNE_ASSOCCHANGED = 0x08000000L;
int SHCNF_IDLIST = 0;
void SHChangeNotify(WinDef.LONG wEventId, WinDef.UINT uFlags, Pointer dwItem1, Pointer dwItem2);
}
static void updateWindowsShortcuts(Path targetDir, String nameAndVersion) {
LOG.info("updateWindowsShortcuts for: " + targetDir);
try {
@@ -213,7 +240,7 @@ final class PostUpdateTasks {
private static Path getFolderPath(int folder, Supplier<Path> fallback) {
try {
var path = new char[WinDef.MAX_PATH];
var res = Shell32.INSTANCE.SHGetFolderPath(null, folder, null, ShlObj.SHGFP_TYPE_CURRENT, path);
var res = com.sun.jna.platform.win32.Shell32.INSTANCE.SHGetFolderPath(null, folder, null, ShlObj.SHGFP_TYPE_CURRENT, path);
if (WinError.S_OK.equals(res)) {
var len = 0;
while (len < path.length && path[len] != 0) len++;