0

I'm trying to move the window of the program I am building inside of unity. I'm getting it's handle via interating through all processes in Process.GetProcesses(). Then, I'm calling SetWindowPos, but nothing happens. Here's my code.

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 just displays a label and shows up properly. _correctHandle is a simple bool and SetWindowPos is put in as

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

I've tried moving quite a few things to get it to work but am out of ideas. Trying to get foregroundwindow brings back a entirely incorrect handle, findwindow for name brings back 0, and quite a few other things don't seem to work. Anyone know what my error could be/

Backs
  • 24,430
  • 5
  • 58
  • 85
Charles
  • 548
  • 1
  • 7
  • 25

2 Answers2

3

p.Handle is the process' handle, not the window handle. You want p.MainWindowHandle.

It's also possible that the process you're trying to attach to has a hidden message-only main window. To figure that out, you'd need to use a tool like Spy++ to look at the structure of the windows.

If all else fails Spy++ will also give you a window class, which you can use with FindWindowEx to locate the window. (I do the same trick with Sticky Notes windows in this answer)

Community
  • 1
  • 1
theB
  • 6,450
  • 1
  • 28
  • 38
  • p.MainWindowHandle brings back 0 - would the be an indication of a hidden main window? – Charles Oct 02 '15 at 02:33
  • Does the process show up on the task bar? If not then `MainWindowHandle` won't work in this specific case. If you have Visual Studio and use Spy++, can you find the handle? If you don't have VS, supposedly [WinSpy++](http://www.catch22.net/software/winspy-17) works ok. – theB Oct 02 '15 at 02:45
  • I +1'd your solution since you sent me on the right track (new to pinvoke stuff, didn't know process ID and window ID weren't the same). I posted my solution below, and could you tell me if my uint conversion could be problematic? – Charles Oct 02 '15 at 03:29
0

The GetActiveWindow idea from this thread : Any way to bring Unity3d to the foreground? got me the right window ID, but changing resolutions still caused me issues since unity was setting the window location to halfway between both monitors after my SetWindowPos is called. Here's my solution for other people needing something like this.

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 imports of

[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);

Did update and ongui check to assure that a full redraw of unity would happen before positioning the window, since calling it at the same time a resolution change caused the window to reset to midscreen. ScreenSet is set to true right after my resolution is set.

Community
  • 1
  • 1
Charles
  • 548
  • 1
  • 7
  • 25
  • Looks Good. Glad you found a solution to the problem. :) Four minor notes: 1) `EntryPoint` for `SetWindowPos` isn't really needed, since you're matching the function name. 2) You probably want to marshal the return from `SetWindowPos` using `[return: MarshalAs(UnmanagedType.Bool)]` since `BOOL` isn't `bool` (That way you can check for errors) 3) Instead of `new IntPtr(0)` you could just use `IntPtr.Zero` (same with the comparison, `Handle == IntPtr.Zero`) 4) `System.UInt32` is the same type as `uint` but you may want to make the types match on each side, so it's clear when you come back later. – theB Oct 02 '15 at 03:50