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

时间:2015-10-07 11:47:50

标签: winapi contextmenu clipboard editcontrol

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

enter image description here

我还需要禁用等效的剪贴板相关命令,如CTRL + C / CTRL + V.

是否有特殊的编辑控件样式或其他任何我们可以用一个简单设置禁用所有复制/粘贴操作的东西?

4 个答案:

答案 0 :(得分:7)

通常,当控件显示弹出菜单时,会生成WM_INITPOPUPMENU消息,“允许应用程序在显示之前修改菜单,而不会更改整个菜单。

不幸的是,标准的Win32编辑控件没有为其默认弹出菜单生成该消息,正如2000年11月MSDN杂志上的文章所证实的那样(MSDN上的链接已经死了,但是这个链接来自Internet Archive):

MSDN Magazine, November 2000, C++ Q&A

  

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

     

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

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

更新:毕竟有一种方法可以访问和修改编辑控件的标准弹出菜单(我刚测试它并且它有效)。 TecMan提供了一个VBForums discussion的链接来讨论它,但它有一些细节错误。我从PureBasic forum discussion获得了正确的详细信息。

正确的方法如下:

  1. 将编辑控件子类化以拦截WM_CONTEXTMENU消息。可以使用SetWindowSubClass()SetWindowLongPtr(GWL_WNDPROC)the first is preferred

  2. 收到WM_CONTENTMENU消息时,调用SetWindowsHookEx()安装线程本地挂钩(hMod参数使用0,GetCurrentThreadId()使用dwThreadId {1}}参数)。可以使用WH_CBTWH_CALLWNDPROC挂钩。然后通过DefSubclassProc()CallWindowProc()WM_CONTENTMENU发送到默认消息处理程序,以调用标准弹出菜单。

  3. 在钩子过程中,当收到HCBT_CREATEWNDWH_CBT钩子)或WM_CREATEWH_CALLWNDPROC钩子)通知时,传递提供的{{1 {}到GetClassName()。如果类名是HWND(菜单的标准窗口类名称,documented on MSDN),发布(非常重要!)使用PostMessage()的自定义窗口消息,在消息的#32768HWND参数中指定菜单窗口WPARAM,指定您控制的任何 LPARAM,例如主窗口,甚至编辑控件本身(因为它已经是子类)。您将需要在下一步中使用菜单HWND。您现在可以选择卸载挂钩,或者等待HWND / DefSubclassProc()退出(它将在菜单解除后退出)。您需要使用CallWindowProc(),因为此时菜单窗口尚未创建其PostMessage()。在HMENU准备好之后,PostMessage()会延迟下一步。

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

  5. 要停用HMENUCutCopy菜单项,请致电EnableMenuItem()。它们的菜单项标识符分别与PasteWM_CUTWM_COPY消息的值相同(Microsoft未对此进行记录,但在Windows版本中保持一致)。

  6. 更新:我刚刚发现了一个更简单的解决方案(在我测试时也能解决)。

    1. 将编辑控件子类化为拦截WM_PASTE,如上所述。

    2. 收到消息后,调用SetWinEventHook()安装线程本地事件挂钩(将WM_CONTEXTMENU参数设置为0,将hmodWinEventProc参数设置为{{1} } idProcess参数为GetCurrentProcessId()idThread参数为0 - 不是GetCurrentThreadId()!)。将dwFlagsWINEVENT_INCONTEXT参数都设置为eventMin,这是您收到的唯一事件。然后将消息调度到默认处理程序以调用弹出菜单。

    3. 当您的事件回调被调用时,菜单已经完全初始化,因此您可以将eventMax消息发送到提供的EVENT_SYSTEM_MENUPOPUPSTART,这将是菜单的窗口(回调的MN_GETMENU参数为HWNDidObject参数为0)。

    4. 根据需要操纵OBJID_CLIENT

    5. 使用它时取消挂钩事件挂钩,如上所述。

    6. 正如你在这里看到的,这确实有用。

      在修改菜单之前:

      before modifying the menu

      禁用菜单项后:

      Disabling the menu items

      甚至删除菜单项:

      Deleting the menu items

答案 1 :(得分:0)

您可以保留选项,但可以锁定剪贴板使用 如果此解决方案适合您,您需要做的就是通过调用OpenClipboard(NULL)创建一个打开剪贴板的程序。要释放剪贴板电话CloseClipboard()

答案 2 :(得分:0)

一种方法(类似于hypmir的想法,但不是那么具有侵入性)只是在更新时用“TecMan删除的数据”覆盖剪贴板。您可以将其作为注册剪贴板查看器执行此操作 打开剪贴板,清除所有格式,添加带通知的CF_TEXT,关闭它。 我会使用一个短暂的延迟(可能是一个计时器回调),以便您在系统上任何其他已注册的剪贴板查看器处理完第一次更新后进行更新。 您的里程可能会有所不同。 像这样滥用剪贴板永远不是一个好主意。

答案 3 :(得分:0)

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

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

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

该代码的另一个问题是它基于一些未记录的Windows功能。我已经检查了解决方案,它仍然可以在Windows 10中运行,但谁知道如何在操作系统的未来更新中更改编辑控件上下文菜单...