[Solved] XAMLUnityConnection in IL2CPP : Xaml C# <-> IL2CPP

Hi,

I can’t find any documetation about how to create communication Unity ↔ XAML when scripting backend is il2CPP.

My XAML host is also in C#.

Any idea about how to achieve the communication please ?

You’ll have to create a C++ interface with the communication functionality you need, implement it in C# on either IL2CPP side or VS side, then pass it to the other side through C++ and finally call methods on it.

I assume you have Unity side and the XAML side working in the same process except the communication part?

Yes we already have an il2cpp export of a project that is hosted in XAML using C#.
We can’t find a way of doing the communication IN and OUT.

Could you provide an example of it please ?

Sure.

Make a windows runtime component with this code:

namespace IL2CPPToDotNetBridge
{
   public interface class IDotNetBridge
   {
   public:
       void MyFunction1();
       void MyFunction2(Platform::String^ arg);
   };

   public interface class IIL2CPPBridge
   {
   public:
       void MyFunction3();
       void MyFunction4(int arg);
   };

   public ref class BridgeBootstrapper sealed
   {
   public:
       static IDotNetBridge^ GetDotNetBridge()
       {
           return m_DotNetBridge;
       }

       static void SetDotNetBridge(IDotNetBridge^ dotNetBridge)
       {
           m_DotNetBridge = dotNetBridge;
       }

       static IIL2CPPBridge^ GetIL2CPPBridge()
       {
           return m_IL2CPPBridge;
       }

       static void SetIL2CPPBridge(IIL2CPPBridge^ il2cppBridge)
       {
           m_IL2CPPBridge = il2cppBridge;
       }

   private:
       static IDotNetBridge^ m_DotNetBridge;
       static IIL2CPPBridge^ m_IL2CPPBridge;

       BridgeBootstrapper();
   };

   IDotNetBridge^ BridgeBootstrapper::m_DotNetBridge;
   IIL2CPPBridge^ BridgeBootstrapper::m_IL2CPPBridge;
}

Compile it for all CPU architectures you care about, drop the DLLs into your Unity project, and also drop one .winmd file into your Unity project (doesn’t matter which one - they will all be identical).

Then in Unity, do this:

using UnityEngine;

#if ENABLE_WINMD_SUPPORT

using IL2CPPToDotNetBridge;

class IL2CPPBridge : IIL2CPPBridge
{
    public void MyFunction3()
    {
        Debug.Log("Inside MyFunction3");
    }

    public void MyFunction4(int arg)
    {
        Debug.Log("Inside MyFunction4: " + arg);
    }
}

#endif

public class Script : MonoBehaviour
{
    void Awake()
    {
#if ENABLE_WINMD_SUPPORT
        BridgeBootstrapper.SetIL2CPPBridge(new IL2CPPBridge());
#endif
    }

    void Start()
    {
#if ENABLE_WINMD_SUPPORT
        var dotnetBridge = BridgeBootstrapper.GetDotNetBridge();
        dotnetBridge.MyFunction1();
        dotnetBridge.MyFunction2("Hello from il2cpp");
#endif
    }
}

On VS side, do this on startup:

using IL2CPPToDotNetBridge;

class DotNetBridge : IDotNetBridge
{
    public void MyFunction1()
    {
        var il2cppBridge = BridgeBootstrapper.GetIL2CPPBridge();
        il2cppBridge.MyFunction3();
        il2cppBridge.MyFunction4(535);
    }

    public void MyFunction2(string arg)
    {
        Debug.WriteLine("Inside MyFunction2: " + arg);
    }
}

static void Main(string[] args)
{
    BridgeBootstrapper.SetDotNetBridge(new DotNetBridge());

    ....
}

And you’re done! You should get this output upon success:

Inside MyFunction3
UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
IL2CPPBridge:MyFunction3()
IL2CPPToDotNetBridge.IDotNetBridge:MyFunction1()
Script:Start()
(Filename: C:\buildslave\unity\build\artifacts/generated/Metro_IL2CPP/runtime/DebugBindings.gen.cpp Line: 51)

