Does IL2Cpp backend support Application.Unload?

I’m working on an UWP appliation with Xaml. Unity is used a library in the app. We follow this guide: [Unity - Manual: Integrating Unity into Windows applications](http:// Unity - Manual: Integrating Unity into Windows applications)

The Unity version is 2020.3.23f1. IL2Cpp is the only available backend.

When the app loads the Unity engine for the first time, everything works. The Unity engine can show a scene correctly. After a while, the app doesn’t need to show the Unity scene. The app unloads the Unity engine, using Unity - Scripting API: Application.Unload. If the app needs to show the Unity scene again, the app tries to load the Unity engine for the second time and the Unity engine fails to initialize.

Is there any way to make this work? Thanks.

Here is the code to load the Unity engine into a Xaml view.

           //This code is executed in the UWP app's UI thread
            if (AppCallbacks.Instance == null)
            {
                var appCallbacks = new AppCallbacks();
                appCallbacks.SetSwapChainPanel(RenderView);
                appCallbacks.SetCoreWindowEvents(Window.Current.CoreWindow);
                appCallbacks.InitializeD3DXAML();
            }

Here is the log with the error.

Logging to C:/Users/.../AppData/Local/Packages/dfddc6f0-b9f0-4d15-93d7-30e6a0891d1e_ce59rgwfarpmg/TempState/UnityPlayer.log
Module information:
 Built with Compiler Ver '192528614'
 Built from '2020.3/staging' branch
 Version is '2020.3.23f1 (c5d91304a876)'
 Debug build
 Application type 'XAML'
 OS 'Windows 10 (10.0.19041) 64bit'
PlayerConnection initialized from .../bin/x64/Debug/AppX/Data (debug = 0)
PlayerConnection initialized network socket : 0.0.0.0 55275
Multi-casting "[IP] 192.168.0.146 [Port] 55275 [Flags] 2 [Guid] 2782453423 [EditorId] 0 [Version] 1048832 [Id] UWPPlayerX64(hxie-laptop) [Debug] 0 [PackageName] dfddc6f0-b9f0-4d15-93d7-30e6a0891d1e_ce59rgwfarpmg [ProjectName] <no name>" to [225.0.0.222:54997]...
Started listening to [0.0.0.0:55275]
_CRT_ASSERT caught:
'''...\Il2CppOutputProject\IL2CPP\libil2cpp\vm\GlobalMetadata.cpp(1211) : Assertion failed: index >= 0 && static_cast<uint32_t>(index) <= s_GlobalMetadataHeader->genericParametersSize / sizeof(Il2CppGenericParameter)
'''
Stacktrace:
0x00007ff82a9a11e8 (UnityPlayer) DllGetActivationFactory
0x00007ff82a9a59d7 (UnityPlayer) DllGetActivationFactory
0x00007ff82a9a883b (UnityPlayer) DllGetActivationFactory
0x00007ff82a3a6791 (UnityPlayer) DllGetActivationFactory
0x00007ff838c89427 (ucrtbased) VCrtDbgReportA
0x00007ff838c88740 (ucrtbased) CrtDbgReport
0x00007ff83574c526 (GameAssembly) il2cpp_value_box
0x00007ff8357f6a39 (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff835809626 (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff83581436b (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff835808679 (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff8358086cf (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff835803e61 (GameAssembly) UseUnityPalForTimeZoneInformation
0x00007ff835701b63 (GameAssembly) il2cpp_init
0x00007ff82838aa05 (UnityPlayer) DllGetActivationFactory
0x00007ff82a36667e (UnityPlayer) DllGetActivationFactory
0x00007ff82a3109aa (UnityPlayer) DllGetActivationFactory
0x00007ff82a2d0ffa (UnityPlayer) DllGetActivationFactory
0x00007ff82a309e8d (UnityPlayer) DllGetActivationFactory
0x00007ff82a32349c (UnityPlayer) DllGetActivationFactory
0x00007ff82a323398 (UnityPlayer) DllGetActivationFactory
0x00007ff8d5017034 (KERNEL32) BaseThreadInitThunk
0x00007ff8d5aa2651 (ntdll) RtlUserThreadStart

The attachment is the log when the Unity engine is loaded for the first time and then unloaded

7684360–960781–UnityPlayer_1stLoad.zip (1.5 KB)

Is GameAssembly.dll failing to unload by any chance? Could you paste the whole output from the Visual Studio “Output” window?

I created a very simple sample. Just create a unity project from 3D template and add a game object to the scene with this script. The unity engine will be unloaded in about 5s

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestReload : MonoBehaviour
{
    int frameCount = 0;
    // Start is called before the first frame update
    void Start()
    {
        frameCount = 0;
    }

    // Update is called once per frame
    void Update()
    {
        frameCount++;
        if (frameCount > 300)
        {
            Application.Unload();
            Debug.Log("Application.Unload() Invoked");
        }
    }
}

I add a button on Xaml UI, to reload the Unity Engine after the unity engine is unloaded.

void UnityReload::MainPage::m_BtnReload_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    auto appCallbacks = ref new AppCallbacks();
    appCallbacks->SetSwapChainPanel(m_DXSwapChainPanel);
    appCallbacks->SetCoreWindowEvents(Window::Current->CoreWindow);
    appCallbacks->InitializeD3DXAML();
}

The attachment is the visual studio output

7701193–964456–UnityReload_Output.zip (58.6 KB)

Looks like for whatever reason, GameAssembly.dll gets loaded before UnityPlayer.dll is loaded, which suggests to me that you are linking to it from the XAML project. Are you calling directly into that DLL from your code? If so, you’ll need to use LoadPackagedLibrary/FreeLibrary in your code so it could be unloaded together with Unity. We don’t really support reloading the engine without reloading GameAssembly.dll at this time.

The app doesn’t manually call LoadPackagedLibrary to load GameAssembly.dll. GameAssembly.dll is loaded by AppCallbacks. Does Application.Unload() unload GameAssembly.dll?

I tried the following way to get the handle of GameAssembly.dll and free the library. But it doesn’t work. It seems that something still hold GameAssembly.dll

    HMODULE handle = LoadPackagedLibrary(TEXT("GameAssembly.dll"), 0);
    FreeLibrary(handle);
    //int count = 0;
    //while (FreeLibrary(handle)) {
    //    count++; // deadlock here
    //};

    auto appCallbacks = ref new AppCallbacks();
    appCallbacks->SetSwapChainPanel(m_DXSwapChainPanel);
    appCallbacks->SetCoreWindowEvents(Window::Current->CoreWindow);
    appCallbacks->InitializeD3DXAML();

That’s the thing, in your case it’s loaded before you instantiate AppCallbacks. If you look at the VS output you sent me, GameAssembly.dll is loaded before UnityPlayer.dll which means it’s loaded by something else.

It does, unless it was loaded before Unity was initialized.

Here’s an interesting thing to check: does the project file by accidentally link to GameAssembly.lib? You can open the .vcxproj file in the text editor and look for “GameAssembly.lib”. If you find it, try removing it and see if that fixes it.

This is the magic. It works for me.

The generated visual studio project has the following configuration. After removing “$(OutDir)GameAssembly.lib” from the project and GameAssembly.dll will be loaded after UnityPlayer.dll and It is working to reload the unity engine.

   <Link>
      <AdditionalDependencies>WindowsApp.lib;$(OutDir)GameAssembly.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>

Thank you , Tautvydas-Zilys.

Great to hear you got it working!