Search Unity

Question Unity as a Library - DLL in WPF - how to unload/quit?

Discussion in 'Windows' started by Chrueschsch1e, Oct 17, 2022.

  1. Chrueschsch1e

    Chrueschsch1e

    Joined:
    Jan 28, 2020
    Posts:
    16
    Hello!

    We currently embed the Unity exe into our parent WPF app with the command line argument -parentHWND.
    Because of reasons, we would like to switch to embedding Unity as a DLL directly, by loading the UnityPlayer.dll from code into our host application. Loading, embedding, and starting a Unity scene with the DLL works.
    But:
    We have tremendous difficulties with unloading/closing Unity and closing (only) the window where it is embedded.

    A short explanation about the setup we want to have in the end:
    The host application has a main window. Then the user can open another window (let’s call it ‘3D window’) that, among other WPF elements, displays the Unity frame. The user needs to be able to close and reopen the 3D window, without the main window closing/ending. Booting Unity from scratch, every time the 3D window is opened, is OK.
    We currently use Unity v. 2021.3.9f1 (LTS).

    These are the things we tried and the problems we encountered:
    First, we tried Unity’s Application.Unload(), but found out – after some time – that it only seems to be supported for iOS and UWP host applications.
    Problem 1: Our host application is a WPF application, so Unload() does exactly nothing.

    Secondly, we tried Unity's Application.Quit(), but:
    Problem 2: Quit() takes down the whole host application with it – not only the 3D window, but also the main window.

    Thirdly, we tried unloading the DLL manually using FreeLibrary(IntPtr hModule).
    Interestingly, if we call FreeLibrary() exactly four times, Unity actually closes.
    Problem 3: Why four times? Unity seems to increase its reference counter internally when being loaded as DLL, but we have no insight into what actually happens under the hood.
    Problem 4: Unloading yields the same behaviour as Application.Quit(): it takes down the whole host application with it.
    Problem 5 (Bonus): We cannot close the 3D window with the red Windows X on the top right. Nothing happens, if we click it.

    Main question:
    Are there any alternatives to Application.Unload()/.Quit() when embedding Unity as DLL in a WPF host application?
    Or generally, is it even possible to stop/unload/terminate the running Unity DLL, without killing the whole application?

    Any help is greatly appreciated!
    Greetings,
    Christian
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,674
    Problem 2: Unfortunately, there is no good way of achieving what you are trying to while hosting Unity in the same process. Currently in Windows Standalone player, we call "TerminateProcess(GetCurrentProcess())" right at the end of "UnityMain()", so any kind of thing that would make Unity to not run anymore will terminate the whole app. The reason for this is complicated and while we have plans to address it, it wouldn't be for a while and such a change wouldn't be backported to earlier supported releases.

    Problem 3: No idea, but doing that is a terrible idea.

    Problem 4: I assume the application crashes if you do that. You cannot unload code that is running. Did you try running it under a native debugger?

    Problem 5: When you use -parentHwnd, Unity doesn't control the parent window. So it's hard to tell what's going on.

    The only way I think would work for you would be to suspend Unity's execution, reparent the window to "nothing" and hide it. Then, when you need to show Unity again, create a new parent window, reparent Unity's window to that parent and resume the execution.
     
    Chrueschsch1e likes this.
  3. Chrueschsch1e

    Chrueschsch1e

    Joined:
    Jan 28, 2020
    Posts:
    16
    Hi Tautvydas-Zilys and thanks a bunch for the insights. It's a little clearer now why the Unity DLL behaves the way it does.

    That is a very interesting suggestion and would indeed work for our project. I have two follow-up questions:

    Question 1: You suggested to "suspend Unity's execution". Is there a built-in functionality for that? Or did you mean to implement our own "pausing"-mechanism with the means of Time.timeScale=0, or loading an empty scene, for example?

    Question 2: You also suggested to "reparent Unity's window". We never thought this would be possible. How can we do that? The -parentHWND + window handle parameters are handed to the UnityMain() method. But we cannot call UnityMain() a second time, can we? How do we get the new window handle delivered to Unity? I feel like we are maybe not seeing the forest for the trees here...
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,674
    Nothing fully built in, but those ideas are viable. You could set up a manual reset event that you wait on with MsgWaitForMultipleObjects (so that window message pump is still pumped) every frame and when you want to pause Unity, you just unset that event so the game will essentially "deadlock" until you resume it.

    You can just call the SetParent Windows API function. "-parentHWND" does just that (and also changes the Unity window style so that it doesn't have a title bar, etc).
     
    Chrueschsch1e likes this.