1

I'm having problems with changing a window's transparency.

What I'm trying to accomplish

I develop a WPF application, which has some interaction with another application (a game developed in Unity3D). My goal is to "integrate" the game into my WPF app to make it look like the game is a natural part of the WPF app. So when I move my main window, I use PInvoke to move the game's window, too, I use PInvoke to hide the window borders of the game window etc. This all works decently.

However, a problem arises when I try to hide the game without closing it. My first approach was to use PInvoke to set the game window as hidden:

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hwnd, int show);

const int SW_HIDE = 0;
const int SW_Show = 5;

ShowWindow(Handle, SW_HIDE);

Using this function call the window disappeares, but as a consequence, the CPU usage of the game process jumps from 1% to 20%. So far, I have no clue why that is. Maybe it's related to Unity, maybe not. (CPU goes back to normal when I show the window again, though).

My second approach was not to hide the window using the ShowWindow-call but to set the window's opacity to 0 (as suggested in this thread). On my setup, this works just fine (without the CPU going crazy), but on other setups, the window will stay visible.

Updating the graphics driver solves the issue sometimes, but not always. I have an old Vista machine where everything works, and I also tried it on newer Dell Laptops with Windows 10 installed, where it does not work. I don't have the issue on my Mac with Windows 10 and Boot Camp. So I have no clue what causes this problem.

Did anyone experience similiar issues? Is there another way to hide a window or does anyone know why CPU goes insane when using the ShowWindow()-method?

Any hint is much appreciated.

EDIT

Would be nice to know why this post is downvoted so much, but okay. I think especially the CPU usage thing is an interesting issue and I am terribly sorry if someone thinks it's obvious and I am a moron.

For those who are also interested, here is a minimal WPF reconstruction of my problem:

MainWindow.xaml

<Window x:Class="SampleWpfApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="1024" Width="1280"
    KeyDown="MainWindow_OnKeyDown">
<Grid>
</Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    const int SW_HIDE = 0;
    const int SW_SHOW = 5;

    private Process gameProcess;
    private IntPtr gameWindowHandle;

    private bool windowHidden = false;

    public MainWindow()
    {
        InitializeComponent();

        this.SizeChanged += OnSizeChanged;
    }

    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        StartGame();
    }

    private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Space)
        {
            if (windowHidden)
            {
                NativeMethods.ShowWindow(gameWindowHandle, SW_HIDE);
                windowHidden = false;
            }
            else
            {
                NativeMethods.ShowWindow(gameWindowHandle, SW_SHOW);
                windowHidden = true;
            }
        }
    }

    private void StartGame()
    {
        var mainWindowHandle = new  WindowInteropHelper(this).Handle;
        var processStartInfo = new ProcessStartInfo(@"Path\To\SampleUnityApp.exe");
        processStartInfo.Arguments += "-parentHWND " + mainWindowHandle.ToInt32() + " " + Environment.CommandLine + " ";
        processStartInfo.CreateNoWindow = true;
        processStartInfo.UseShellExecute = false;

        gameProcess = Process.Start(processStartInfo);
        gameProcess.WaitForInputIdle();

        NativeMethods.EnumChildWindows(mainWindowHandle,
            (hwnd, lparam) =>
            {
                // Set the window handle of the game
                gameWindowHandle = hwnd;
                return 0;
            }, IntPtr.Zero);
    }

    static class NativeMethods
    {

        [DllImport("user32.dll")]
        internal static extern bool ShowWindow(IntPtr hwnd, int show);

        [DllImport("user32.dll")]
        internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

        internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
    }
}

The SampleUnityApp I used is just a blank Unity project with a main camera. No other game object. No code I wrote. It's created with Unity 5.3.4f1 Personal.

Screenshot of TaskManager with window visible When window is visible

Screenshot of TaskManager with window hidden enter image description here

Community
  • 1
  • 1
mbger
  • 114
  • 2
  • 8
  • Setting the opacity to 0 is *never* a better way to hide a window than actually hiding it. Does WPF not have a simple Visible property and/or a Show/Hide method for its window class? I find that rather impossible to believe. And if it does, it almost certainly calls ShowWindow internally, so this suggests that something else is wrong in your code, something that we cannot see. – Cody Gray - on strike Nov 30 '16 at 11:17
  • I don't have access to the window I want to hide. I only have the window handle which belongs to the process. Hiding a child window which I created within my WPF application would be no problem for sure. I did not say that setting the opacity to 0 is a better way, it's just the way that doesn't cause massive CPU usage for me. – mbger Nov 30 '16 at 11:23
  • 2
    Have you run this Unity application with cmd parameter that allows Unity Player to be held as a subwindow in another process? [Check it here](https://docs.unity3d.com/Manual/CommandLineArguments.html) it's `-parentHWND ` – mrogal.ski Nov 30 '16 at 11:43
  • @m.rogalski Yes I did, good hint though – mbger Nov 30 '16 at 12:57
  • The biggest problem with what you're trying to accomplish is that WPF does not operate on individual HWNDs but rather on "bitmap painting" scheme. I did some project that was running instances of Unity application as a separate tabs in WPF but i was just hiding one window behind another when tabs were switched. Maybe you can try similiar approach with your project? – mrogal.ski Nov 30 '16 at 13:39
  • 1
    Instead of having a separate app be the UI you should consider using DLL injection to actually load a MonoBehaviour in the game that adds your UI elements. If you need to do extra processing you can still do it with a external app using some form of IPC to communicate between the two. Google "Unity3d Injector" for some tools and guides on how to do it. – Scott Chamberlain Nov 30 '16 at 14:05
  • @m.rogalski Thanks, this is actually the workaround I currently use, but it is not my preferred option. The rise in CPU usage when hiding the Unity window was something I really didn't understand at all, so I thought it would be worthwile to ask about it here. Looks like I'm quite lonely with that opinion ;) – mbger Nov 30 '16 at 14:17
  • @ScottChamberlain Thanks for the suggestion. I already use anonymous pipes to communicate between both processes. The WPF app is more than just a little bit of UI surrounding the game. In fact, the game is rather a small gimmick to the whole WPF app. So if I understood you correctly, your approach would mean too many changes to the whole structure at this point of time. Nevertheless I will have a look at unity3d injectors. – mbger Nov 30 '16 at 15:33
  • Could you minimize the Unity application, while having ShowInTaskbar == false? Afaik this is not the same as SW_HIDEing it, maybe Unity is more comfortable with being minimized than with being hidden. – Thomas Hilbert Dec 01 '16 at 17:38
  • @ThomasHilbert just tried it. SW_Hide, SW_ShowMinimized,SW_Minimize and SW_ShowMinNoActivate unfortunately all have the same effect on CPU. In addition, the Minimize-options leave a "SampleUnityApp"-Button in the lower left corner of the MainWindow to restore the game window, which is - at least in my case - undesirable. – mbger Dec 05 '16 at 08:45

0 Answers0