See the question and my original answer on StackOverflow

You need to tell .NET that the arrays are Out parameters in IAnsiStream, like this:

[Guid("3e47f2e5-81d4-4d3b-897f-545096770373"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAmsiStream
{
    [PreserveSig]
    int GetAttribute(AMSI_ATTRIBUTE attribute, int dataSize, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, out int retData);
    
    [PreserveSig]
    int Read(long position, int size, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] buffer, out int readSize);
}

Also note I use PreserveSig to explicitely define methods return value as HRESULT.

And here is a sample implementation for the GetAttribute method:

public int GetAttribute(AMSI_ATTRIBUTE attribute, int dataSize, byte[] data, out int retData)
{
    const int E_NOT_SUFFICIENT_BUFFER = unchecked((int)0x8007007A);
    switch (attribute)
    {
        case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_APP_NAME:
            const string appName = "My App Name";
            var bytes = Encoding.Unicode.GetBytes(appName + "\0"); // force terminating zero
            retData = bytes.Length;
            if (dataSize < bytes.Length)
                return E_NOT_SUFFICIENT_BUFFER;

            Array.Copy(bytes, data, bytes.Length);
            return 0;

        // TODO: implement what's needed

        default:
            retData = 0;
            const int E_NOTIMPL = unchecked((int)0x80004001);
            return E_NOTIMPL;
    }
}