Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

IL2CPP Back Coding

Discussion in 'Windows' started by ClaytonOne, Dec 4, 2016.

  1. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Hi guys, if I add a .cpp file to my Unity project as a "c++ source code native plugin" and then build for Universal Windows 10 with IL2CPP as the scripting backend. Is it them possible to access / call that code from MainPage.xaml.cpp in the generated solution?
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    It is. When you put your C++ source code into Unity project, it gets compiled into the same DLL as IL2CPP generated C++ code (GameAssembly.dll).

    To call it from generated Visual Studio project (which gets compiled into the ".exe" file), you'll have to mark the functions you want to call with "__declspec(dllexport)". Now, there are 2 ways to actually do the call:

    1. Use LoadPackagedLibrary()/GetProcAddress() to dynamically fetch the function pointer for the desired function;
    2. Add "$(OutDir)\GameAssembly.lib" to your linker settings in Visual Studio (like this), and just call it directly. Windows loader will load the function pointer for you.
     
  3. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Thanks for the reply.

    Is it possible to dllexport an entire class?
     
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    Sure. But then you can only use the second option - GetProcAddress doesn't work nicely with classes.

    Something like this:

    Code (csharp):
    1. #if GENERATED_PROJECT
    2. #define PLUGIN_API __declspec(dllimport)
    3. #else
    4. #define PLUGIN_API __declspec(dllexport)
    5. #endif
    6.  
    7. class PLUGIN_API MyClass
    8. {
    9. ....
    10. };
    And then in generated C++ project (let's say in MainPage.xaml.cpp), define "GENERATED_PROJECT" before including the header:

    Code (csharp):
    1. #define GENERATED_PROJECT 1
    2. #include "..\Il2CppOutputProject\il2cppOutput\MyClass.h"
     
  5. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Basically I'm trying to create a bridge between the Unity and UWP sides. Given the code below I'm trying to call "MyFunction" from the Unity C# side and see it raised on the C++ MainPage.xaml.cpp side. I just have a simple function pointer as the bridge but it causes a linker error with

    Error LNK2019 unresolved external symbol _MyFunction@4 referenced in function _TestSceneController_MyFunction_m3479406564 Il2CppOutputProject C:\Users\mike-\Documents\Build\Il2CppOutputProject\CD707F8B36287D247F4BEF831BFFD391.obj 1

    Unity Controller
    Code (CSharp):
    1.  
    2. [DllImport("__Internal")]
    3. private static extern void MyFunction([MarshalAs(UnmanagedType.SysInt)]int number);
    4.  
    5. public void ButtonPressed()
    6. {
    7.        MyFunction(12);
    8. }
    9.  
    .cpp file as plugin
    Code (CSharp):
    1.  
    2. extern __declspec(dllexport) void(*MyFunction)(int);
    3.  
    MainPage.xaml.cpp (added "$(OutDir)\GameAssembly.lib to the linker settings)
    Code (CSharp):
    1.  
    2. #include "..\Il2CppOutputProject\il2cppOutput\Testing123.cpp"
    3.  
    4. MyFunction = [](int a)
    5. {
    6. };
    7.  
     
    Last edited: Dec 5, 2016
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    There are a couple of issues.

    1. You cannot PInvoke into function pointers. You'll have to make an actual function in C++ code which you call from C#, which can then call your function pointer.

    2. Don't include C++ files directly. That's a fast way to linker errors (even though it's not harmful in this case). Make a header file instead that has your function signatures and include that.

    3. You'll have to annotate that function pointer with "PLUGIN_API" macro, or you will not be able to set it from a different binary.
     
  7. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Would something like this work? Sorry my C++ isn't great.

    Code (CSharp):
    1. extern __declspec(dllexport)
    2. void(*MyFunction)(int);
    3.  
    4. extern __declspec(dllexport)
    5. void CallMyFunction(int number)
    6. {
    7.     MyFunction(number);
    8. }
     
  8. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    Extern keywords for variables are only useful in header files. Also, for functions you call from C#, you need to mark them with extern "C" so the compile decorates their name accordingly. Lastly, the function you call from managed code needs to use __stdcall calling convention, as that's what managed code expects.

    Do something like this:

    In Plugin.h:

    Code (csharp):
    1. #if GENERATED_PROJECT
    2. #define PLUGIN_API __declspec(dllimport)
    3. #else
    4. #define PLUGIN_API __declspec(dllexport)
    5. #endif
    6.  
    7. extern PLUGIN_API void (*MyFunction)(int);
    In Plugin.cpp:

    Code (csharp):
    1. #include "Plugin.h"
    2.  
    3. void (*MyFunction)(int);
    4.  
    5. extern "C" void __stdcall CallMyFunction(int number)
    6. {
    7.     MyFunction(number);
    8. }
    In Script.cs:

    Code (csharp):
    1. [DllImport("__Internal")]
    2. private static extern void MyFunction(int number);
    3.  
    4. public void ButtonPressed()
    5. {
    6.     MyFunction(12);
    7. }
    In generated VS project any .cpp file:

    Code (csharp):
    1. #include "..\Il2CppOutputProject\il2cppOutput\Plugin.h"
    2.  
    3. void SetFunctionPointer()
    4. {
    5.     MyFunction = [](int a)
    6.     {
    7.          // do stuff with a
    8.     };
    9. }
    I didn't actually test any of this, but it should work if I didn't make any syntax mistakes :).
     
  9. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Seems to throw up loads of errors on the UWP side and doesn't resolve "MyFunction"

    Edit: Never mind its working now - thanks for the awesome support!
     
    Last edited: Dec 5, 2016
  10. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Last question :)

    Suppose I wanted to go back the other way (i.e raise an event on the UWP side that I could listen to on the managed Unity side). I tried passing a delegate across but got an error about IL2CPP not allowing marshalling of delegates.

    Is there a way around this?
     
  11. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    You can marshal delegates, but there are 2 restrictions:

    1. They must point to a static method;
    2. That method must be annotated with [MonoPInvokeCallback] attribute.
     
  12. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Could you post a little example? :)
     
  13. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
  14. Wadjey

    Wadjey

    Joined:
    Feb 4, 2015
    Posts:
    244
    Hi,
    In this example it's a void function, could you please tell me I can return a string?
    cc @Tautvydas-Zilys

     
  15. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    Wadjey likes this.
  16. Wadjey

    Wadjey

    Joined:
    Feb 4, 2015
    Posts:
    244
    Hmmm, I'm really beginner in C++
    If you could provide me an example it will be much more easy for me to understand!
     
  17. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    Code (csharp):
    1. [DllImport("__Internal")]
    2. private static extern IntPtr GetString();
    3.  
    4. public static string GetStringFromNative()
    5. {
    6.     var stringPtr = GetString();
    7.     var str = Marshal.PtrToStringUni(stringPtr);
    8.     Marshal.FreeCoTaskMem(stringPtr);
    9.     return str;
    10. }
    Code (csharp):
    1. extern "C" wchar_t* GetString()
    2. {
    3.     const wchar_t* stringChars = ...; // the pointer to string characters
    4.     const size_t stringLength = ...; // the length of string
    5.  
    6.     auto result = static_cast<wchar_t*>(AllocCoTaskMem(sizeof(wchar_t) * (stringLength + 1)));
    7.     memcpy(result, stringChars, sizeof(wchar_t) * (stringLength + 1));
    8.     return result;
    9. }
     
  18. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89
    Is there a way to get hold of the swapchain panel from the Unity side?
     
  19. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,507
    ClaytonOne likes this.
  20. ClaytonOne

    ClaytonOne

    Joined:
    Sep 5, 2015
    Posts:
    89