Search code examples
c#marshallingbass

Marshal bool return as custom Return<bool>


I am currently wrapping Un4seen BASS audio library in .Net

BASS functions returning a bool value, require checking LastError to know the Error that occurred if false is returned.

So I implemented a Return<T> class to make it object-oriented:

using ManagedBass.Dynamics;

namespace ManagedBass
{
    /// <summary>
    /// Wraps a Bass Error in a function return value
    /// </summary>
    /// <typeparam name="T">The Type of the function return value</typeparam>
    public class Return<T>
    {
        public Errors ErrorCode { get; }

        Return(T Value)
        {
            ErrorCode = Bass.LastError;
            this.Value = Value;
        }

        public T Value { get; }

        public static implicit operator T(Return<T> e) => e.Value;

        public static implicit operator Return<T>(T e) => new Return<T>(e);

        public bool Success => ErrorCode == Errors.OK;
    }
}

Now for methods like:

[DllImport(DllName, EntryPoint = "BASS_StreamFree")]
public static extern bool StreamFree(int Handle);

I would instead want to return a Return<bool> so that the user can easily check the ErrorCode in object-oriented way.

Now, you may say that I create another method like:

// bool implicitly converts to Return<bool>
public static Return<bool> StreamFree2(int Handle) => StreamFree(Handle)

But, the problem is that there are already thousands of DllImports and doing this for all of them would be a nightmare.

So, I tried:

[DllImport(DllName, EntryPoint = "BASS_StreamFree")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Return<bool> StreamFree(int Handle);

It compiled, but didn't work at all.

Can anyone please point me out where I am wrong or is something like this even possible.

Thanks in Advance.


Solution

  • There is no way to do this without writing wrapper functions. You cannot simply change the function's return type. The marshaller isn't going to be able to create a new Return<T> object automatically. Your final attempt at a declaration tells the marshaller that the function returns a BOOL, but the function is not declared as returning a BOOL. Obviously that's not going to work; it fails at runtime when the mismatch is detected.

    There is likely another problem. I don't know what type the BASS library functions actually return, but if it is bool, then UnmanagedType.Bool is the wrong type. You actually want UnmanagedType.U1. UnmanagedType.Bool refers to the 4-byte BOOL type, which is actually an integer. UnmanagedType.U1 refers to C++'s 1-byte bool type.

    More generally, I don't think your "object-oriented" approach would offer any real advantage. Sure, you'd be returning an object, but the client code you write wouldn't look any different because you'd still have to check the error code each time.

    An elegant, .NET-conforming design would throw an exception when the library function returned an error. That exception (BassException or whatever) would then wrap the error code. Of course, this will require writing wrapper functions for each of the library functions, but at least there will be some payoff once you get done. Consider automating the generation of such functions with a tool.