65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Visual Studio 2010 中使用鼠标侧键进行前进/后退代码导航 (C++, Visual Basic, F#)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2010年2月8日

CPOL

2分钟阅读

viewsIcon

70948

downloadIcon

592

使用鼠标拇指按钮在代码中导航的插件

引言

Visual Studio 2010 缺乏对使用鼠标侧键在 C++, Visual Basic 和 F# 语言的源代码中进行导航的支持。本文中的插件将提供对使用鼠标侧键在源代码中进行导航的支持,并且它将展示如何在插件内部获取鼠标事件。

背景

这个插件是 Visual Studio 2008 插件的改进版本: 用于 Visual Studio 2008 (C++) 的鼠标侧键前进/后退导航。 在旧的插件中,我使用鼠标钩子来响应鼠标事件。在 Visual Studio 2010 的开发过程中,我找到了一种更简洁的解决方案来监听鼠标事件: NativeWindow 类。

用法

  1. 跳转到函数定义 (右键单击函数名,"转到定义")。
  2. 使用鼠标的"后退按钮"跳回。

实现

插件样板是使用 Addin-wizard 创建的。它创建一个名为 Connect 的类。 IDTExtensibility2 接口提供插件事件,如 OnConnectOnDisconnectOnConnect 提供 DTE2 对象作为参数。

主要问题是解决如何获取鼠标事件。插件 API 仅支持键盘和高级别事件 (以及一些其他事件,如焦点)。在这个 论坛*中,我找到了一种简洁的解决方案来拦截主 IDE 窗口消息过程:NativeWindow 类。

它提供窗口过程的低级别封装。它是一个使用 SetWindowLong() GWL_WNDPROC 的 WinAPI 子类化的包装器。 WndProc 从子类窗口过程调用。同一进程中的所有窗口都可以被子类化。由于该插件位于 Visual Studio 的进程中,因此此处可以使用 NativeWindowNativeWindow 的实现源代码可以在此处找到。

在 VS2010 中,文本编辑器区域不再像 VS2008 中那样是子窗口。它直接在主 IDE 窗口内部绘制。因此,必须子类化的窗口是主 IDE 窗口。 AssignHandle(HWND_IDE_WINDOW) 执行此步骤。主窗口的 HWND ID 存储在 DTE2.MainWindow.HWnd 中。

*: 论坛线程 IVsBroadcastMessageEvents::OnBroadcastMessage 中讨论的其他解决方案仅提供以下消息: WM_WININICHANGE, WM_DISPLAYCHANGE, WM_SYSCOLORCHANGE, WM_PALETTECHANGED, WM_PALETTEISCHANGING WM_ACTIVATEAPP

NativeWindow 派生的 SubclassedWindow 类如下所示

public class SubclassedWindow : NativeWindow
{
  private DTE2 dte;
  private const int WM_XBUTTONUP = 0x020C;
  private const int XBUTTON1 = 1;
  private const int XBUTTON2 = 2;

  public SubclassedWindow(int hWnd, DTE2 dte)
  {
    this.dte = dte;
    AssignHandle((IntPtr)hWnd);
  }

  static int HiWord(IntPtr Number)
  {
    return (ushort)((Number.ToInt32() >> 16) & 0xffff);
  }

  protected override void WndProc(ref Message m)
  {
    if (m.Msg == WM_XBUTTONUP)
    {
      if (this.dte.ActiveDocument != null &&
          (this.dte.ActiveDocument.Language == "C/C++" ||
          this.dte.ActiveDocument.Language == "Basic" ||
          this.dte.ActiveDocument.Language == "F#"))
      {
        switch (HiWord(m.WParam))
        {
          case XBUTTON1:
            {
              try { this.dte.ExecuteCommand("View.NavigateBackward", ""); }
              catch { }
            } break;

          case XBUTTON2:
            {
              try { this.dte.ExecuteCommand("View.NavigateForward", ""); }
              catch { }
            }
            break;
        }
      }
    }

    base.WndProc(ref m);
  }
}

SubclassedWindowOnConnection 方法内部创建。 WndProc 方法拦截来自主窗口的所有窗口消息。鼠标侧键的按钮释放值为 WM_XBUTTONUP ActiveDocument.Language 包含当前活动文档的语言,类型为 string。如果活动文档包含 C++、Visual Basic 或 F# 代码,则将使用 ExecuteCommand 执行 View.NavigateBackwardView.NavigateForward 。 这将触发与 VS 菜单中的“前进/后退”按钮相同的功能。

完整的插件源代码

/*
 *  MouseThumbButtonsVS2010
 * 
 *  Copyright 2010, Jochen Baier, email@jochen-baier
 *  License: The Code Project Open License (CPOL) 1.02
 * 
 *  Addin for VS2010 providing support for forward/backward navigation with the
 *  mouse thumb buttons in C++, Visual Basic and F#.
 *   
 */

using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Diagnostics;
using System.Windows.Forms;

namespace MouseThumbButtonsVS2010
{
  public class SubclassedWindow : NativeWindow
  {
    private DTE2 dte;
    private const int WM_XBUTTONUP = 0x020C;
    private const int XBUTTON1 = 1;
    private const int XBUTTON2 = 2;

    public SubclassedWindow(int hWnd, DTE2 dte)
    {
      this.dte = dte;
      AssignHandle((IntPtr)hWnd);
    }

    static int HiWord(IntPtr Number)
    {
      return (ushort)((Number.ToInt32() >> 16) & 0xffff);
    }

    protected override void WndProc(ref Message m)
    {
      if (m.Msg == WM_XBUTTONUP)
      {
        if (this.dte.ActiveDocument != null &&
            (this.dte.ActiveDocument.Language == "C/C++" ||
            this.dte.ActiveDocument.Language == "Basic" ||
            this.dte.ActiveDocument.Language == "F#"))
        {
          switch (HiWord(m.WParam))
          {
            case XBUTTON1:
              {
                try { this.dte.ExecuteCommand("View.NavigateBackward", ""); }
                catch { }
              } break;

            case XBUTTON2:
              {
                try { this.dte.ExecuteCommand("View.NavigateForward", ""); }
                catch { }
              }
              break;
          }
        }
      }

      base.WndProc(ref m);
    }
  }

  public class Connect : IDTExtensibility2
  {
    private DTE2 _applicationObject;
    private AddIn _addInInstance;
    private SubclassedWindow subclassedMainWindow;

    public void OnConnection(object application, 
	ext_ConnectMode connectMode, object addInInst, ref Array custom)
    {
      _applicationObject = (DTE2)application;
      _addInInstance = (AddIn)addInInst;
      subclassedMainWindow = new SubclassedWindow
	(_applicationObject.MainWindow.HWnd, _applicationObject);
    }

    public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
    {
      if (subclassedMainWindow != null)
      {
        subclassedMainWindow.ReleaseHandle();
        subclassedMainWindow = null;
      }
    }

    #region not_used
    #endregion
  }
}

限制

只有当文本编辑器“窗口”停靠时,该插件才能工作。

历史

  • 基于 VS 2008 插件的初始版本
© . All rights reserved.