Visual Studio 鼠标拇指按钮导航插件开发

Visual Studio中开发插件时,经常需要考虑如何提高代码编辑的效率。本文将介绍如何开发一个插件,该插件使用鼠标拇指按钮在C++Visual BasicF#语言中进行代码导航。这个插件是Visual Studio 2008版本插件的改进版,它利用了Visual Studio 2010的新特性,提供了一种更简洁的方式来监听鼠标事件。

Visual Studio2008的旧版本插件中,使用了鼠标钩子来响应鼠标事件。在开发Visual Studio 2010版本时,发现了一个更优雅的解决方案:使用NativeWindow类。这个类提供了对窗口过程的低级封装,是一个使用SetWindowLong()和GWL_WNDPROC进行WinAPI子类化的包装器。WndProc是从子类窗口过程调用的。由于插件运行在Visual Studio进程中,因此可以使用NativeWindow。

使用方式

要使用这个插件,可以通过右键点击函数名,然后选择“Go to definition”来跳转到函数定义。使用鼠标的“后退按钮”可以返回。

实现

插件的样板是通过Addin-wizard创建的,它创建了一个名为Connect的类。IDTExtensibility2接口提供了诸如OnConnect或OnDisconnection之类的插件事件。OnConnect提供了DTE2对象作为参数。主要的问题是如何获取鼠标事件。插件API只支持键盘和高级事件(以及其他一些事件,如焦点)。在论坛上,找到了一个干净的解决方案来拦截主IDE窗口的消息过程:NativeWindow类。

在VS2010中,文本编辑器区域不再是VS2008中的子窗口,而是直接在主IDE窗口内绘制。因此,需要子类的窗口是主IDE窗口。AssignHandle(HWND_IDE_WINDOW)执行了这一步。主窗口的HWND ID存储在DTE2.MainWindow.HWnd中。

SubclassedWindow类继承自NativeWindow,如下所示:

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包含了当前活动文档的语言作为字符串。如果活动文档包含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 } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485