Inside MyFunction4: 535
UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
IL2CPPBridge:MyFunction4(Int32)
IL2CPPToDotNetBridge.IDotNetBridge:MyFunction1()
Script:Start()
(Filename: C:\buildslave\unity\build\artifacts/generated/Metro_IL2CPP/runtime/DebugBindings.gen.cpp Line: 51)

Inside MyFunction2: Hello from il2cpp

Thank you,

It looks good, I am going to test this :slight_smile:

Hi,

I have implemented the solution and everything is fine. Thanks for the tip :slight_smile:

Hi,T@Tautvydas-Zilys.

I have done a project in UWP with .NET scripting backend. So now I need to do it using IL2CPP scripting backend. So without changing that, for testing I created a new unity uwp project using IL2CPP backend and another UWP C# visual studio project. So if there is way to communicate between those two projects (show unity screen in a part of my XAML view and display something on it by addressing unity functions from MainPageXaml.cs), I can develop the rest of my project using IL2CPP backend scripting. So can you please state the steps of doing this one?

Did the steps I posted above not work for you for communication?

Thank you for your quick response. I think your code can do the thing I want. But I want to clarify few things. These are the tasks I have done so far.

  1. Created a unity project and built it with IL2CPP backend scripting for UWP.
  2. Then created UWP c# project in Visual studio and copied the generated files into the above project’s UWP part.
  3. Then created the cpp interface with the given code on a windows run-time component.
  4. And run it and copied the generated dll files and .winmd file into my unity project. (into “Assets/Scripts/” folder ).
  5. Then added the unity code in Script.cs file in unity Scripts folder.
  6. Then created c# code in VS and added the given code there.

As well as there was an error:

" Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point. UWPCpp F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\MainPage.xaml.cs 35 Active
"’

So I added this part " BridgeBootstrapper.SetDotNetBridge(new DotNetBridge());" in MainPage block and run it.

My code:

namespace UWPCpp
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {

        public MainPage()
        {
            this.InitializeComponent();
            BridgeBootstrapper.SetDotNetBridge(new DotNetBridge());
            // BridgeBootstrapper.SetDotNetBridge(new DotNetBridge());

        }



        class DotNetBridge : IDotNetBridge
        {
            public void MyFunction1()
            {
                Debug.WriteLine("I'm FUnction1");
                var il2cppBridge = BridgeBootstrapper.GetIL2CPPBridge();
                il2cppBridge.MyFunction3();
                il2cppBridge.MyFunction4(535);
            }

            public void MyFunction2(string arg)
            {
                Debug.WriteLine("I'm FUnction2");
                Debug.WriteLine("Inside MyFunction2: " + arg);
            }

        }
    }

}

But I got a result as below. Couldn’t get that debug statments.

Result:

"

'UWPCpp.exe' (CoreCLR: DefaultDomain): Loaded 'C:\Program Files\WindowsApps\Microsoft.NET.CoreRuntime.1.1_1.1.27004.0_x64__8wekyb3d8bbwe\System.Private.CoreLib.ni.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\entrypoint\UWPCpp.exe'. Symbols loaded.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Runtime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Program Files\WindowsApps\Microsoft.NET.CoreRuntime.1.1_1.1.27004.0_x64__8wekyb3d8bbwe\mscorlib.ni.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\WinMetadata\Windows.winmd'. Module was built without symbols.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Runtime.InteropServices.WindowsRuntime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Runtime.WindowsRuntime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Numerics.Vectors.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Runtime.WindowsRuntime.UI.Xaml.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\Microsoft.UI.Xaml.Markup.winmd'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Threading.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Collections.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Reflection.dll'. Module was built without symbols.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Threading.Tasks.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Reflection.Extensions.dll'. Module was built without symbols.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Linq.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\IL2CPPToDotNetBridge.winmd'. Module was built without symbols.
'UWPCpp.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'F:\Malinda\UWP_Projects\Test\NewProject\UWPCpp\bin\UWPCpp\bin\x64\Debug\AppX\System.Private.Uri.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The thread 0x2f94 has exited with code 0 (0x0).
The thread 0x104c has exited with code 0 (0x0).

