Conversation
…them Shell popups such as "Send To" are populated lazily: their owner keeps inserting items into the HMENU after menu::construct_with_hmenu has already enumerated it. The Breeze widget was therefore built from a stale snapshot (usually a single leftover item), so opening e.g. "Send To" showed an empty popup even though the items arrived a few milliseconds later. Evidence from the debug.log attached to std-microblock#378: 14.458 [info] Menu widget init from data: 1 14.462 [warning] Late menu mutation via SetMenuItemInfoW (...) 14.462..14.665 Late menu mutation via InsertMenuItemW (...) x45 The mutation hooks already detect these late changes, but they only re-synced *state* (sync_native_menu_item_update). Extend the same idea to the item list: * contextmenu: remember handle_menu_msg / init_popup_lparam so a menu can be re-read, and add send_init_msg = false so that re-reading does not ask the owner to populate the popup a second time. * menu_widget: add native_content_dirty + resync_from_native(), which rebuilds the children from the current native menu, and run it at the start of update() so the new content is laid out in the same frame. * hooks: mark the matching submenu dirty on any late mutation, hopping to the render loop thread first since the widget tree is not thread-safe. Also log the contents of a native submenu when it is built and when it is re-synced, which makes this class of problem diagnosable from debug.log alone.
The shell32 part of fix_win11_menu rewrites
mov ecx, 0x10 ; VK_SHIFT
call [GetKeyState]
into a constant `mov rax, 0xffff`, i.e. it makes the shell believe the
SHIFT key is held down forever. The code right after that call is
mov ecx, edi
bts ecx, 8 ; ecx |= 0x100 (CMF_EXTENDEDVERBS)
cmp si, ax
cmovle ecx, edi ; SHIFT not held -> drop 0x100
so the patch forces CMF_EXTENDEDVERBS (0x100) onto every context menu.
Explorer's "Send To" extension is driven by exactly that flag: it then
enumerates every first-level folder under %USERPROFILE% and keeps
appending them to the submenu until its command-ID range runs out.
That is what issues std-microblock#212, std-microblock#240 and std-microblock#323 report ("Send To lists all
folders in the user profile", "extra items injected everywhere").
The ExplorerFrame patch above - the one keyed on both VK_SHIFT and
VK_F10 - is what actually makes Explorer use the classic HMENU context
menu. The shell32 patch only adds CMF_EXTENDEDVERBS and contributes
nothing to that goal, so gate it behind a new `patch_shell32_dll`
option and leave it disabled by default.
Verified locally on Windows 11 26100 by restoring the original 12 bytes
at shell32 RVA 0x2B212D in a live explorer.exe: the Send To submenu goes
back to the real entries only, and no other context menu item disappears.
Owner
ExplorerFrame.dll 的 patch 是用于侧边栏的,这个patch是用于正常菜单的,不能直接删掉啊 |
Owner
现在应该已经有一个方式来补充新增的菜单了...? 我看看吧 |
Owner
|
这两个问题我自己看下吧,感谢 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复「发送到」子菜单的两个独立缺陷。
1. 展开「发送到」时子菜单空白(#378)
menu::construct_with_hmenu()在发出WM_INITMENUPOPUP之后立刻用GetMenuItemCount()枚举子菜单内容。但 Shell 对「发送到」「打开方式」这类子菜单是延迟填充的 —— 消息返回时它还是空的,于是渲染出一个空白子菜单。
修复方式:
menu新增handle_menu_msg/init_popup_lparam成员,construct_with_hmenu()增加
send_init_msg参数,允许「只读当前内容、不再转发」;menu_widget新增native_content_dirty标记与resync_from_native(),在update()开头重新读取原生HMENU;hooks.cc新增schedule_submenu_resync(HMENU),挂进WARN_LATE_MENU_MUTATION宏(覆盖 13 个 hook),经
post_loop_thread_task投递,使子菜单在 Shell 填完之后被重读一次。
2. 「发送到」里混进用户主目录下的所有文件夹(#212 / #240 / #323)
fix_win11_menu会给 explorer 进程里的 shell32.dll 打一处内存补丁,把整段替换成常量
mov rax, 0xffff—— 也就是让 Shell 永远认为 SHIFT 处于按下状态。紧随其后的原始代码是:
于是这个补丁会让每一个上下文菜单都带上
CMF_EXTENDEDVERBS(0x100)。而 Explorer 的「发送到」扩展正是被这个标志驱动的:它会把
%USERPROFILE%下所有一级目录(含隐藏目录)追加进「发送到」子菜单,一直填到 Explorer 分配给它的命令 ID 区间用尽为止。
实测数据(Windows 11 26100,shell32 26100.4768):
31006..31010),随后 41 项是用户主目录条目,wID
31011..31051连续递增、正好用尽;WM_INITMENUPOPUP之前GetMenuItemCount()就已经是 46 —— 说明这些多余项与Breeze 自己的转发无关;
并且其它菜单项一个都没少(「以管理员身份运行」「打开文件所在的位置」等扩展动词仍在)。
既然
CMF_EXTENDEDVERBS对这个补丁本身的目标(让 Explorer 走经典 HMENU 菜单)毫无贡献—— 真正做到这一点的是
ExplorerFrame.dll那一处(它同时判断VK_SHIFT和VK_F10,逻辑精准,本 PR 保持不动)—— 就把 shell32 这处包进新的配置项
context_menu.patch_shell32_dll,默认关闭。变更范围
8 个文件,+168 / −27:
config.json不需要任何改动即可生效(未写入的字段会落到新的默认值)。如需恢复旧行为,在
context_menu段写"patch_shell32_dll": true。验证
关于 CI
本 PR 只包含上述两处业务修复,不含任何为了让 fork 的 CI 跑起来而做的构建侧改动
(跳过未配置的 sentry secrets、绕开依赖漂移等)。若上游 master 的 CI 本身是红的,
与本 PR 无关。