Hi all,
Early in the launch of my game (in player, not editor) I need to spawn Blender to act as a backend to my game…
Unfortunately I was not able to find a way to start Blender hidden or minimized and when I spawn that process while the player loads my game, windows activates Blender and my user needs to Alt-Tab back to Unity Player to play…
Is there a way to re-activate the Unity player window? (Say by obtaining the window handle of it and calling a user32 command?)
Many thanks!
Hi guys figured this out…
First remember Unity’s HWND early in your game init when you know it has the focus:
_hWnd_Unity = (IntPtr)GetActiveWindow(); //###NOTE: Obtain the HWND of our Unity editor / player window. We will need this to re-activate our window. (Starting blender causes it to activate and would require user to alt-tab back to game!!)
Later on when you need to regain focus call:
SetForegroundWindow(_hWnd_Unity); // Set our editor / player back into focus (away from just-spawned Blender)
With these user32 function defined:
[DllImport(“user32.dll”)] static extern uint GetActiveWindow();
[DllImport(“user32.dll”)] static extern bool SetForegroundWindow(IntPtr hWnd);
4 Likes
This is excellent. I was trying hard to find a good solution. But, your solution worked perfectly well
Just to optimize it a bit more, (For a kiosk mode application) we should focus a window only when it goes out of focus.
Cameron’s answer describes it very well here
Yes this solution is great, the system32 way of doing it. Since it’s low level you just want to be cautious about it if you’re deploying to certain platforms.
This solution isn’t great, it’s flaky.
If you launch the game and just focus another window while the splash screen is running or while it’s still starting up - you will get the hwnd of a different window. Might be good enough for some personal tool or a game jam game where 10 people run it, but this is not a robust solution anyone should ship with.
Unity needs to expose the hwnd, or better yet: fix whatever is causing Process.GetCurrentProcess().MainWindowHandle to be 0 perpetually.
Alright, I managed to scrape together a working solution.
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("Kernel32.dll")]
static extern int GetCurrentThreadId();
static IntPtr GetWindowHandle()
{
IntPtr returnHwnd = IntPtr.Zero;
var threadId = GetCurrentThreadId();
EnumThreadWindows(threadId,
(hWnd, lParam) => {
if(returnHwnd == IntPtr.Zero) returnHwnd = hWnd;
return true;
}, IntPtr.Zero);
return returnHwnd;
}
I can then use both of these functions I want to use:
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
[DllImport("user32.dll", EntryPoint = "SetWindowText")]
public static extern bool SetWindowText(System.IntPtr hwnd, System.String lpString);
For any questions on what parameters to pass to those functions, see microsoft’s documentation on them.
Edit: User Bunny83 has a good note on using lambdas with yhese callbacks here: How do you reliably the hwnd (window handle) of the game's own window? - Unity Engine - Unity Discussions
Basically: Lambdas in native callbacks will break with il2cpp
The issue is tracked and can be voted on here: