vs extension 监听键盘输入

背景

有一个这样的业务需求:我们希望监听用户在编写代码时的键盘事件

基本分析

part1

如果把vs看做一个桌面应用程序,那么我们就是希望介入到这个窗体应用的键盘事件处理过程
当然我直观上认为vs不会直接把键盘事件处理接口暴露给我

这一个观点在我验证jaredpar大佬的经验分享1的时候得到了一定验证,我还没有找到窗体键盘处理事件的扩展点

part2

接下来,我尝试寻找其他方式来直接或者间接的方式得到键盘事件,这时候我得到了第一种方案,我通过监听代码文件对应的文档对象的内容变化事件来确认键盘事件,因为我们的编码过程,每一次键盘敲击的主要目的都是修改代码文本。

这部分的内容可以通过 EnDTE.DTE2.events.DocumentEvents 暴露出来的接口来实现

但是上面的方案存在着一定问题,我们的一次键盘事件可能对应着一段输入(参考tab键,copy paste,使用中文输入法输入中文),或者多次键盘事件对应着一次输入(使用中文输入法输入中文和组合键),所以上面的方案存在着缺陷

part3

而后我找到了jaredpar大佬的经验分享1,我对vs处理键盘事件有了少量的理解,下面摘录大佬的分享的原话。

Let’s start off with a high level view of the events and systems and the order in which they
fire
  1. PreTranslateMessage – Native windows messages.  May handle and swallow a message
  2. WPF and TextCompositionManager
     a. KeyDown
     b. TranslateAccelator
     b. TextInput
 
Now how the components in Visual Studio play together to route key input
 
  1. PreTranslateMessage – This is handled in msenv.  It has the first go and will attempt 
     to map keyboard input into a key boarding binding for a Visual Studio command.  It 
     will only attempt to map non-alphanumeric input or alphanumeric input which has a 
     modifier such as Shift, Control, Alt.  If it can find a match then it uses the key 
     stroke otherwise it gets passed on goes to #2
  2. WPF
    a. VisualElement raises the KeyDown.  KeyProcessor’s now come into play as they 
       indirectly subscribe to this event which they can interpret and handle. 
    b. Translate Acessorator goes through the IVsChain.  This causes problems because certain
       components (IVsFilterKeysAdapter) will intercept and swallow the keystroke.  
    c. TextCompositionManager receives the message and after much magic raises the 
       TextInput event on KeyProcessors. 
      i. Note: How the key is entered changes which field on TextComposition has the text.  
         For normal printable input the text will be on the Text property.  For items like 
         CTRL-M it will be on the ControlText property. 
      ii. One of the key processors is VsKeyProcessor which will turn every character in the 
        Text property of the argument into a TYPECHAR command. 
 
 Another way to look at it from a component perspective from when a message is recieved 

  - msenv::ProcessMessage
    - msenv::SCM_MsoStdCompMgr::FPreTranslateMessage
      - msenv::CMsoComponent::FPreTranslateMessage
        - msenv::CMsoComponent::MainFTranslateMessage
          - IVsWindowManager::TranslateAccelerator
            - IVsCodeWindow::PreTranslateMessage
              - IVsFilterKeysAdapter::TranslateAccelerator
          - Visual Studio Commands.
    - IVsWindowManager::PreTranslateInput
      - System.Windows.Interop.ComponentDispatcher.RaiseThreadMessage
         - TextCompositionManager 
            - KeyProcessor.KeyDown
            - KeyProcessor.TextInput
      - If that returns true then done
  - TranslateMessage
  - DispatchMessage

这里把关注点放在最下面的接口调用过程上
从 msenv::ProcessMessage至 IVsFilterKeysAdapter::TranslateAccelerator,我在官方文档上面没有找到直接的扩展点,而Commands可以尝试通过EnDTE的DTE.events.CommandEvents接口来实现监听,这里可以直接捕获到一些编辑命令(copy paste save cut redo undo)

而后续过程中的KeyProcessor,可以通过MEF的方式,以实现IKeyProcessorProvider接口的形式扩展,这部分的内容可以参考大佬们再论坛里的讨论2.

part4

我在检索的时候找到了微软团队在github上公开的vs extension样例项目,其中Typing_Speed_Meter项目3中用到监听键盘事件相关的操作,这个与之前的思路不太一样,用的是Io1eCommand命令,来过滤一部分的键盘输入事件。

方案验证

最后验证了keyProcessor 和 Io1eCommand 两种方式

##keyProcessor 方案
无法监听到退格和回车按钮的keyDown事件
Ctrl组合按键的输入字符事件会被吞掉
在遇到输入法的时候,事件收到的内容是ImeProcessed

##Io1eCommand 方案
这部分是基于代码窗口的textView监听命令事件,但是由于时间派发机制的问题,转发到这一层的事件只有文本编辑相关的,copy paste和cut相关的命令还是收不到。
这部分可以监听到退格和回车事件

解决方案

由于一些客观上的原因,这里最终选择一种有损的接收方式,我通过keyProcessor 监听普通的键盘事件,而后通过Io1eCommand 方式监听空格和回车事件,在此之外通过CommandEvents接口监听常见的命令事件


  1. https://github.com/jaredpar/VsVim/blob/master/Src/VsVim/KeyboardInputRouting.txt ↩︎ ↩︎

  2. https://social.msdn.microsoft.com/Forums/sqlserver/en-US/9f0798d5-99a3-43be-91f9-dd09c183bb7b/catching-keyboard-input?forum=vsx ↩︎

  3. https://github.com/Microsoft/VSSDK-Extensibility-Samples/tree/master/Typing_Speed_Meter ↩︎

猜你喜欢

转载自blog.csdn.net/u010953266/article/details/84627219