nonstatic extern functions from DLL plugin import

Hi

I’m working on an acoustic room simulation with the processing being performed in a dll. It seems as though calling the function via dllimport requires the function to be static, limiting me to one audio source at a time, due to the design of the DSP. Is there a way to create nonstatic instances of imported extern functions so that they can be used as independent objects?

Thanks

The concept of “instance” method is actually just some sugar that the compiler / framework provides. Actually all methods are static as the code for those methods only exist in one place. Instance method just have an additional, hidden parameter “this”. That’s usually the first parameter that is passed to the method.

If you want to call a native method for a certain native object, you would need to pass the native object reference along to the method call. Usually you would create a wrapper class in .NET / C# that holds that native pointer (IntPtr) and provides the required method for the C# environment. Those calls are then forwarded to the native interface using static exported methods and just pass the object along.

Unity does something similar for almost everything inside the Untiy API. However they don’t pass the native pointer alone but simply the managed object. That’s what UnityEngine.Object is for. It contains an IntPtr to the native object. When a native method is called they pass the managed object. From this managed object reference the native code can get the pointer to the native object.

edit

This is not a “full” example and it doesn’t necessary need to compile at all. It just should demonstrate the basic concept. First of all you have to think about who is actually creating the native object. If the creation is initiated from managed code (which would be usually the case) you can trigger the native object creation in the constructor of our wrapper class.

// C#
public class SomeNativeObject
{
    private IntPtr m_NativeObject = IntPtr.Zero;
    public SomeNativeObject()
    {
        m_NativeObject = Internal_CreateNativeObject();
    }
    ~SomeNativeObject()
    {
        Destroy();
    }
    public void Destroy()
    {
        if (m_NativeObject != IntPtr.Zero)
        {
            Internal_DestroyNativeObject(m_NativeObject);
            m_NativeObject = IntPtr.Zero;
        }
    }
    public void SomeNativeMethod(int SomeParameter)
    {
        if (m_NativeObject == IntPtr.Zero)
            throw new Exception("No native object");
        Internal_SomeNativeMethod(m_NativeObject, SomeParameter);
    }
    [DllImport("YourDLL")]
    private static extern IntPtr Internal_CreateNativeObject();
    [DllImport("YourDLL")]
    private static extern Internal_DestroyNativeObject(IntPtr obj);
    [DllImport("YourDLL")]
    private static extern void Internal_SomeNativeMethod(IntPtr obj, int SomeParameter);
}

And this would be the matching C++ code:

// C++
extern "C" {
    YourNativeObject* Internal_CreateNativeObject()
    {
        YourNativeObject* obj = new YourNativeObject();
        // you might want to store the object reference on the native side for tracking
        return obj;
    }
    void Internal_DestroyNativeObject(YourNativeObject* obj)
    {
        // may need to update your tracking in native code
        delete obj;
    }
    void Internal_SomeNativeMethod(YourNativeObject* obj, int SomeParameter)
    {
        obj->SomeNativeMethod(SomeParameter);
    }
}

Here “YourNativeObject” is your C++ class name. As i said no guarantee that this will work exactly that way. The last time i used C++ was about 5 years ago. You might have to adjust the marshalling of the parameters but that’s the basic concept.

Unity’s internal methods usually pass the managed object which would be marshalled into a native wrapper class. From that they most likely retrieve the actual pointer to the actual native object.

Of course i don’t have to mention that you have to be careful when creating and destroying native objects. Also locking / synchronising within multithreaded environments should be carried out carefully.

The C# wrapper class might also implement the IDisposable interface and Destroy the object in Dispose(). You have to think about who owns the native object. At the moment if the C# managed wrapper class get out of extent and is garbage collected the native object is destroyed. If you don’t want the managed world to “own” the native object you should remove all those “auto destroy” things and require the constructor to pass in the native object pointer.