C# 使用SetWindowPos移动窗口

C# 使用SetWindowPos移动窗口,c#,interop,handle,intptr,C#,Interop,Handle,Intptr,我正在尝试移动我在unity内部构建的程序的窗口。我通过在Process.GetProcesses()中的所有进程进行交互来获得它的句柄。然后,我给SetWindowPos打电话,但什么也没发生。这是我的密码 internal static void CheckHandle() { Process[] ps = Process.GetProcesses(); foreach (Process p in ps) { try {

我正在尝试移动我在unity内部构建的程序的窗口。我通过在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,并且许多其他东西似乎都不起作用。任何人都知道我的错误可能是什么/

p.Handle
是进程句柄,而不是窗口句柄。您需要
p.MainWindowHandle

您试图附加到的进程也可能只有一个隐藏的消息主窗口。要了解这一点,您需要使用Spy++之类的工具来查看windows的结构


如果所有其他操作都失败,Spy++还将为您提供一个窗口类,您可以使用它与
FindWindowEx
一起定位窗口。(我对中的便笺窗口使用相同的技巧)

此线程中的GetActiveWindow思想:为我获得了正确的窗口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检查,以确保在定位窗口之前会完全重新绘制unity,因为同时调用它会导致窗口重置为中屏幕。屏幕集在设置分辨率后立即设置为true。

p.MainWindowHandle返回0-是否表示隐藏的主窗口?进程是否显示在任务栏上?如果没有,则在这种特定情况下,
MainWindowHandle
将不起作用。如果您有Visual Studio并使用Spy++,您能找到句柄吗?如果你没有VS,应该可以正常工作。自从你把我送到正确的轨道上(pinvoke新手,不知道进程ID和窗口ID不一样),我+1推荐你的解决方案。我在下面发布了我的解决方案,你能告诉我我的uint转换是否有问题吗?看起来不错。很高兴你找到了解决问题的办法。:)四个小提示:1)
SetWindowPos的
EntryPoint
实际上并不需要,因为您正在匹配函数名。2) 您可能希望使用
[return:marshallas(UnmanagedType.Bool)]
封送来自
SetWindowPos
的返回,因为
Bool
不是
Bool
(这样您可以检查错误)3)而不是
新的IntPtr(0)
您可以只使用
IntPtr.Zero
(与比较相同,
Handle==IntPtr.Zero
)4)
System.UInt32
的类型与
uint
的类型相同,但您可能需要使每侧的类型匹配,以便稍后回来时可以清楚地看到。