如何禁用Windows编辑控件上下文菜单中的复制/粘贴命令?

6

我该如何禁用Windows操作系统本地编辑控件上下文菜单中的3个标准剪切/复制/粘贴命令?

enter image description here

我还需要禁用与剪贴板相关的命令,例如CTRL+C/CTRL+V。是否有特殊的编辑控件样式或其他可用于通过一个简单设置禁用所有复制/粘贴操作的方法?

@DavidHeffernan,这是一些最终用户的要求。在一般情况下,我们还需要将我们的自定义项添加到默认的操作系统上下文菜单中。 - TecMan
奇怪的用户!最简单的方法可能是用自己的菜单替换整个菜单。 - David Heffernan
@DavidHeffernan,您是否希望我自己实现所有这些特定的命令,例如“从右到左阅读顺序”? - TecMan
我怀疑你的用户不需要那些,对吧? - David Heffernan
这是愚蠢的。那么使用非本地GUI的应用程序呢?工具栏按钮呢?没有办法100%识别和禁用它们。 - Chris Thornton
1
闻起来像是一种误导性的尝试,旨在阻止用户粘贴密码。 - Chris Becke
4个回答

14
通常,当控件显示弹出菜单时,会生成一个WM_INITPOPUPMENU消息,该消息“允许应用程序在显示菜单之前修改菜单,而不改变整个菜单。”
不幸的是,标准的Win32编辑控件没有为其默认弹出菜单生成该消息,这在MSDN Magazine的2000年11月文章中得到了确认(MSDN本身的链接已经失效,但此链接来自Internet Archive): MSDN Magazine, November 2000, C++ Q&A:

问:为什么右键单击编辑控件时不会生成WM_INITMENUPOPUP消息?

答:我不能告诉你为什么没有,但我可以确认这是真的...编辑控件不会发送WM_INITMENUPOPUP。编辑控件必须使用空HWND句柄和/或TPM_NONOTIFY调用TrackPopupMenu,告诉菜单不要发送通知。可能(我只是猜测)作者试图通过减少消息流量来提高性能...无论如何,假设您想向编辑控件上下文菜单添加自己的菜单项。你该怎么办?唉,你别无选择,只能重新发明轮子。

因此,唯一可用的选项是对编辑控件进行子类化,并处理 WM_CONTEXTMENU 消息,根据需要创建并显示自定义弹出菜单。这意味着您必须手动复制您想要在自定义菜单中出现的任何标准菜单项的功能。

更新:事实上,有一种方法可以访问和修改编辑控件的标准弹出菜单(我刚刚测试过了,它有效)。TecMan提供了一个VBForums讨论的链接,但其中有些细节是错误的。我从PureBasic论坛讨论中得到了正确的细节。

正确的方法如下:

  1. 子类化编辑控件以拦截 WM_CONTEXTMENU 消息。可以使用 SetWindowSubClass()SetWindowLongPtr(GWL_WNDPROC),但首选第一个

  2. 接收到 WM_CONTEXTMENU 消息时,调用 SetWindowsHookEx() 安装线程本地钩子(将 0 用于 hMod 参数,将 GetCurrentThreadId() 用于 dwThreadId 参数)。可以使用 WH_CBTWH_CALLWNDPROC 钩子。然后通过 DefSubclassProc()CallWindowProc()WM_CONTENTMENU 分派到默认消息处理程序,以调用标准弹出菜单。

  3. 在钩子过程中,当收到 HCBT_CREATEWNDWH_CBT 钩子)或 WM_CREATEWH_CALLWNDPROC 钩子)通知时,将提供的 HWND 传递给 GetClassName()。如果类名为 #32768(作为MSDN文档中记录的菜单的标准窗口类名),则使用 PostMessage() 发送自定义窗口消息(非常重要!),在消息的 WPARAMLPARAM 参数中指定菜单窗口的 HWND,发送给任何您控制的 HWND,例如您的主窗口,甚至是编辑控件本身(因为它已经被子类化)。您将需要下一步中的菜单的 HWND。此时可以选择立即卸载钩子,或等待 DefSubclassProc()/CallWindowProc() 退出(在菜单被关闭后退出)。因为此时菜单窗口尚未创建其 HMENU,所以必须使用 PostMessage()PostMessage() 将延迟到下一步,直到 HMENU 准备就绪。

  4. 收到自定义窗口消息时,通过 SendMessage() 向从钩子获取的菜单的 HWND 发送 MN_GETMENU 消息。现在您拥有了菜单的 HMENU,可以随心所欲地使用它。

  5. 要禁用 CutCopyPaste 菜单项,请调用 EnableMenuItem()。它们的菜单项标识符与相应的 WM_CUTWM_COPYWM_PASTE 消息的值相同(这不是 Microsoft 记录的,但在 Windows 版本之间保持一致)。

