Jump to content

Plugin error on Mono installations


pünktchen

Recommended Posts

pünktchen

@Luke My plugin makes some trouble for a user that has a Synology NAS with DSM 7 and is running the Mono framework: embyserver (1).txt

*** Error Report ***
Version: 4.7.8.0
Command line: /volume1/@appstore/EmbyServer/system/EmbyServer.exe -programdata /var/packages/EmbyServer/var -ffdetect /var/packages/EmbyServer/target/bin/ffdetect -ffmpeg /var/packages/EmbyServer/target/bin/ffmpeg -ffprobe /var/packages/EmbyServer/target/bin/ffprobe -nolocalportconfig -ignore_vaapi_enabled_flag -pidfile /var/packages/EmbyServer/var/EmbyServer.pid -defaultdirectory /volume1/Public -updatepackage emby-server-synology7-mono_{version}_armv7_legacy.spk -noautorunwebapp
Operating system: Linux version 3.10.108 (root@build2) (gcc version 8.5.0 (GCC) ) #42661 SMP Mon Jun 27 15:03:29 CST 2022
Framework: Mono: 6.12.0.122 (tarball Wed Jun  9 04:23:41 PM UTC 2021)
OS/Process: X86/X86
Runtime: volume1/@appstore/EmbyServer/lib/mono/4.5/mscorlib.dll
Processor count: 4
Data path: /var/packages/EmbyServer/var
Application path: /volume1/@appstore/EmbyServer/system
System.TypeLoadException: System.TypeLoadException: Could not resolve type with token 01000062 from typeref (expected class 'System.ReadOnlySpan`1' in assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51')
  at (wrapper dynamic-method) System.Object.lambda_method(System.Runtime.CompilerServices.Closure,object,object)
  at Emby.Server.Implementations.Services.ServiceController.Execute (Emby.Server.Implementations.HttpServer.HttpListenerHost appHost, System.Object requestDto, MediaBrowser.Model.Services.IRequest req) [0x00093] in <af0cef51841b42809f369e52e9ee1d5c>:0 
  at Emby.Server.Implementations.Services.ServiceHandler.ProcessRequestAsync (Emby.Server.Implementations.HttpServer.HttpListenerHost appHost, MediaBrowser.Model.Services.IRequest httpReq, MediaBrowser.Model.Services.IResponse httpRes, Emby.Server.Implementations.Services.RestPath restPath, System.String responseContentType, System.Threading.CancellationToken cancellationToken) [0x00168] in <af0cef51841b42809f369e52e9ee1d5c>:0 
  at Emby.Server.Implementations.HttpServer.HttpListenerHost.RequestHandler (MediaBrowser.Model.Services.IRequest httpReq, System.ReadOnlyMemory`1[T] urlString, System.ReadOnlyMemory`1[T] localPath, System.Threading.CancellationToken cancellationToken) [0x00b87] in <af0cef51841b42809f369e52e9ee1d5c>:0 
Source: mscorlib
TargetSite: Void Throw()

In my plugin i'm referencing:

<TargetFramework>netstandard2.1</TargetFramework>

The only two methods that are used within my custom api request and that needs "Span" are:

Emby.UserManager.GetUserByName(string.AsSpan());
Emby.JsonSerializer.SerializeToSpan(List<string>());

What's the problem here? Please help!

Link to comment
Share on other sites

pünktchen

Will try that, but is this an Emby limitation? Netstandard 2.1 is supported since Mono 6.4.

Link to comment
Share on other sites

Mono is used to support older devices, so even though newer versions of mono run netstandard 2.1, not every platform will actually have that. In addition, trying to target 2.1 has created some problems for the android server, so for both of these reasons, plugins should still target 2.0.

Link to comment
Share on other sites

29 minutes ago, pünktchen said:

Will try that, but is this an Emby limitation? Netstandard 2.1 is supported since Mono 6.4.

The general mono project is a dead horse for everything except Xamarin.
It is still getting regular updates, but the changes are solely done by MS who need to keep it alive for Xamarin. There's no more community driven work for some years now.

mono implements netstandard2.1 since 2019, and it's possible to use that with Xamarin targets. For everything else you can only use ns2.1 on mono with some trickery, but that works for simple projects only because the runtime is unable to correctly apply binding redirections for more complex setups with mixed targeting of projects and nuget packages.
Nobody is working on this, so I'm sure this will never get fixed.

My conclusion on some recent research on whether Emby Server could run on Mono with ns2.1 was this:

Quote

 

Due to the work done by Xamarin to implement netstandard2.1 in mono (for use with Xamarin), there's now the (rather unintended) by-product that the regular mono runtime is now like a "net-framework 4.9 with support for netstandard2.1".

But there does not exist a "net-framework 4.9" target and nf4.8 cannot support ns2.1. The trick I had used was to compile EmbyServer like an executable application but with ns2.1 as the target framework (practically an invalid configuration).
This works basically, but it lacks all the required implementation for dealing with dependencies and binding redirections.
(Xamarin have done that for their custom mono runtimes on Android and iOS, but nobody works on implementing this for the regular mono runtimes for Linux or Windows).

 

Link to comment
Share on other sites

pünktchen

Cannot give you any feedback yet, because i use a method from netstandard 2.1 that is not available in earlier versions and still don't know how to circumvent that.

Link to comment
Share on other sites

10 minutes ago, pünktchen said:

Cannot give you any feedback yet, because i use a method from netstandard 2.1 that is not available in earlier versions and still don't know how to circumvent that.

Which one?

Link to comment
Share on other sites

Try this:

namespace Emby.Test
{
    using System;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography;

    internal static class RngExtension
    {
        public static int GetInt32(this RandomNumberGenerator rng, int fromInclusive, int toExclusive)
        {
            if (fromInclusive >= toExclusive)
            {
                throw new ArgumentException("InvalidRandomRange");
            }

            // The total possible range is [0, 4,294,967,295).
            // Subtract one to account for zero being an actual possibility.
            uint range = (uint)toExclusive - (uint)fromInclusive - 1;

            // If there is only one possible choice, nothing random will actually happen, so return
            // the only possibility.
            if (range == 0)
            {
                return fromInclusive;
            }

            // Create a mask for the bits that we care about for the range. The other bits will be
            // masked away.
            uint mask = range;
            mask |= mask >> 1;
            mask |= mask >> 2;
            mask |= mask >> 4;
            mask |= mask >> 8;
            mask |= mask >> 16;

            Span<uint> resultSpan = stackalloc uint[1];
            uint result;

            do
            {
                FillSpan(rng, MemoryMarshal.AsBytes(resultSpan));
                result = mask & resultSpan[0];
            }
            while (result > range);

            return (int)result + fromInclusive;
        }

        public static int GetInt32(this RandomNumberGenerator rng, int toExclusive)
        {
            if (toExclusive <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(toExclusive), "NeedPosNum");
            }

            return GetInt32(rng, 0, toExclusive);
        }

        internal static void FillSpan(RandomNumberGenerator rng, Span<byte> data)
        {
            if (data.Length > 0)
            {
                var buffer = new byte[data.Length];

                rng.GetBytes(buffer);
                buffer.CopyTo(data);
            }
        }
    }
}

 

