Search Unity

how to call scripts from the xaml file in UWP IL2CPP ?

Discussion in 'Windows' started by creat327, Sep 20, 2018.

  1. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,756
    Hi

    Up until now, I was compiling UWP using the .Net version which generated .CS files. I decided to upgrade to IL2CPP since the other one will be deprecated but I have the problem that I can't find a way to execute my code.

    For instance in MainPage.xaml.cs I would do something like this to attach to a delegate:
    MyOwnClass.OnDisplayPrompt += Here_OnDisplayPrompt;

    MyOwnClass is a class in my game. It has OnDisplayPrompt as a public action. Works just fine to attach methods to it from my mainpage.

    On MainPage.xaml.cpp I have no idea how to achieve the same result. It can't find MyOwnClass class. I've tried everything I can think of with no luck. Any ideas?

    Thanks
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    There are several ways, but let me ask you a different question: what are you going to do in the handler? Does it need to be done on Visual Studio side?
     
  3. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,756
    Well, I need to display/hide a control that is on the mainpage.xaml file based on an event that happens on the game.I also set a bunch of properties on that control. In CS it was easy, I just attach a local method to a delegate and do all the magic when the delegate gets fired from the game. On C++ I see no way to do the same thing and no instructions on the site.
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Put a C++ file in your Unity project:

    Code (csharp):
    1. typedef void (*OnDisplayPromptCallback)();
    2. static OnDisplayPromptCallback s_Callback;
    3.  
    4. extern "C" void __stdcall OnDisplayPrompt()
    5. {
    6.     auto callback = s_Callback;
    7.     if (callback != nullptr)
    8.         callback();
    9. }
    10.  
    11. __declspec(dllexport) void SetDisplayPromptCallback(OnDisplayPromptCallback callback)
    12. {
    13.     s_Callback = callback;
    14. }
    Then, in your C# code on Unity side, do this:

    Code (csharp):
    1. [DllImport("__Internal")]
    2. extern static void OnDisplayPrompt();
    3.  
    4. static void FireOnDisplayPromptEvent()
    5. {
    6.     OnDisplayPrompt();
    7. }
    Then, on Visual Studio side, do this:

    Code (csharp):
    1. typedef void (*OnDisplayPromptCallback)();
    2. __declspec(dllimport) void SetDisplayPromptCallback(OnDisplayPromptCallback callback);
    3.  
    4. static void SetupDisplayPromptCallback()
    5. {
    6.     SetDisplayPromptCallback(Here_OnDisplayPrompt);
    7. }
     
  5. abhijit93

    abhijit93

    Joined:
    Mar 5, 2016
    Posts:
    4
    @Tautvydas-Zilys Thanks for the quick replies. I needed to do a similar but basically set up a protocol on in App::Activated() in app.cpp but I just want to call a function and send a string as a parameter. Any code example I can look into?

    In app.cpp
    Code (CSharp):
    1. void App::OnActivated(CoreApplicationView^ sender, IActivatedEventArgs^ args)
    2. {
    3.     m_CoreWindow->Activate();
    4.     if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol)
    5.     {
    6.         Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs^ eventArgs =
    7.             dynamic_cast<Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs^>(args);
    8.  
    9.         // Call my Unity function of Launcher class here (static). Say,
    10.         // Launcher::ReceivedResponse(eventArgs->Uri->ToString());
    11.     }
    12. }
    How do I go about calling it?
     
    Last edited: Sep 21, 2018
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    It's tricky. The engine and C# world are initializing on a different thread, so you're not guaranteed that C# world even exists at this point in time. Furthermore, multiple activation requests can come in succession and you don't want to lose any of them. I think the best solution here is to setup both a callback and multiple state storage. Something like this.

    .cpp file in your Unity Project:

    Code (csharp):
    1. #include <string>
    2. #include <vector>
    3. #include <windows.h>
    4.  
    5. struct CriticalSection
    6. {
    7.     inline CriticalSection()
    8.     {
    9.         InitializeCriticalSectionAndSpinCount(&m_CriticalSection, 200);
    10.     }
    11.  
    12.     inline ~CriticalSection()
    13.     {
    14.         DeleteCriticalSection(&m_CriticalSection);
    15.     }
    16.  
    17.     inline void Enter()
    18.     {
    19.         EnterCriticalSection(&m_CriticalSection);
    20.     }
    21.  
    22.     inline void Leave()
    23.     {
    24.         LeaveCriticalSection(&m_CriticalSection);
    25.     }
    26.  
    27.     struct Lock
    28.     {
    29.         inline Lock(CriticalSection& criticalSection) :
    30.             m_CriticalSection(criticalSection)
    31.         {
    32.             criticalSection.Enter();
    33.         }
    34.  
    35.         inline ~Lock()
    36.         {
    37.             m_CriticalSection.Leave();
    38.         }
    39.  
    40.     private:
    41.         CriticalSection& m_CriticalSection;
    42.     };
    43.  
    44. private:
    45.     CRITICAL_SECTION m_CriticalSection;
    46. };
    47.  
    48. typedef void(__stdcall* ActivatedEventCallback)(const wchar_t* uri);
    49.  
    50. static CriticalSection s_CriticalSection;
    51. static std::vector<std::wstring> s_ActivationUriBacklog;
    52. static ActivatedEventCallback s_ActivatedEventCallback;
    53.  
    54. extern "C" void __stdcall SetupActivatedEventCallback(ActivatedEventCallback callback)
    55. {
    56.     CriticalSection::Lock lock(s_CriticalSection);
    57.  
    58.     s_ActivatedEventCallback = callback;
    59.  
    60.     for (const auto& uri : s_ActivationUriBacklog)
    61.         s_ActivatedEventCallback(uri.c_str());
    62.  
    63.     s_ActivationUriBacklog.clear();
    64. }
    65.  
    66. __declspec(dllexport) void ReceivedResponse(const wchar_t* uri)
    67. {
    68.     CriticalSection::Lock lock(s_CriticalSection);
    69.  
    70.     if (s_ActivatedEventCallback != nullptr)
    71.     {
    72.         s_ActivatedEventCallback(uri);
    73.     }
    74.     else
    75.     {
    76.         s_ActivationUriBacklog.emplace_back(uri);
    77.     }
    78. }
    And then on your Unity project side in C#, you'd do this:

    Code (csharp):
    1.         delegate void ReceivedResponseDelegate([MarshalAs(UnmanagedType.LPWStr)]string uri);
    2.  
    3.         [MonoPInvokeCallback(typeof(ReceivedResponseDelegate))]
    4.         static void ReceivedResponse(string uri)
    5.         {
    6.             // Your code
    7.         }
    8.  
    9.         [DllImport("__Internal")]
    10.         extern static void SetupActivatedEventCallback(ReceivedResponseDelegate callback);
    11.  
    12.         void Awake()
    13.         {
    14.             SetupActivatedEventCallback(ReceivedResponse);
    15.         }
    Finally, on the VS side, you just have to declare and call that function:

    Code (csharp):
    1. __declspec(dllimport) void ReceivedResponse(const wchar_t* uri);
    2. ReceivedResponse(eventArgs->Uri->ToString()->Data());
     
    Pierre-Olivier-Pigny likes this.
  7. abhijit93

    abhijit93

    Joined:
    Mar 5, 2016
    Posts:
    4
    This works perfect! Thanks for the help
     
  8. Barkers-Crest

    Barkers-Crest

    Joined:
    Jun 19, 2013
    Posts:
    159
    I was able to make this work for our game as well so thank you. In IL2CPP this ends up becoming a necessity. If the activation event is passed at startup, only app.cpp is able to receive that event. If you register an event handler in your c# script for CoreApplication.MainView.Activated += ApplicationView_Activated; then that is too late.

    I am curious is it possible to modify the script to pass the ProtocolActivatedEventArgs from cpp to c#?

    I couldn't figure out how.
     
  9. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    I'll try to come up with an example today.
     
  10. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    I've made an example. Look here:

    https://github.com/TautvydasZilys/u...CPP_WithCSharpProject_Example/App.xaml.cs#L22
    https://github.com/TautvydasZilys/u...er/Assets/Scripts/PrintActivationArguments.cs
    https://github.com/TautvydasZilys/u.../Assets/Scripts/ActivationArgumentsHelper.cpp

    The generated project was converted to C# as somebody was asking me how to do it in another thread. However, for your purposes that doesn't matter - hooking up the event is nearly identical in both C++ and C# and I posted C++ syntax in comments above the equivalent C# syntax.
     
  11. Barkers-Crest

    Barkers-Crest

    Joined:
    Jun 19, 2013
    Posts:
    159
    Okay this makes sense. I'll give this a try. Thanks so much.
     
  12. yaqinh0818

    yaqinh0818

    Joined:
    Mar 4, 2017
    Posts:
    5
    Hi Tautvydas-Zilys,

    Your code is really great and you are an expert here. For this specific question, I have an extra question about it.
    I posted my question in the forum: https://forum.unity.com/threads/get...launched-by-another-uwp-non-unity-app.641509/

    If you have some time, could you help me and answer my thread? Thank you!

    I reply here is because I think this post is related to my problem. Instead of getting URL, I need to get shared files.
    So back to your solution. I tried your solution in my Unity UWP D3D IL2CPP app. I can not make the app work following your solution.

    Few questions I want to ask:

    1. Besides all these code changes, is there any settings I need to do to make it work?

    2. Does this solution only work for XAML app? Not for D3D app right? If possible, what changes should I make this work with D3D app?

    Thanks!
     
  13. Sappac

    Sappac

    Joined:
    Mar 25, 2019
    Posts:
    5
    Hello
    Hello! I tried this method, but I get some error on VS side in MainPage.xaml.cpp when compile build:

    Error LNK2019 unresolved external symbol "__declspec(dllimport) void __cdecl SetDisplayPromptCallback(void (__cdecl*)(bool))" (__imp_?SetDisplayPromptCallback@@YAXP6AX_N@Z@Z) referenced in function "public: __cdecl MYPROJECT::MainPage::MainPage(void)" (??0MainPage@MYPROJECT@@QE$AAA@XZ) MYPROJECT D:\WORKSPACE\_BUILDS\MYPROJECT\MainPage.xaml.obj 1

    Plugin C++ located in VS side on UnityData section.
    Could you help me, please!
     
  14. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    It should be put into your Unity project, so it automatically gets included in Il2CppOutputProject.
     
  15. Sappac

    Sappac

    Joined:
    Mar 25, 2019
    Posts:
    5
    Yes. I compiled this code
    1. typedef void (*OnDisplayPromptCallback)();
    2. static OnDisplayPromptCallback s_Callback;
    3. extern "C" void __stdcall OnDisplayPrompt()
    4. {
    5. auto callback = s_Callback;
    6. if (callback != nullptr)
    7. callback();
    8. }
    9. __declspec(dllexport) void SetDisplayPromptCallback(OnDisplayPromptCallback callback)
    10. {
    11. s_Callback = callback;
    12. }
    to dll plugin and added plugin to Unity and enabled checkbox for WSAPlayer, than i compiled in Unity and opened solution in VS. Location plugin in VS shows in UnityData

    In VS I get this error about Error LNK2019 unresolved external symbol "__declspec(dllimport) void __cdecl SetDisplayPromptCallback
     
  16. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Oh, if you put that code in a DLL rather than raw .cpp files in your Unity project, you'll have to reference "plugin.lib" (or whatever your plugin is called) from generated VS project. You can open it in text editor to see how GameAssembly.lib is referenced.
     
  17. Sappac

    Sappac

    Joined:
    Mar 25, 2019
    Posts:
    5
    Hm, I added raw .cpp file to unity project and run in unity, i see in unity's log error:
    EntryPointNotFoundException: OnDisplayPrompt

    It is ok?
     
  18. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    What you do DLLImport, what DLL do you specify? If it's a raw .cpp file, you need to specify "__Internal".
     
  19. Sappac

    Sappac

    Joined:
    Mar 25, 2019
    Posts:
    5
    Yes, I have this code in unity project:
    [DllImport("__Internal")]
    extern static void OnDisplayPrompt();


    but in running in unity I get this error EntryPointNotFoundException: OnDisplayPrompt

    So, i don't undestand why it not works. The raw cpp file located in unity project in assets folder.
    I want undestand this example because I need make similar task. Maybe you know more optimal way how I make my task.
    I have unity game and I build for UWP with il2cpp backend. In game I need show microsoft ads (banner and interstitial) in some serapate game's menu. When I built for WP8 with .NET backend, I called actions in custom class from unity project in MainPage.cs for getting boolean variables showBannerAd or hide, showInterstitialAd or hide. For adding microsoftAdvertising I see documentation in this link https://docs.microsoft.com/en-us/windows/uwp/monetize/adcontrol-in-xaml-and--net
    And now banner shows in all menu in game.
     
    Last edited: Apr 4, 2019
  20. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Raw C++ files will work only after you build the project. It won't work in the editor.
     
  21. makaqo

    makaqo

    Joined:
    Sep 16, 2015
    Posts:
    22
    I have the same problem @Sappac "Error LNK2019 unresolved external symbol "__declspec(dllimport) void __cdecl SetDisplayPromptCallback", Do you have any solution?
     
  22. makaqo

    makaqo

    Joined:
    Sep 16, 2015
    Posts:
    22
  23. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Great! I assume you were able to figure out that linker error?
     
  24. makaqo

    makaqo

    Joined:
    Sep 16, 2015
    Posts:
    22
    Yes, my problem was that the function I was sending as parameter for SetDisplayPromptCallback was not static,
    and I modified your code until it became linker error.

    With a static function and the implementation of native VungleSDK in C ++ I was able to implement the video ads without problems.

    Thanks!