Search Unity

Question Unity can not load native plugin when source c++ code contains "new"

Discussion in 'Scripting' started by Nstdspace, Jan 30, 2023.

  1. Nstdspace

    Nstdspace

    Joined:
    Feb 6, 2020
    Posts:
    26
    I am not sure if it is a bug or not, maybe I am doing something completely wrong.
    Following basically the same steps as in this article from unity learn I got a simple plugin written in c++
    to be correctly imported and executed from a unity project.

    The C# side contains the following code:

    Code (CSharp):
    1. public class NativePluginWrapper {
    2.     [DllImport("SomePlugin", CallingConvention = CallingConvention.Cdecl)]
    3.     public static extern int getSomeNumber();
    4. }
    5. ...
    6. // in some Start()
    7. var result = NativePluginWrapper.getSomeNumber();
    8. Debug.Log(result)
    Where SomePlugin refers to a dll named SomePlugin.dll placed in Assets/Plugins which is compiled
    from the following c++ code:

    Code (NativePlugin.cpp):
    1. #include "NativePlugin.h"
    2.  
    3. extern "C" {
    4.     DllExport int getSomeNumber() {
    5.         return 1234;
    6.     }
    7. }
    Code (NativePlugin.h):
    1. #pragma once
    2.  
    3. #define DllExport __declspec (dllexport)
    4.  
    5. extern "C" {
    6.     DllExport int getSomeNumber();
    7. }
    This setup works and the log shows the correct number returned by the plugin.

    Now, adding any kind of "new" expression inside of the c++ code, for example just
    int* x = new int[1];

    leads to the following exception:

    DllNotFoundException: NativePlugin assembly:<unknown assembly> type:<unknown type> member:(null)


    What is happening here? Is allocating memory not supported / not allowed or am I doing something fundamentally wrong?
     
    Last edited: Jan 30, 2023
  2. dave_g_69

    dave_g_69

    Joined:
    Jul 24, 2021
    Posts:
    28
    I've not had problems with new and native dll's. so I can confirm they work. also typo in NativePlugin.h "void" instead of "int" getSomeNumber();

    also another great resource is https://github.com/Unity-Technologies/NativeRenderingPlugin if going deeper with functionality of your plugin. (but anything with device context stuff, has to only be done on the rendering thread - so cannot call functions that use device context from c# directly) that tripped me up when I was learning to make unity plugin. just thought I'd mention that if ever needed.
     
    Kurt-Dekker likes this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    I use malloc/free all over the place... are you able to hit those instead?

    Is it your calling convention? Did you try stdcall? I've never bothered specifying calling convention in my interops for any of the Unity targets. But then again I've never called new in my native C / C++ either... all my stuff is C. :)
     
    dave_g_69 likes this.
  4. Nstdspace

    Nstdspace

    Joined:
    Feb 6, 2020
    Posts:
    26
    Thank you for the resources. To your first point, I just made a copy paste error in my initial post, I corrected it.
    I have found an other example of working / non working pair:

    working:

    Code (CSharp):
    1. extern "C" {
    2.     int getSomeNumber() {
    3.         auto *vec = new std::vector<int>();
    4.         vec->push_back(20200);
    5.         return (*vec)[0];
    6.     }
    7. }
    Changing the return type to int* and returning &(*vec)[0] instead makes the dll fail to load.

    I read about the calling convention in some stack overflow post and just tried it out, but the default produces the same error.

    I would really like to find a way to debug the runtime itself to check what exactly is causing the dll load to fail...
     
  5. dave_g_69

    dave_g_69

    Joined:
    Jul 24, 2021
    Posts:
    28
    no prob, thought so but thought Id mention it). if return type is int* getSomeNumber() then I think you would need unsafe on the c# side. If I have time today will have look and see if I can replicate.

    side note: one strange thing I noticed with the project I linked to above was I could only use extern c in one .h file. as it would only export symbols for function in that .h file. might be a unity specific thing with that rendering project thought I would mention that too.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Are you able to return the memory pointer (address of this array) directly?

    Obviously your interop would need to return System.IntPtr instead of int and marshal it into something useful.

    When I return a good old null-terminated C string, I used this C# interop:

    Code (csharp):
    1. private static extern System.IntPtr dispatcher1_entrypoint1( int opcode1, int arg1);
    and the native C looks like:

    Code (csharp):
    1. char            *dispatcher1_entrypoint1( int opcode1, int arg1);
    and C# stands the string up as:

    Code (csharp):
    1. return Marshal.PtrToStringAnsi( dispatcher1_entrypoint1( (int)opcode, arg1));
    Here was my setup for pinning a pixel array for native code fill it out:

    Pinning a texture for manipulation via native code:

    https://forum.unity.com/threads/texture-byte-per-pixel.624343/#post-4185943

    This is all for my KurtMaster2D collection of MS-DOS and PalmOS games I wrote back in the day:

    Apple iTunes: https://itunes.apple.com/us/app/kurtmaster2d/id1015692678
    Google Play (including TV): https://play.google.com/store/apps/details?id=com.plbm.plbm1