如何在本机 Windows 操作系统编辑控件的上下文菜单中禁用这 3 个标准剪切/复制/粘贴命令?
我还需要禁用与剪贴板相关的等效命令,例如 CTRL+C/CTRL+V。
是否有一种特殊的编辑控件样式或其他任何我们可以使用一个简单的设置来禁用所有复制/粘贴操作的方法?
如何在本机 Windows 操作系统编辑控件的上下文菜单中禁用这 3 个标准剪切/复制/粘贴命令?
我还需要禁用与剪贴板相关的等效命令,例如 CTRL+C/CTRL+V。
是否有一种特殊的编辑控件样式或其他任何我们可以使用一个简单的设置来禁用所有复制/粘贴操作的方法?
通常,当控件显示弹出菜单时,WM_INITPOPUPMENU
会生成一条消息,“允许应用程序在菜单显示之前对其进行修改,而无需更改整个菜单。 ”
不幸的是,标准的 Win32 Edit 控件不会为其默认弹出菜单生成该消息,正如 MSDN 杂志 2000 年 11 月的一篇文章所证实的那样(MSDN 本身的链接已失效,但此链接来自Internet 存档):
Q:为什么右击编辑控件不产生WM_INITMENUPOPUP消息?
A:我不能告诉你为什么没有,但我可以确认这是真的……编辑控件不发送 WM_INITMENUPOPUP。编辑控件必须使用空 HWND 句柄和/或 TPM_NONOTIFY 调用 TrackPopupMenu,它告诉菜单不要发送通知。有可能(我只是在猜测)作者试图通过减少消息流量来提高性能......无论如何,假设您想将自己的菜单项添加到编辑控件上下文菜单中。你怎么做呢?唉,你别无选择,只能重新发明轮子
因此,唯一可用的选项是子类化编辑控件并处理WM_CONTEXTMENU
消息,根据需要创建和显示您自己的自定义弹出菜单。这意味着您必须手动复制要显示在自定义菜单中的任何标准菜单项的功能。
更新:毕竟有一种方法可以访问和修改编辑控件的标准弹出菜单(我刚刚测试过它并且它有效)。TecMan 提供了一个讨论它的VBForums 讨论的链接,但是它有一些细节错误。我从PureBasic 论坛讨论中获得了正确的详细信息。
正确的做法如下:
子类化编辑控件以拦截WM_CONTEXTMENU
消息。要么 要么SetWindowSubClass()
可以SetWindowLongPtr(GWL_WNDPROC)
使用,虽然第一个是首选。
收到WM_CONTEXTMENU
消息后,调用SetWindowsHookEx()
以安装线程本地挂钩(使用 0 作为参数hMod
和参数)。可以使用 a或hook。然后通过或调用标准弹出菜单调度到默认消息处理程序。GetCurrentThreadId()
dwThreadId
WH_CBT
WH_CALLWNDPROC
WM_CONTENTMENU
DefSubclassProc()
CallWindowProc()
在钩子过程中,当收到HCBT_CREATEWND
(WH_CBT
钩子)或WM_CREATE
(WH_CALLWNDPROC
钩子)通知时,将提供的传递HWND
给GetClassName()
. 如果类名是#32768
(菜单的标准窗口类名,如MSDN 中所述),则使用发布(非常重要!)自定义窗口消息,在消息或参数中PostMessage()
指定菜单窗口到您控制的任何窗口,例如你的主窗口,甚至是编辑控件本身(因为它已经被子类化了)。您将在下一步中需要菜单。您现在可以选择在此时卸载钩子,或等待/HWND
WPARAM
LPARAM
HWND
HWND
DefSubclassProc()
CallWindowProc()
退出(它会在菜单被关闭后退出)。您需要使用PostMessage()
,因为此时菜单窗口尚未创建HMENU
。 PostMessage()
延迟下一步直到HMENU
准备好之后。
当收到自定义窗口消息时,通过从钩子获得的菜单发送MN_GETMENU
消息。你现在有了菜单,可以用它做任何你想做的事情。SendMessage()
HWND
HMENU
要禁用Cut
、Copy
和Paste
菜单项,请调用EnableMenuItem()
。它们的菜单项标识符分别与WM_CUT
,WM_COPY
和WM_PASTE
messages 的值相同(Microsoft 没有记录,但在 Windows 版本中是一致的)。
更新:我刚刚找到了一个更简单的解决方案(当我测试它时它也有效)。
将编辑控件子类化为 intercept WM_CONTEXTMENU
,如上所述。
收到消息后,调用SetWinEventHook()
以安装线程本地事件挂钩(将hmodWinEventProc
参数设置为 0,将idProcess
参数设置为GetCurrentProcessId()
,将idThread
参数设置为GetCurrentThreadId()
,并将dwFlags
参数设置为 0 - 不是WINEVENT_INCONTEXT
!)。将eventMin
和eventMax
参数都设置为EVENT_SYSTEM_MENUPOPUPSTART
,使其成为您接收的唯一事件。然后将消息分派给默认处理程序以调用弹出菜单。
当你的事件回调被调用时,菜单已经完全初始化,所以你可以将MN_GETMENU
消息发送到提供的HWND
,这将是菜单的窗口(回调的idObject
参数是OBJID_CLIENT
,idChild
参数是0)。
HMENU
根据需要操作。
如上所述,在使用完事件挂钩后解开它。
正如您在此处看到的,这确实有效。
修改菜单前:
禁用菜单项后:
甚至删除菜单项:
我发现了一个关于如何在 vbforums.com 上获取编辑控件上下文菜单句柄的有趣想法:
它演示了如何将自定义上下文菜单项添加到标准操作系统上下文菜单中。我想,这个想法也可以用来修改菜单。理论上我需要枚举菜单项并禁用与复制/粘贴命令相关的项目。问题是如何知道菜单项是否与复制/粘贴有关?获取菜单项文本是个坏主意;)
该代码的另一个问题是它基于一些未记录的 Windows 功能。我已经检查了解决方案,它在 Windows 10 中仍然有效,但谁知道在操作系统的未来更新中如何更改编辑控件上下文菜单......
您可以使选项保持可见,但锁定剪贴板不被使用。
如果此解决方案适合您,您需要做的就是制作一个程序,通过调用打开剪贴板OpenClipboard(NULL)
。为了释放剪贴板调用CloseClipboard()
。
一种方法(类似于 hypmir 的想法,但不那么具有侵入性)是在更新时简单地用“DATA REMOVED BY TecMan”覆盖剪贴板。您可以作为注册的剪贴板查看器执行此操作。
打开剪贴板,清除所有格式,在通知中添加CF_TEXT,关闭它。我会使用短暂的延迟(可能是计时器回调),以便在系统上任何其他注册的剪贴板查看器处理第一个更新之后进行更新。
你的旅费可能会改变。
像这样滥用剪贴板绝不是一个好主意。