Search Unity

UWP: Get SwapChainPanel (Grid) in il2cpp xaml build

Discussion in 'Windows' started by big_3, Oct 28, 2017.

  1. big_3

    big_3

    Joined:
    Apr 20, 2016
    Posts:
    45
    Hi,

    I want to get the SwapChainPanel in my Unity project, because I want to add a XAML control to the UI.

    Code (CSharp):
    1.  
    2. #if ENABLE_WINMD_SUPPORT
    3.    var content = Windows.UI.Xaml.Window.Current.Content as Windows.UI.Xaml.Controls.Frame;
    4.    var page = content.Content as Windows.UI.Xaml.Controls.Page;
    5.    Debug.Log(content.Content.GetType());
    6.    Windows.UI.Xaml.Controls.Grid dxSwapChainPanel = page.Content as Windows.UI.Xaml.Controls.Grid;
    7. #endif
    8.  
    This work perfectly when I use the .Net scripting backend.

    However when I use the il2cpp backend the "page" variable is "null" (line 4).

    The type of "content.Content" is "System.__Il2CppComObject" and is not of type "Page" (line 5).

    How can I get the SwapChainPanel in il2cpp? As I said, in .net scripting backend this works.

    Thanks
    Jus
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    6,436
    The reason this technique doesn't work on IL2CPP is because your page is defined in raw C++ and IL2CPP has no idea what your page type is. When you call "content.Content", a COM object of type "MainPage" is returned. IL2CPP asks what its name is to figure out what type of the wrapper object it needs to create, and when it says it's "MainPage", IL2CPP goes "argh, I have no idea what it is, generic runtime callable wrapper will have to do".

    The reason it works on .NET is because the generated project is the same C# you use in Unity, and it knows what that type is because it made it. If you created the page-derived class in IL2CPP, it would work too.

    It's pretty straightforward to work around, though. We can just drop down to C++ level to do the swap chain panel retrieval operation. Put this code into a .cpp file, drop it in your Unity project and mark it as compatible with UWP:

    Code (csharp):
    1. #include <windows.ui.xaml.controls.h>
    2. #include <wrl.h>
    3.  
    4. using namespace ABI::Windows::UI::Xaml;
    5. using namespace ABI::Windows::UI::Xaml::Controls;
    6. using namespace Microsoft::WRL;
    7.  
    8. extern "C" HRESULT GetPageContent(IInspectable* frame, IUIElement** pageContent)
    9. {
    10.    *pageContent = nullptr;
    11.  
    12.    ComPtr<IContentControl> frameContentControl;
    13.    auto hr = frame->QueryInterface(__uuidof(frameContentControl), &frameContentControl);
    14.    if (FAILED(hr))
    15.        return hr;
    16.  
    17.    ComPtr<IInspectable> frameContentInspectable;
    18.    hr = frameContentControl->get_Content(&frameContentInspectable);
    19.    if (FAILED(hr))
    20.        return hr;
    21.  
    22.    if (frameContentInspectable == nullptr)
    23.        return S_OK;
    24.  
    25.    ComPtr<IUserControl> frameContent;
    26.    hr = frameContentInspectable.As(&frameContent);
    27.    if (FAILED(hr))
    28.        return hr;
    29.  
    30.    return frameContent->get_Content(pageContent);
    31. }
    In your C# script do this:

    Code (csharp):
    1. using System.Runtime.InteropServices;
    2. using UnityEngine;
    3.  
    4. public class NewBehaviourScript : MonoBehaviour
    5. {
    6. #if ENABLE_WINMD_SUPPORT
    7.     [DllImport("__Internal")]
    8.     extern static int GetPageContent([MarshalAs(UnmanagedType.IInspectable)]object frame, [MarshalAs(UnmanagedType.IInspectable)]out object pageContent);
    9. #endif
    10.  
    11.     void Start()
    12.     {
    13.         UnityEngine.WSA.Application.InvokeOnUIThread(() =>
    14.         {
    15. #if ENABLE_WINMD_SUPPORT
    16.             object pageContent;
    17.             var result = GetPageContent(Windows.UI.Xaml.Window.Current.Content, out pageContent);
    18.             if (result < 0)
    19.                 Marshal.ThrowExceptionForHR(result);
    20.  
    21.             var dxSwapChainPanel = pageContent as Windows.UI.Xaml.Controls.SwapChainPanel;
    22.             Debug.Log(dxSwapChainPanel);
    23. #endif
    24.         }, false);
    25.     }
    26. }
    I tested this code in Unity 5.6. Make sure you reference concrete "SwapChainPanel" type in your C# code, as otherwise it will be stripped and IL2CPP will go into the same "I have no idea what this is" mode.
     
    jjpp31807336 likes this.
  3. big_3

    big_3

    Joined:
    Apr 20, 2016
    Posts:
    45
    Great, thanks for your help!
     
  4. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    673
    @Tautvydas-Zilys I get this error when compiling on Visual Studio the resulting UWP project:

    I have simply created a new project and create the two scripts you mentioned.

    I see my *.cpp file inside "Il2CppOutputProject" project on "Source/CppPlugins" folder. I don't know C++ so it could be simply a "stupid" syntax error on your example?

    I'm using Unity 2018.2.15f1 with .NET Standard 2.0
     
    Last edited: Nov 19, 2018
  5. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    6,436
    It's actually a typo in my example! Sorry about that.

    Instead of this:

    Code (csharp):
    1. extern "C" HRESULT GetPageContent(IInspectable* frame, IUIElement** pageContent)
    It should be this:

    Code (csharp):
    1. extern "C" HRESULT __stdcall GetPageContent(IInspectable* frame, IUIElement** pageContent)
    "__stdcall" only is relevant when you build for x86, and is ignored for other CPU architectures. Since I tested it on x64, I missed it.
     
    jjpp31807336 likes this.
  6. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    673
    Thanks again @Tautvydas-Zilys ! I hope it helps to anyone having the same problem.
     
  7. jjpp31807336

    jjpp31807336

    Joined:
    May 31, 2017
    Posts:
    2
    Thank you very much and help me solve a big trouble!