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





5.00/5 (11投票s)
使用鼠标拇指按钮在代码中导航的插件
引言
Visual Studio 2010 缺乏对使用鼠标侧键在 C++, Visual Basic 和 F# 语言的源代码中进行导航的支持。本文中的插件将提供对使用鼠标侧键在源代码中进行导航的支持,并且它将展示如何在插件内部获取鼠标事件。
背景
这个插件是 Visual Studio 2008 插件的改进版本: 用于 Visual Studio 2008 (C++) 的鼠标侧键前进/后退导航。 在旧的插件中,我使用鼠标钩子来响应鼠标事件。在 Visual Studio 2010 的开发过程中,我找到了一种更简洁的解决方案来监听鼠标事件: NativeWindow 类。
用法
- 跳转到函数定义 (右键单击函数名,"转到定义")。
- 使用鼠标的"后退按钮"跳回。
实现
插件样板是使用 Addin-wizard 创建的。它创建一个名为 Connect
的类。 IDTExtensibility2
接口提供插件事件,如 OnConnect
或
。 OnDisconnec
tOnConnect
提供 DTE2 对象作为参数。
主要问题是解决如何获取鼠标事件。插件 API 仅支持键盘和高级别事件 (以及一些其他事件,如焦点)。在这个 论坛*中,我找到了一种简洁的解决方案来拦截主 IDE 窗口消息过程:NativeWindow 类。
它提供窗口过程的低级别封装。它是一个使用 SetWindowLong()
和 GWL_WNDPROC
的 WinAPI 子类化的包装器。 WndProc
从子类窗口过程调用。同一进程中的所有窗口都可以被子类化。由于该插件位于 Visual Studio 的进程中,因此此处可以使用 NativeWindow
。 NativeWindow
的实现源代码可以在此处找到。
在 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);
}
}
SubclassedWindow
在 OnConnection
方法内部创建。 WndProc
方法拦截来自主窗口的所有窗口消息。鼠标侧键的按钮释放值为 WM_XBUTTONUP
。 ActiveDocument.Language
包含当前活动文档的语言,类型为 string
。如果活动文档包含 C++、Visual Basic 或 F# 代码,则将使用 ExecuteCommand
执行 View.NavigateBackward
或 View.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 插件的初始版本