更新:我刚刚找到了一个更简单的解决方案(在测试时也起作用)。

  1. 子类化编辑控件以拦截WM_CONTEXTMENU消息,如上所述。

  2. 当接收到消息时,调用SetWinEventHook()来安装一个线程本地事件钩子(将hmodWinEventProc参数设置为0,idProcess参数设置为GetCurrentProcessId()idThread参数设置为GetCurrentThreadId()dwFlags参数设置为0 - 而不是WINEVENT_INCONTEXT!)。将eventMineventMax参数都设置为EVENT_SYSTEM_MENUPOPUPSTART,这样它就是您接收到的唯一事件。然后将消息分派给默认处理程序以调用弹出菜单。

  3. 当调用您的事件回调函数时,菜单已经完全初始化,因此您可以向提供的HWND发送MN_GETMENU消息,它将是菜单的窗口(回调的idObject参数将为OBJID_CLIENTidChild参数将为0)。

  4. 根据需要操纵HMENU

  5. 在使用完成后取消钩子事件,如上所述。


As you can see here, this does work. 在这里可以看到,这确实有效。
Before modifying the menu: 在修改菜单之前:

before modifying the menu

禁用菜单项后:

Disabling the menu items

即使删除菜单项:

Deleting the menu items


请看我在vbforums.com上的答案链接。也许你知道更好和/或有文档记录的方法来检索编辑控件上下文菜单的句柄? - TecMan
@TecMan:获取窗口弹出菜单的一种记录方式是使用“MN_GETMENU”消息。诀窍在于获取弹出菜单的“HWND”,以便您可以获取其“HMENU”。挂钩是唯一的方法。然而,您提供的VBForums链接中提供的细节有些错误。我已经更新了我的答案,提供了正确的细节。 - Remy Lebeau
经过艰苦努力,我写出了第二种方法的 C 语言版本。这肯定应该被选为答案。 - Salvador
不错,Remy。这个方法很有效。如何删除“重新转换”菜单?它的菜单ID是什么? - RCECoder
1
@amaninlove 我不知道它的菜单ID是什么。但你可以使用GetMenuItemCount()GetMenuItemInfo()来枚举菜单项,直到找到你感兴趣的项目,然后你将拥有它的ID(和索引),以便根据需要进行操作。 - Remy Lebeau
显示剩余3条评论

0

您可以让选项保持可见,但锁定剪贴板以防止使用。
如果这个解决方案适合您,您只需要编写一个程序,通过调用OpenClipboard(NULL)打开剪贴板。为了释放剪贴板,请调用CloseClipboard()


请注意意外后果。许多应用程序都依赖于剪贴板按照文档正常工作。因此,长时间锁定剪贴板将导致冲突、崩溃等问题。您甚至不期望的应用程序,如远程桌面,也可能出现异常行为。 - Chris Thornton

0
一种方法(类似于hypmir的想法,但不那么侵入式)是每当剪贴板更新时,用“数据已被TecMan删除”覆盖剪贴板。您可以将其作为注册的剪贴板查看器执行。
打开剪贴板,清除所有格式,添加通知的CF_TEXT,然后关闭它。 我建议使用短延迟(可能是定时器回调),以便在系统上的任何其他注册剪贴板查看器处理第一个更新之后再进行更新。 结果可能因人而异。 滥用剪贴板从来都不是一个好主意。

0

我在vbforums.com上发现了一个有趣的想法,可以获取编辑控件上下文菜单的句柄:

http://www.vbforums.com/showthread.php?776385-RESOLVED-Modify-right-click-context-menu-in-standard-controls

它演示了如何向标准操作系统上下文菜单添加自定义上下文菜单项。我认为,这个想法也可以用来修改菜单。理论上,我需要枚举菜单项并禁用与复制/粘贴命令相关的项目。问题是如何知道菜单项是否与复制/粘贴相关?获取菜单项文本是一个不好的主意 ;)

该代码的另一个问题是它基于一些未记录的Windows功能。我已经检查了解决方案,在Windows 10中仍然有效,但谁知道编辑控件上下文菜单在操作系统未来的更新中可能会发生什么变化...


尽管VBForum讨论中讨论了一种获取标准编辑控件HMENU的技术,但其中一些细节是错误的。我已经更新了我的答案,提供了正确的细节。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接