Opposed to the actual method, this requires you to create an instance and then call the extension method like this:

            var rng = RandomNumberGenerator.Create();
            int a = rng.GetInt32(0, 100);

It does the same as the original method, just a bit slower, but as long as you aren't creating thousands or millions of random numbers, you probably won't notice..

Link to comment
Share on other sites

pünktchen

What is FillSpan() in your do-while loop? Netstandard 2.1 uses RandomNumberGeneratorImplementation.FillSpan() that is also not available in Netstandard 2.0

Link to comment
Share on other sites

11 minutes ago, pünktchen said:

What is FillSpan() in your do-while loop? Netstandard 2.1 uses RandomNumberGeneratorImplementation.FillSpan() that is also not available in Netstandard 2.0

FillSpan does the same as GetBytes, just that it writes directly into the span. I'm calling RNG.GetBytes() and copy the result into the span.

Or in other words: GetBytes is internally calling RandomNumberGeneratorImplementation.FillSpan(ret) as well. We can't access RandomNumberGeneratorImplementation, but we can use a public method that makes the internal call we need and then use that in place of it.

Link to comment
Share on other sites

Another typical way to resolve such things is to go deeper, but in this case, it is getting into platform-specific implementations and once we go there, we are losing platform independency. The way above is the safest way, because it doesn't use any non-public APIs.

Link to comment
Share on other sites

pünktchen

Okay, it's all working now with Mono, but that is really a big step backwards in coding.
Not because of the RandomNumberGenerator, but netstandand2.0 is missing simple things StringComparison for String.Contains 😒

  • Thanks 1
Link to comment
Share on other sites

You can use the extension methods from the Emby Plugin API:

        using MediaBrowser.Model.Extensions;

        private void Test()
        {
            string myString = "abcdABCD";

            if (myString.ContainsIgnoreCase("cdab"))
            {
                //
            }

            if (myString.EqualsIgnoreCase("abcdabcd"))
            {
                //
            }
        }

 

  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...