【问题标题】:Moving window with SetWindowPos使用 SetWindowPos 移动窗口
【发布时间】:2015-12-30 04:31:50
【问题描述】:

我正在尝试在统一内移动我正在构建的程序的窗口。我通过对 Process.GetProcesses() 中的所有进程进行交互来处理它。然后,我调用 SetWindowPos,但没有任何反应。这是我的代码。

internal static void CheckHandle()
{
    Process[] ps = Process.GetProcesses();
    foreach (Process p in ps)
    {
        try
        {
            if (string.Equals(p.ProcessName, "TestBuild0001"))
            {
                _correctHandle = true;
                _handle = p.Handle;
            }
        }
        catch
        {
            //no catch, simply exited process
        }
    }
}

internal static void SetPosition()
{
    if (!_correctHandle)
        CheckHandle();
    if (_correctHandle)
    {
        NewGUI.SetWarning("Window set!",5,50,900,300,50);
        SetWindowPos(_handle, 0, 0, 0, 0, 0, 0x0001);
    }
}

NewGUI.SetWarning 只是显示一个标签并正确显示。 _correctHandle 是一个简单的 bool,SetWindowPos 被放入为

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern bool SetWindowPos(IntPtr hwnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

我已经尝试移动很多东西以使其正常工作,但我没有想法。尝试获取 foregroundwindow 会返回一个完全不正确的句柄,findwindow for name 会返回 0,而且还有很多其他的东西似乎不起作用。任何人都知道我的错误可能是什么/

【问题讨论】:

    标签: c# interop handle intptr


    【解决方案1】:

    p.Handle 是进程的句柄,而不是窗口句柄。你想要p.MainWindowHandle

    您尝试附加到的进程也可能具有隐藏的仅消息主窗口。要弄清楚这一点,您需要使用像 Spy++ 这样的工具来查看窗口的结构。

    如果一切都失败了,Spy++ 也会给你一个窗口类,你可以用它和FindWindowEx 来定位窗口。 (我在this answer 中对粘滞便笺窗口做同样的事情)

    【讨论】:

    • p.MainWindowHandle 带回 0 - 是否表示隐藏的主窗口?
    • 进程是否显示在任务栏上?如果不是,那么MainWindowHandle 在这种特定情况下将不起作用。如果你有 Visual Studio 并使用 Spy++,你能找到句柄吗?如果你没有 VS,据说WinSpy++ 工作正常。
    • 我为你的解决方案 +1 了,因为你把我送到了正确的轨道上(pinvoke 的新手,不知道进程 ID 和窗口 ID 不一样)。我在下面发布了我的解决方案,你能告诉我我的 uint 转换是否有问题吗?
    【解决方案2】:

    来自该线程的 GetActiveWindow 想法:Any way to bring Unity3d to the foreground? 为我提供了正确的窗口 ID,但更改分辨率仍然给我带来了问题,因为在调用我的 SetWindowPos 后,unity 将窗口位置设置为两个监视器之间的中间位置。这是我的解决方案,供其他需要类似功能的人使用。

    void FixedUpdate()
    {
        if (Handle == IntPtr.Zero)
        {
            Handle = GetActiveWindow();
            uint uP;
            GetWindowThreadProcessId(Handle, out uP);
            System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById((int)uP);
            if (string.Equals(p.ProcessName, "Unity"))
                AllowReset = false;
            else if (string.Equals(p.ProcessName, "TestBuild00001"))
                AllowReset = true;
            else
                Handle = IntPtr.Zero;
        }
    }
    
    void Update()
    {
        if (ScreenSet && AllowReset && GuiValidReset)
        {
            GuiValidReset = false;
            ScreenSet = false;
            SetPosition();
        }
    }
    
    void OnGUI()
    {
        if (ScreenSet && AllowReset && !GuiValidReset)
            GuiValidReset = true;
    }
    
    internal static void SetPosition()
    {
            SetWindowPos(Handle, 0, 0, 0, 0, 0, 0x0001);
    }
    

    DLL 导入

    [DllImport("User32.dll")]
    internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    [DllImport("user32.dll")]
    internal static extern IntPtr GetActiveWindow();
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetWindowPos(IntPtr hwnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
    

    是否更新和 ongui 检查以确保在定位窗口之前会发生完整的统一重绘,因为在调用它的同时分辨率更改会导致窗口重置为中屏。我的分辨率设置后,ScreenSet 立即设置为 true。

    【讨论】:

    • 看起来不错。很高兴您找到了解决问题的方法。 :) 四个小提示:1) SetWindowPosEntryPoint 并不是真正需要的,因为您正在匹配函数名称。 2)您可能希望使用[return: MarshalAs(UnmanagedType.Bool)] 编组来自SetWindowPos 的返回,因为BOOL 不是bool(这样您可以检查错误) 3)您可以使用new IntPtr(0) 而不是IntPtr.Zero (与比较相同,Handle == IntPtr.Zero) 4) System.UInt32uint 的类型相同,但您可能希望使两边的类型匹配,所以稍后回来时就清楚了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 2018-11-26
    • 2022-01-16
    • 2012-06-03
    相关资源
    最近更新 更多