借助网络配置,控制Modbus工具的日志显示,避免卡死界面
智能大石头 authored at 2021-12-11 22:09:19
5.47 KiB
XCoder
using System.Runtime.InteropServices;

namespace CrazyCoder.E2E;

/// <summary>Win32 辅助:窗口激活、关闭、截屏等</summary>
public static class Win32
{
    const Int32 SW_RESTORE = 9;
    const Int32 WM_CLOSE = 0x0010;

    [DllImport("user32.dll")] static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
    [DllImport("user32.dll")] static extern Boolean SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] static extern Boolean BringWindowToTop(IntPtr hWnd);
    [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")] static extern UInt32 GetWindowThreadProcessId(IntPtr hWnd, out UInt32 processId);
    [DllImport("user32.dll")] static extern Boolean AttachThreadInput(UInt32 idAttach, UInt32 idAttachTo, Boolean fAttach);
    [DllImport("kernel32.dll")] static extern UInt32 GetCurrentThreadId();
    [DllImport("user32.dll")] static extern Boolean PostMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")] static extern Boolean IsWindow(IntPtr hWnd);
    [DllImport("user32.dll")] static extern Boolean GetWindowRect(IntPtr hWnd, out RECT lpRect);
    [DllImport("user32.dll")] static extern Boolean PrintWindow(IntPtr hWnd, IntPtr hdcBlt, UInt32 nFlags);
    [DllImport("user32.dll")] static extern Int32 GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, Int32 count);
    [DllImport("user32.dll")] static extern Int32 GetClassName(IntPtr hWnd, System.Text.StringBuilder text, Int32 count);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, String lParam);
    [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

    const UInt32 EM_SETSEL = 0x00B1;
    const UInt32 EM_REPLACESEL = 0x00C2;

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

    /// <summary>把窗口恢复到前台(含线程输入附加技巧,绕过前台锁)</summary>
    public static void ForceForeground(IntPtr hWnd)
    {
        if (hWnd == IntPtr.Zero || !IsWindow(hWnd)) return;
        if (GetForegroundWindow() == hWnd) return;

        UInt32 fgThread = 0;
        GetWindowThreadProcessId(GetForegroundWindow(), out fgThread);
        var cur = GetCurrentThreadId();
        var attach = fgThread != 0 && fgThread != cur;
        if (attach) AttachThreadInput(cur, fgThread, true);

        try
        {
            ShowWindow(hWnd, SW_RESTORE);
            BringWindowToTop(hWnd);
            SetForegroundWindow(hWnd);
        }
        finally
        {
            if (attach) AttachThreadInput(cur, fgThread, false);
        }
    }

    /// <summary>关闭窗口(发送 WM_CLOSE)</summary>
    public static Boolean CloseWindow(IntPtr hWnd)
    {
        if (hWnd == IntPtr.Zero || !IsWindow(hWnd)) return false;
        return PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    }

    /// <summary>用 EM_SETSEL + EM_REPLACESEL 替换 RichTextBox 全部内容(UIA 对 RichEdit 不支持写值)</summary>
    public static void ReplaceText(IntPtr hWnd, String text)
    {
        if (hWnd == IntPtr.Zero || !IsWindow(hWnd)) return;
        // 全选:wParam=起点0,lParam=终点-1
        SendMessage(hWnd, EM_SETSEL, IntPtr.Zero, (IntPtr)(-1));
        // 替换:wParam=是否可撤销(false),lParam=文本
        SendMessage(hWnd, EM_REPLACESEL, IntPtr.Zero, text);
    }

    delegate Boolean EnumProc(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll")] static extern Boolean EnumWindows(EnumProc lpEnumFunc, IntPtr lParam);

    /// <summary>枚举指定进程的全部顶层窗口</summary>
    public static List<(IntPtr hwnd, String title, String cls)> EnumTopWindows(Int32 pid)
    {
        var list = new List<(IntPtr, String, String)>();
        EnumWindows((h, l) =>
        {
            UInt32 p;
            GetWindowThreadProcessId(h, out p);
            if (p == (UInt32)pid)
            {
                var t = new System.Text.StringBuilder(512);
                GetWindowText(h, t, 512);
                var c = new System.Text.StringBuilder(256);
                GetClassName(h, c, 256);
                list.Add((h, t.ToString(), c.ToString()));
            }
            return true;
        }, IntPtr.Zero);
        return list;
    }

    /// <summary>获取窗口矩形(屏幕坐标)</summary>
    public static RECT? GetRect(IntPtr hWnd)
    {
        RECT r;
        if (!GetWindowRect(hWnd, out r)) return null;
        return r;
    }

    /// <summary>PrintWindow 截屏窗口内容到文件</summary>
    public static Boolean CaptureWindow(IntPtr hWnd, String file)
    {
        var rect = GetRect(hWnd);
        if (rect == null) return false;
        var r = rect.Value;
        var w = r.Right - r.Left;
        var h = r.Bottom - r.Top;
        if (w <= 0 || h <= 0) return false;

        using var bmp = new Bitmap(w, h);
        using (var g = Graphics.FromImage(bmp))
        {
            var hdc = g.GetHdc();
            try
            {
                // PW_RENDERFULLCONTENT=2:捕获 DirectX/较新渲染的完整内容
                if (!PrintWindow(hWnd, hdc, 2))
                    PrintWindow(hWnd, hdc, 0);
            }
            finally
            {
                g.ReleaseHdc(hdc);
            }
        }
        var dir = Path.GetDirectoryName(file);
        if (dir != null) Directory.CreateDirectory(dir);
        bmp.Save(file);
        return true;
    }
}