Assembly Loading Problem in Tizen .NET Applications
Swift Kim
Engineer
You may have encountered the following error when launching a .NET application on your Tizen device, although the DLL file specified by the error message is already owned by the application (in tpkroot
).
Note: You can view the log from the Tizen Log Viewer window or dlogutil.
Unhandled exception.
System.IO.FileNotFoundException: Could not load file or assembly 'System.Collections.Immutable, Version=1.2.5.0'.
The system cannot find the file specified.
File name: 'System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at TizenAppTemplate.App.OnCreate()
at Tizen.Applications.CoreBackend.UICoreBackend.OnCreateNative(IntPtr data)
at Tizen.Applications.CoreBackend.UICoreBackend.Run(String[] args)
at Tizen.Applications.CoreApplication.Run(String[] args)
at Tizen.Applications.CoreUIApplication.Run(String[] args)
at TizenAppTemplate.App.Main(String[] args)
Why do I see this error?
The error is a result of a DLL version mismatch. Your application expects a newer version of system DLL than what the device has. This error appears when both of the following conditions are met:
- The application package contains or has a recursive dependency on one or more system DLLs (
System.*
orTizen.*
) - The application's system DLLs are newer than DLLs installed on the device
This usually happens when you reference a system package from your application. The following example shows an application project file (.csproj
) that contains such a reference.
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>tizen50</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="1.7.0" />
</ItemGroup>
In some cases, the file doesn't reference the system package directly. However, it may have a recursive dependency in its dependency graph.
<ItemGroup>
<!-- This library is dependent on System.Collections.Immutable 1.7.0. -->
<PackageReference Include="SomeLibrary" Version="1.0.0" />
</ItemGroup>
Note: AssemblyVersion, AssemblyFileVersion, and NuGet PackageVersion are different version properties. For example, the NuGet package System.Collections.Immutable version 1.7.0 (NuGetPackageVersion) comes with System.Collections.Immutable.dll
version 1.2.0.5 (AssemblyVersion). Only AssemblyVersion is used by the runtime to verify DLL dependencies.
The following example shows the AssemblyVersion of the DLL used by the application and the AssemblyVersion of the DLL installed on the system.
### Application-owned DLL
$ sdb pull /opt/usr/globalapps/org.tizen.example.TizenAppTemplate/bin/System.Collections.Immutable.dll
$ dotnet-ildasm System.Collections.Immutable.dll | grep '\.ver' | tail -n 1
.ver 1:2:0:5
### Pre-installed DLL
$ sdb pull /usr/share/dotnet.tizen/netcoreapp/System.Collections.Immutable.dll
$ dotnet-ildasm System.Collections.Immutable.dll | grep '\.ver' | tail -n 1
.ver 1:2:0:4
The current implementation of the application launcher in Tizen (which hosts the .NET runtime) always prioritizes DLLs found on the device and disregards application-owned DLLs, even though they are newer. It also ignores any properties defined in the application's .deps.json
file.
In fact, this restriction is intentionally added in Tizen for performance reasons. The launcher pre-fetches several runtime components commonly used by applications (for reduced startup time) by assuming a unified set of Trusted Platform Assemblies across all applications. Because of the fundamental design of CoreCLR, a DLL cannot be loaded into the default LoadContext if a DLL with the same name exists in the Trusted Platform Assemblies.
For further details, you can read:
- Runtime-dependent deployment
- CoreCLR hosting APIs
- AssemblyLoadContext Class
- Tizen's launcher implementation
Possible solutions
Here are some solutions you can try:
-
Remove or downgrade the package
The first, most basic workaround you can try is to eliminate the dependency itself. Check if the package is being used in the code and, if possible, re-implement the code without the package. Otherwise, downgrade the package from NuGets so that its DLL versions are compatible with all target devices. However, this may not be feasible if you cannot find a working version or you cannot control the dependencies of the libraries used by your application.
<ItemGroup> <!--<PackageReference Include="System.Collections.Immutable" Version="1.7.0" />--> <PackageReference Include="System.Collections.Immutable" Version="1.6.0" /> </ItemGroup>
-
Handle AssemblyResolve events
Another workaround is to install a custom AssemblyResolve event handler inside your application. This handler is invoked when the runtime cannot find a DLL with a matching version. Note that if an application DLL has a native image, the native image is not automatically resolved by Assembly.LoadFile().
static void Main(string[] args) { AppDomain.CurrentDomain.AssemblyResolve += (object s, ResolveEventArgs eventArgs) => { var appDir = Path.GetDirectoryName(typeof(App).Assembly.Location); var assemblyName = eventArgs.Name.Split(',')[0]; var assemblyPath = Path.Combine(appDir, assemblyName + ".dll"); return File.Exists(assemblyPath) ? Assembly.LoadFile(assemblyPath) : null; }; .. app.Run(args); }
Use this API with care since it can cause some unexpected behaviors, such as a type identity problem (see Best Practices for Assembly Loading). Also, you cannot use Assembly.Load() (with a full
assemblyRef
as an argument) or Assembly.LoadFrom() because they implicitly load an assembly into the default LoadContext and this leads to a similar error:Unhandled exception. System.IO.FileLoadException: Could not load file or assembly 'System.Collections.Immutable, Version=1.2.5.0'. Could not find or load a specific file. (0x80131621) File name: 'System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' ---> System.IO.FileLoadException: Could not load file or assembly 'System.Collections.Immutable, Version=1.2.5.0'. at System.Runtime.Loader.AssemblyLoadContext.InternalLoadFromPath(String assemblyPath, String nativeImagePath) at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath) at System.Reflection.Assembly.LoadFrom(String assemblyFile) ..
-
Use custom AssemblyLoadContext
For advanced scenarios, you can create your own AssemblyLoadContext in your application to isolate any specific DLLs. The following
CustomAssemblyLoadContext
class overrides theLoad()
method to resolve DLLs from the current application directory. See Understanding System.Runtime.Loader.AssemblyLoadContext for detailed information.private class CustomAssemblyLoadContext : AssemblyLoadContext { protected override Assembly Load(AssemblyName assemblyName) { var appDir = Path.GetDirectoryName(typeof(App).Assembly.Location); var assemblyPath = Path.Combine(appDir, assemblyName.Name + ".dll"); return File.Exists(assemblyPath) ? LoadFromAssemblyPath(assemblyPath) : null; } } // In application code, var alc = new CustomAssemblyLoadContext(); var assembly = alc.LoadFromAssemblyName(new AssemblyName("SomeLibrary")); assembly.GetType("SomeLibrary.SomeClass").GetMethod("SomeMethod").Invoke(null, null);
The sample code shown above may not work if the prefer_dotnet_aot
manifest property is enabled in tizen-manifest.xml
because application DLLs can have their native images located in either bin
or bin/.native_image
. In such cases, consider using ApplicationInfo.ExecutablePath from the TizenFX API instead of Assembly.Location.
If you need help, please contact me at swift.kim@samsung.com.
For further information about Tizen .NET, see Samsung Developers Tizen.