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

VSTO Outlook:如何检测Explorer/Inspector窗口停止移动

检测Outlook窗口停止移动的实现方案

Outlook对象模型并没有提供直接的「窗口移动结束」事件,需要通过间接方式实现检测,以下是两种可行方案:


方案一:定时器轮询窗口位置(低成本实现)

核心思路

在BeforeMove事件触发时启动定时器,每隔固定短时间(如100ms)检查窗口的Left和Top属性。当连续多次检测到窗口位置不变时,判定移动停止,重置全局变量并关闭定时器。

具体实现(以VSTO C#为例)

private Timer _moveCheckTimer;
private int _stablePositionCount;
private Point _lastWindowPos;
private bool _isWindowMoving; // 你的全局变量

// 初始化事件订阅与定时器
private void InitializeHandlers()
{
    _moveCheckTimer = new Timer { Interval = 100 };
    _moveCheckTimer.Tick += MoveCheckTimer_Tick;
    
    // 订阅Explorer和Inspector的BeforeMove事件
    Globals.ThisAddIn.Application.ExplorerBeforeMove += OnExplorerBeforeMove;
    Globals.ThisAddIn.Application.InspectorBeforeMove += OnInspectorBeforeMove;
}

private void OnExplorerBeforeMove(Explorer explorer, ref bool cancel)
{
    StartMoveDetection(explorer.Window);
}

private void OnInspectorBeforeMove(Inspector inspector, ref bool cancel)
{
    StartMoveDetection(inspector.Window);
}

private void StartMoveDetection(Microsoft.Office.Interop.Outlook.Window window)
{
    _isWindowMoving = true;
    _lastWindowPos = new Point(window.Left, window.Top);
    _stablePositionCount = 0;
    _moveCheckTimer.Start();
}

private void MoveCheckTimer_Tick(object sender, EventArgs e)
{
    var activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
    if (activeWindow == null)
    {
        _moveCheckTimer.Stop();
        _isWindowMoving = false;
        return;
    }

    Point currentPos = new Point(activeWindow.Left, activeWindow.Top);
    if (currentPos == _lastWindowPos)
    {
        _stablePositionCount++;
        // 连续3次位置不变(300ms)判定为停止移动
        if (_stablePositionCount >= 3)
        {
            _isWindowMoving = false;
            _moveCheckTimer.Stop();
        }
    }
    else
    {
        _stablePositionCount = 0;
        _lastWindowPos = currentPos;
    }
}

注意事项

  • 需处理窗口意外关闭的情况,避免定时器持续运行
  • VBA环境可使用Application.OnTime模拟定时器逻辑

方案二:Windows API钩子(精准检测)

核心思路

通过Windows API设置窗口消息钩子,监听窗口的WM_EXITSIZEMOVE消息(值为0x0232),该消息会在窗口结束移动/调整大小时触发,以此精准判定移动停止时机。

具体实现(C#)

private IntPtr _moveHook;
private bool _isWindowMoving;

// Windows API声明
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

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

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

private const int WH_CALLWNDPROC = 4;
private const int WM_EXITSIZEMOVE = 0x0232;

private void OnExplorerBeforeMove(Explorer explorer, ref bool cancel)
{
    _isWindowMoving = true;
    SetMoveHook(explorer.HWnd);
}

private void OnInspectorBeforeMove(Inspector inspector, ref bool cancel)
{
    _isWindowMoving = true;
    SetMoveHook(inspector.HWnd);
}

private void SetMoveHook(IntPtr hWnd)
{
    if (_moveHook != IntPtr.Zero)
        UnhookWindowsHookEx(_moveHook);
    _moveHookProc = MoveHookCallback;
    _moveHook = SetWindowsHookEx(WH_CALLWNDPROC, _moveHookProc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
}

private IntPtr MoveHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
        CWPSTRUCT cwp = Marshal.PtrToStructure<CWPSTRUCT>(lParam);
        if (cwp.message == WM_EXITSIZEMOVE)
        {
            _isWindowMoving = false;
            UnhookWindowsHookEx(_moveHook);
            _moveHook = IntPtr.Zero;
        }
    }
    return CallNextHookEx(_moveHook, nCode, wParam, lParam);
}

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

注意事项

  • 钩子需与窗口线程关联,需处理线程安全问题
  • VBA环境可通过Declare语句调用API,但实现复杂度较高,适合对精度要求高的场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 19:16:05