Introduction
Moving a .NET application to Windows Arm64 can be straightforward when the application consists mostly of managed .NET code. The situation becomes more complicated when the application depends on NuGet packages, native DLLs, database drivers, third-party SDKs, or other components that were built for a specific processor architecture.
A project can compile successfully and still fail after it is deployed to an Arm64 machine.
This is why dependency checking should happen before the migration rather than after the first production deployment.
The goal is not to inspect every package manually. The goal is to identify dependencies that can affect runtime compatibility, determine whether Arm64 support exists, and replace or isolate anything that cannot run on the target architecture.
This article walks through a practical dependency audit for a .NET application before moving it to Windows Arm64.
Why Dependency Checking Matters
The .NET application itself may be architecture-neutral, but its dependencies are not necessarily architecture-neutral.
Consider an application with this structure:
ASP.NET Core Application
|
+-- Managed .NET libraries
|
+-- NuGet packages
|
+-- Native DLL
|
+-- Database driver
|
+-- Third-party SDKThe managed libraries may work without modification.
The native DLL or SDK may only support x64.
That creates a situation where:
Application builds successfully
↓
Application starts
↓
Native dependency is loaded
↓
Runtime failureDependency validation is intended to catch that problem before deployment.
Start With the Target Framework
First identify the target framework used by the application.
Open the project file:
<TargetFramework>net10.0</TargetFramework>You may also see:
<TargetFramework>net8.0</TargetFramework>or a multi-targeted project:
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>The target framework matters because Arm64 support depends partly on the runtime and framework version being used.
Also check the SDK installed on the development machine:
dotnet --infoLook at:
.NET SDK version
Runtime versions
OS
OS architecture
Installed runtimes
Do this on both the development machine and the intended Arm64 test environment when possible.
List All NuGet Dependencies
The first practical step is to create an inventory of the application's packages.
For SDK-style projects, use:
dotnet list packageFor newer .NET SDK versions, you may also encounter:
dotnet package listThe command output gives you a starting point for identifying direct and transitive dependencies.
For example:
Top-level Package
> Microsoft.Extensions.Http
> Newtonsoft.Json
> Some.Database.Client
> Some.Native.LibraryDo not stop at the direct packages.
A package you explicitly installed may depend on another package that contains native runtime assets.
Separate Managed and Native Dependencies
This is one of the most important steps in the audit.
A fully managed assembly generally does not contain CPU-specific machine code in the same way a native DLL does.
For example:
using Newtonsoft.Json;
var json = JsonConvert.SerializeObject(
new { Name = "Developer" });
Console.WriteLine(json);The application does not need a different C# implementation for Arm64 simply because it uses JSON serialization.
A native dependency is different.
For example:
[DllImport("NativeProcessor.dll")]
private static extern int ProcessData();The CLR must load NativeProcessor.dll into the process.
If that DLL is x64-only, an Arm64 process can encounter a loading failure.
This distinction should guide your dependency review.
Inspect the Runtime Assets in NuGet Packages
NuGet packages can contain architecture-specific files under the runtimes directory.
A package may contain assets such as:
runtimes/
win-x64/
native/
library.dllAnother package may provide:
runtimes/
win-x64/
native/
library.dll
win-arm64/
native/
library.dllThe second package is much more useful for a native Arm64 deployment because it explicitly provides an Arm64 runtime asset.
When reviewing a package, look for directories such as:
runtimes/win-arm64and:
runtimes/win-x64The presence of win-arm64 is a strong compatibility signal, although it should still be validated with the actual package version you plan to deploy.
Check the Assets File
For deeper dependency analysis, inspect the generated NuGet assets file:
obj/project.assets.jsonThis file contains information about how NuGet resolved packages for the project.
Search for:
win-arm64and:
win-x64You may also search for:
nativeThis can help identify packages that bring architecture-specific assets into the application.
For example:
"runtime": {
"win-arm64": {
"NativeLibrary.dll": {}
}
}indicates that the dependency provides an Arm64 runtime asset.
Do not manually edit project.assets.json. It is generated by NuGet and should be treated as diagnostic output.
Use dotnet nuget why to Trace Dependencies
A package that looks harmless may be introduced by another dependency.
Modern .NET SDK versions provide:
dotnet nuget whyFor example:
dotnet nuget why MyApplication.csproj Some.Native.PackageThis helps answer an important question:
Why is this package part of my application?
The dependency chain might look like:
MyApplication
|
+-- Reporting.Package
|
+-- Native.Rendering.PackageYou may not have installed Native.Rendering.Package directly, but your application still depends on it.
This is especially useful when investigating large applications with many transitive dependencies.
Check Native DLLs in the Application
NuGet is only part of the dependency picture.
Some applications carry native DLLs directly inside the repository.
Search the project and output directories for files such as:
*.dll
*.exe
*.sysNot every DLL is native, so the file extension alone is not enough to determine compatibility.
Pay particular attention to libraries loaded using:
DllImportor:
NativeLibrary.LoadFor example:
using System.Runtime.InteropServices;
var handle = NativeLibrary.Load("NativeProcessor.dll");These calls should trigger a manual review.
Review P/Invoke Code
Platform invocation, commonly called P/Invoke, is another area that deserves attention.
For example:
[DllImport("user32.dll")]
private static extern int MessageBox(
IntPtr hWnd,
string text,
string caption,
uint type);Operating-system APIs may have Arm64-compatible implementations, but custom native libraries are more likely to cause migration problems.
Search the codebase for:
DllImport
LibraryImport
NativeLibrary.Load
NativeLibrary.TryLoadAlso search for project files and configuration that reference native libraries.
For newer applications, you may see source-generated interop such as:
[LibraryImport("NativeProcessor")]
private static partial int ProcessData();The same architecture question still applies: the native library must support the target architecture.
Check Database Drivers
Database connectivity deserves a separate review because database providers can have native components.
Start by identifying the database libraries used by the application.
Examples include:
SQL Server providers
PostgreSQL providers
Oracle providers
SQLite libraries
ODBC-based integrations
Vendor-specific database SDKs
A fully managed provider may be straightforward.
A provider that depends on native client libraries requires additional verification.
For example, an application might use:
Application
↓
.NET Database Provider
↓
Native Database Client
↓
Database Server
Join the conversation! Your thoughts help the community grow.