Author Topic: bass.net with MAUI iOS cannot load native bass library  (Read 1732 times)

pgruebele

  • Posts: 94
I have been trying to migrate to the latest Core release for a MAUI based iOS app and cannot get the native bass library it seems.  My first call to Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 20); throws an exception:

{System.TypeInitializationException: The type initializer for 'Un4seen.Bass.Bass' threw an exception.
 ---> System.DllNotFoundException: bass
   at Un4seen.Bass.Bass.InitBass()
   at Un4seen.Bass.Bass..cctor()

In the csproj file I am using the following:

      <Reference Include="Bass.Net">
         <HintPath>..\bass\core\Bass.Net.dll</HintPath>
      </Reference>

      <NativeReference Include="Platforms/iOS/lib/bass.xcframework">
         <Kind>Framework</Kind>
      </NativeReference>

I have spent hours trying to get this to work without success.

Any chance a sample MAUI app could be provided which shows how to successfully consume bass? The current batch of samples don't show this and I am sure others will start running into this issue as they migrate.

Probably best to use the latest VS preview with .Net 7 RC for this...


radio42

  • Posts: 4839
You need to provide the native bass library (libbass.dylib) in the same folder where your executable is located (which should be the same as where you also locate the Bass.Net.dll).
But unfortunately I have no iOS development environment...and as such can not test this.

pgruebele

  • Posts: 94
The deployed app package has the following structure:

- Frameworks [folder]
  - bass.framework [folder]
    - _CodeSignature [folder]
    - bass [Document]
    - Info.plist [Property List]
  - [... other frameworks that work]
- Bass.Net.dll
- [other assets]
- [libmono and app related dylib files (there is no dylib for bass)]

Also, the following library load returns success = True, so the library is there and dynamically loadable (even though the following bass call still gives the same DllNotFoundException):

Code: [Select]
var success = NativeLibrary.TryLoad("Frameworks/bass.framework/bass", out var libHandle);
Also, other frameworks with the same folder structure (no dylib in container root) work correctly so it seems there is an issue with how bass.net is loading the library?

I'm hoping that this is enough information to try to fix the issue and send me a build to test, but if you do need a proper environment you can just use VS 17.4.0 Preview 5.0 Community Edition to test this out.  I assume you have a mac available but if not it would be good to have at least a VMware emulated mac for development...

radio42

  • Posts: 4839
As far as I can see you placed the Bass.Net.dll to the root folder. But where do you place the native bass libs in your structure? Ie. is the libbass.dylib in the same folder as the Bass.Net.dll ?

Note, that the new v2.4.17.x requires the same version also for all native libs and that since that version all iOS native libs are dynamic libs.

pgruebele

  • Posts: 94
The app container has all xcframework dynamic libraries in the Frameworks sub-folder (bass and third-party), so in the case of libbass, the path to the dynamic library is "Fameworks/bass.framework/bass" (no dylib extension). I verified that this is the correct loadable path by successfully doing "NativeLibrary.TryLoad("Frameworks/bass.framework/bass", out var libHandle);".  So I think for iOS/Mac, bass.net needs to check if the file exists in the container root and if not try to load from the "Fameworks/bass.framework/bass" instead.  And this needs to be done for all extensions (replace "bass" with the extension name).

The app container root also has dynamic libraries with dylib extension, but those are for mono framework etc (not for included xcframeworks).

Note that this is not me placing the files but how the latest VS preview places files into the app container.


radio42

  • Posts: 4839
Bass.Net doesn’t do anything magic and is not loading the libraries manually.
 Bass.Net is a .Net wrapper for the native bass libraries and it uses the [DllImport("bass")] attribute to marshal the native bass unmanaged functions.
As no extra path is given with the DllImport, the wrapper and the native libs must reside in the same folder.

pgruebele

  • Posts: 94
OK I will try to make this work with symbolic links or search paths and will report back...

pgruebele

  • Posts: 94
It's not pretty but for anyone else having this issue here is my current work-around:

Code: [Select]
var bundlePath = Foundation.NSBundle.MainBundle.BundlePath;
var fm = Foundation.NSFileManager.DefaultManager;
if (!File.Exists(Path.Combine(bundlePath, "bass")))
{
string[] libs = { "bass", "bass_fx", "bass_mpc", "bassape", "bassenc", "bassenc_flac", "bassenc_mp3", "bassenc_ogg", "bassenc_opus", "bassflac", "bassmidi", "bassmix", "bassopus", "basswv" };
foreach (var lib in libs)
{
var success2 = fm.CreateSymbolicLink(Path.Combine(bundlePath, lib), Path.Combine(bundlePath, $"Frameworks/{lib}.framework/{lib}"), out var error2);
CrossPlatform.Assert(success2, "bass symlink");
}
}

I just create symbolic links for each dylib.

A better way would be for bass to support something like https://stackoverflow.com/questions/58955723/net-core-project-with-native-library-dependency where the resolver can determine where to load the dllimport from, and in the case of iOS it can look in the root and Frameworks folder...

Ian @ un4seen

  • Administrator
  • Posts: 26037
Please clarify which part of that stackoverflow thread you're referring to, and would it need to be implemented in the native BASS libraries or BASS.Net or both?

I'm not a .Net user myself, so I'm not sure about this, but I seem to recall seeing a config option in the past that an app could use to specify where its libraries are? If that does exist, perhaps it can be used instead of symbolic links?

radio42

  • Posts: 4839
I assume he means the use of the .Net core specific "NativeLibrary" class, which allows to set and load native libraries by a "DllImportResolver" delegate for an assembly.
If yes, it would be something which can only be solved in the .Net world.

However, I am wondering, why the user doesn't implement this by his own?
The "DllImportResolver" is defined per assembly, i.e. his application, and the folder structure (where he would like to put his native libraries) is specific to this application.

E.g. by default Bass.Net assumes all native bass libraries within the same folder as the Bass.Net.dll - native libs will be loaded automatically with the first call to any of its function.
If you want to implement your own folder structure, you can also (on .Net core) use the "NativeLibrary.TryLoad" method to manually load native assemblies.
Nothing blocks the user from doing so.
As such, I assume, there is nothing to implement out-of-the-box - even for .Net core users...

pgruebele

  • Posts: 94
You are right.  I added this code to my assembly and now bass is able to load all the libraries normally:

Code: [Select]
// Used to get correct dylib path for bass libraries
public static nint ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
nint libHandle = 0;

if (libraryName.StartsWith("bass"))
{
#if WINDOWS
NativeLibrary.TryLoad($"{libraryName}.dll", out libHandle);
#elif __ANDROID__
NativeLibrary.TryLoad($"{libraryName}.so", assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
#elif __IOS__
NativeLibrary.TryLoad($"./Frameworks/{libraryName}.framework/{libraryName}", assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
#else
CrossPlatform.Assert(false);
#endif
}

return libHandle;
}

Just register the above function before making the first bass calls:

Code: [Select]
#if IOS
NativeLibrary.SetDllImportResolver(typeof(BassNet).Assembly, ImportResolver);
#endif

Laurent0506

  • Posts: 3
I'm trying to load bass.dll on a Maui project windows platform without success.

The Bass.Net.dll is the core version.

I have copied bass.dll 64 and bass_fx.dll 64 in this folder source\repos\MauiApp1\MauiApp1\bin\Debug\net6.0-windows10.0.19041\win10-x64

I use .net 6 / Microsoft Visual Studio Community 2022 (64 bits) - Preview Version 17.4.0 Preview 6.0

This small project is here : https://github.com/Laurent0506/MauiAppBass.git

Error is :    InnerException    (0x8007000B)"   System.Exception {System.BadImageFormatException}

I assume that I'm not a .net killer and I thank everyone who can help !!!

radio42

  • Posts: 4839
You need to provide the native bass library in the same folder where your executable is located (which should be the same as where you also locate the Bass.Net.dll).

Laurent0506

  • Posts: 3
If by "provide the native bass library in the same folder ..." you mean to copy bass.dll in same folder then Bass.net.dll and my executable, it's what I've done.

By the way, the same code is working for a WPF type project


radio42

  • Posts: 4839
Then I can only think of a 32- vs. 64-bit conflict?!
A BadImageFormatException actually indicates this, as it is only thrown when the native lib couldn’t be loaded. Eg. your app is 64-Bit, but the bass.dll is 32-bit (or the other way around).

Laurent0506

  • Posts: 3
My mistake was that Base.net.dll is present in 2 directories and I choose the first one without searching for another place :(  :
  • bin\Debug\net6.0-windows10.0.19041.0\win10-x64
  • bin\Debug\net6.0-windows10.0.19041.0\win10-x64\AppX
With this.GetType().Assembly.Location you can see that the runtime directory is AppX.

Thx Radio42.

serkanp

  • Posts: 135
Re: bass.net with MAUI iOS cannot load native bass library
« Reply #16 on: 30 Oct '23 - 18:53 »
You are right.  I added this code to my assembly and now bass is able to load all the libraries normally:

Code: [Select]
// Used to get correct dylib path for bass libraries
public static nint ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
nint libHandle = 0;

if (libraryName.StartsWith("bass"))
{
#if WINDOWS
NativeLibrary.TryLoad($"{libraryName}.dll", out libHandle);
#elif __ANDROID__
NativeLibrary.TryLoad($"{libraryName}.so", assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
#elif __IOS__
NativeLibrary.TryLoad($"./Frameworks/{libraryName}.framework/{libraryName}", assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
#else
CrossPlatform.Assert(false);
#endif
}

return libHandle;
}

Just register the above function before making the first bass calls:

Code: [Select]
#if IOS
NativeLibrary.SetDllImportResolver(typeof(BassNet).Assembly, ImportResolver);
#endif

i am having trouble to set csproj file for ios and ios simulator.
can you give an example how you set it?