You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在VS扩展中通过DTE/扩展性获取Visual Studio IDE的屏幕位置与尺寸?

在VS扩展中获取IDE主窗口位置并让WinForms浮动工具栏跟随

核心思路

要获取VS IDE整个框架的位置和尺寸,关键是拿到主窗口的句柄(而非内部面板句柄),再通过Win32 API或DTE事件实现监听与位置同步。

步骤1:获取VS主窗口句柄

VS的DTE对象提供MainWindow.HWnd属性,这就是IDE主框架的窗口句柄:

EnvDTE.DTE dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE));
IntPtr mainWindowHandle = new IntPtr(dte.MainWindow.HWnd);

步骤2:获取主窗口的位置与尺寸

通过Win32 API GetWindowRect获取主窗口的屏幕坐标和尺寸,先定义必要的结构体和API导入:

using System.Runtime.InteropServices;
using System.Drawing;

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

// 获取主窗口信息
if (GetWindowRect(mainWindowHandle, out RECT mainWindowRect))
{
    int windowWidth = mainWindowRect.Right - mainWindowRect.Left;
    int windowHeight = mainWindowRect.Bottom - mainWindowRect.Top;
    Point windowLocation = new Point(mainWindowRect.Left, mainWindowRect.Top);
    // 此处已获取主窗口的位置和尺寸
}

步骤3:监听主窗口的移动与尺寸变化

方法A:使用DTE内置事件

利用DTE的WindowEvents监听主窗口的移动和resize事件,仅处理主窗口的变化:

private EnvDTE.WindowEvents _windowEvents;

// 扩展初始化时订阅事件
public void Initialize()
{
    _windowEvents = dte.Events.WindowEvents;
    _windowEvents.WindowMoved += OnMainWindowMoved;
    _windowEvents.WindowResized += OnMainWindowResized;
}

private void OnMainWindowMoved(EnvDTE.Window window)
{
    if (window.Equals(dte.MainWindow))
    {
        UpdateFloatingToolbarPosition();
    }
}

private void OnMainWindowResized(EnvDTE.Window window)
{
    if (window.Equals(dte.MainWindow))
    {
        UpdateFloatingToolbarPosition();
    }
}

// 更新浮动工具栏位置的逻辑
private void UpdateFloatingToolbarPosition()
{
    IntPtr mainWindowHandle = new IntPtr(dte.MainWindow.HWnd);
    if (GetWindowRect(mainWindowHandle, out RECT rect))
    {
        // 示例:将工具栏放在主窗口右上角外侧,偏移10px
        int toolbarX = rect.Right + 10;
        int toolbarY = rect.Top + 10;
        yourFloatingToolBarForm.Location = new Point(toolbarX, toolbarY);
    }
}

方法B:Win32消息钩子(更实时)

如果DTE事件响应不够及时,可通过Win32窗口钩子监听WM_MOVE和WM_SIZE消息,注意钩子的安装与卸载:

private IntPtr _hookHandle;
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private HookProc _hookDelegate;

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);

private const int WH_CALLWNDPROC = 4;
private const int WM_MOVE = 0x0003;
private const int WM_SIZE = 0x0005;

// 安装钩子
public void InstallWindowHook()
{
    _hookDelegate = HookCallback;
    using (Process curProcess = Process.GetCurrentProcess())
    using (ProcessModule curModule = curProcess.MainModule)
    {
        _hookHandle = SetWindowsHookEx(WH_CALLWNDPROC, _hookDelegate, GetModuleHandle(curModule.ModuleName), (uint)curProcess.Threads[0].Id);
    }
}

// 钩子回调
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
        var msg = Marshal.PtrToStructure<CWPSTRUCT>(lParam);
        if (msg.hwnd == mainWindowHandle && (msg.message == WM_MOVE || msg.message == WM_SIZE))
        {
            UpdateFloatingToolbarPosition();
        }
    }
    return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
}

[StructLayout(LayoutKind.Sequential)]
public struct CWPSTRUCT
{
    public IntPtr lParam;
    public IntPtr wParam;
    public uint message;
    public IntPtr hwnd;
}

// 卸载钩子(扩展关闭时调用)
public void UninstallWindowHook()
{
    if (_hookHandle != IntPtr.Zero)
    {
        UnhookWindowsHookEx(_hookHandle);
        _hookHandle = IntPtr.Zero;
    }
}

注意事项

  • 扩展关闭时务必取消DTE事件订阅或卸载Win32钩子,防止内存泄漏。
  • 可将WinForms窗口设置为TopMost = true,确保工具栏始终在VS窗口上方(按需调整)。
  • 测试时覆盖VS窗口移动、resize、多显示器切换等场景,确保位置同步准确。

内容的提问来源于stack exchange,提问作者Sam Marrocco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 05:03:34