"

Try switching to “Mixed Mode” debugging in Visual Studio Project Properties, Debugging tab.

By the way, does Unity get initialized properly?

@Tautvydas-Zilys I am trying to integrate an UWP C# app with a UWP app built from Unity IL2CPP (as .net is deprecated)

In the below question do you mean that integration part? Before following your steps to get the communication part working, I think first I need to put my C# uwp project and the Unity IL2CPP built C++ uwp project in to one project.

It will be really helpful if you can provide the exact steps I have to follow to do that or any reference to such guide.

My requirement is I have a C# UWP project coded in VS. Now I need to open 3D models inside that app, so need to include Unity inside my UWP project.

Thank you.

OK I’ll try it. I have spent about three days to solve this issue. So I really appreciate your contribution. I wanted to clarify whether the above procedure is correct or wrong. What you have meant by Unity initialization?
I have created the UWP project with IL2CPP scripting backend, and now I have created another C# UWP project just using visual studio(not by Unity). Then I copied those files also into the project folder of the first project. Then I followed your procedure. But I think the reason for getting a null instance of that bridge is lack of any connection between unity and C# UWP project. I think they: [UWP C# prject and UWP CPP Unity project] work as seperate projects. Finally my target is to load a unity scene on a section of my C# UWP app.

I made an example C# project with IL2CPP scripting backend here: GitHub - TautvydasZilys/unity-uwp-il2cpp-with-csharp-project-example: This project demonstrates how to use IL2CPP scripting backend when you want final UWP application to use a C# project.

By asking whether Unity got initialized - I meant does your game actually start?

Thanks for your quick response. Yes, I think that the reason for this issue, when I run the project built from unity with IL2CPP backend scripting, I can see the unity scene in my UWP app. But it can not be seen in my C# project. Because it’s just a UWP project built from VS.

Can you tell me the steps of creating a project like this “https://github.com/TautvydasZilys/unity-uwp-il2cpp-with-csharp-project-example

Another thing is I used this example project and included the codes you provided there and tested the output result. But again I got the same output, (Null Reference Exception) when using the below statement to call unity methods directly.

            Debug.WriteLine("This is just for testing!");
            bg.MyFunction1( );

Well, just look inside the source code for that project and the project files itself. There’s code to initialize Unity. And then there’s also references to Unity components in the project file.

https://github.com/TautvydasZilys/unity-uwp-il2cpp-with-csharp-project-example/blob/master/bin/UWP_IL2CPP_WithCSharpProject_Example/UWP_IL2CPP_WithCSharpProject_Example_CSharpProject.csproj
https://github.com/TautvydasZilys/unity-uwp-il2cpp-with-csharp-project-example/blob/master/bin/UWP_IL2CPP_WithCSharpProject_Example/App.xaml.cs
https://github.com/TautvydasZilys/unity-uwp-il2cpp-with-csharp-project-example/blob/master/bin/UWP_IL2CPP_WithCSharpProject_Example/MainPage.xaml.cs

When I call a method in Unity script,

            BridgeBootstrapper.SetDotNetBridge(bg = new DotNetBridge());
            Debug.WriteLine("This is just for testing!");
            bg.MyFunction2("Malinda");
           bg.MyFunction1();

Why do I receive a Null Pointer Exception? I think if I can find a solution for that one, the whole problem can be solved.

Which line causes it? And how is BridgeBootstrapper defined?

I just use that Debug statements to check whether DotNetBridge() is NULL or not. Your code snippets are used in the rest of the code. Problem is occurred in line NUmber 4.

Perhaps the exception is thrown from inside that function?