-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tour of .NET Behavior on Windows 11 Arm64 #7709
Comments
Yes, with the undocumented
Isn't that a regression in .NET 6 comparing to .NET Framework 4.8.1? The first three lines in the "netfx" logo need to be indented by one more space. Good article! |
Reported: dotnet/runtime#73974 |
Regarding the behavior for .NET Framework 4.8.1: is there no mechanism to enable Arm64 support at build time, other than building an Arm64-specific executable? Many .NET Framework apps (I'm thinking of client apps in particular) are distributed/deployed as loose, portable executables with no installer. Given the options described above, there's currently no convenient way to enable Arm64 support for these apps without either:
I would have imagined that, for example, targeting .NET Framework 4.8.1 would automatically cause an AnyCPU app to start as Arm64 (i.e. changing the target framework would act as confirmation that the app was made to work with Arm64). Or, perhaps, an app.config option similar to <supportedRuntime> could indicate that the app should run using the Arm64 runtime instead of the x64 one. Or, one could even imagine a new "AnyCPU, Arm64 preferred" configuration that assemblies could be built with. All of these alternatives would address the inability for .NET Framework apps to opt-in to Arm64 support without changing their deployment model. Many client apps, for better or for worse, are still built on .NET Framework, and this ability would allow them to trivially opt-in to Arm64 support. Given that "pure managed" apps with no unmanaged dependencies are very common, I would think that the return on a feature like this could be high, and the lack of it seems like a missed opportunity. |
Great question. We had the 32BitPrefered feature from the past. It is kinda similar. I think this would be NativeArchPrefered. We haven't built that. While simple, it is not straightforward. We are looking some other options to achieve similar results. At present, you've correctly determined the set of options available. Note that there isn't a magic feature like this coming for .NET 6/7/8. This limits our desire to invest in an expensive magic feature for .NET Framework. |
Now that .NET Framework 4.8.1 is available to Windows 10 on ARM as well, it would be helpful to document the behavior there too. I guess AnyCPU will run as x86 by default there. Also IIRC |
Good point. Yes, that should added to the doc, too. I am away on a trip so it will be a bit before that happens. |
The problem with NativeArchPrefered is, it will create even more issues if .NET Framework add yet another new architecture (think RISC-V?) to support 5-10 years down the road. Sure, hopefully .NET Framework would be dead by then, but you never know, it was presumed to be dead years ago when MS decided no .NET Framework on ARM64. |
We are not going to build NativeArchPrefered for this reason. The options we are looking at are future proof and naturally extend to potential new architectures. |
What is the point of targeting .NET Framework 4.8.1 right now, if not for supporting ARM64? I mean, no legacy project will bother to target net481 for some WinForms/WPF accessibility enhancements. AnyCPU being x64 for net48 (and older) makes perfectly sense. But it doesn't for net481. |
Just my 2 cents, Ie. if I write an app targeting 4.6 or some other similar version and include this config option it will be ignored on all frameworks up to 4.8.1/2/whenever it's added and then followed on a framework that understands it. This solution would allow full developer control on what architectures they want to be able to run in. Use case: I use the .NET Framework primarily as a bootstrapper for my .NET Core apps these days (and for some old apps and for older versions of some apps) - this allows a single exe to check .NET Core installations etc., and have no plans to stop using it since .NET Core will not be installed on Windows by default and isn't supported for like 10 years and isn't fully forwards compatible. I currently target .NET Framework 4.6.1 (for some reason?), but will eventually update to a newer version, but not past 4.8 since it doesn't support Windows 7 (until I eventually give up on supporting windows 7 but that's not directly related to what we're discussing here). I'd like the ability to be able to easily detect the os architecture in my bootstrapper which seems to be fixed in 4.8.1 (or earlier, see below), and be able to force older versions of my apps to be able to run in arm64 if I know it will work with ease. Also, I have a question. If a user launches an app compiled with a version ≤4.8 on an arm64 machine, what will OSArchitecture return? It should probably return x64 in this scenario unless my above feature is implemented and includes that architecture in the list (since this is what it does with 4.8 and lower - or at least whatever version I tested this with I think). Also a second question. If windows does support a new architecture in the future, is it safe to assume that they will all support x64 emulation? Or should I only assume x86 emulation is available? Is there a Windows API that tells me which processor architectures can be emulated from something like an image file machine constant Thanks for your time reading this lol. |
I don't think this is necessary, most AnyCPU .NET FX app can run as ARM64 unmodified, as long as it does not use things like P/Invoke to third party unmanaged dependencies. I even ran a .NET 4.5 app on ARM64 with If we does not limit it to .NET 4.8.x target where this is added, then end-user can try to make their app ARM64 native even without source code.
GetMachineTypeAttributes: https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-getmachinetypeattributes |
I do not expect my users to have to open the command line to open an app (which is why this doesn't solve it for a portable app), but I will use this when I have control over launching it.
As you said above, a user can use It might even be good that it's easy to change it permanently, e.g. if they have an old app that they want to force to run natively that doesn't have any native dependencies (they would probably find out very quickly whether it has native dependencies or not because it would crash, and could revert it).
Thanks! |
Also, can is different to do, as stated in the original post, these only run natively when compiled specifically for arm64. Otherwise they run in x64 emulation for compatibility (without things like running on the command line with |
OK, I misunderstood you, I thought you mean one have to target 4.8.x in order to use this config option. So we actually are on the same page. |
I just installed Windows 10 ARM for testing, and no, ARM64 .Net Framework is not available on Windows 10. Not exactly sure why though. |
|
Tangentially related question: Is there a plan to release a version of Microsoft.DotNet.Framework.NativeImageCompiler for Arm64? It works well for us on x86/x64 in places where we can't use Ngen.exe (the Microsoft Store), so it would be nice to provide the same experience on Arm64. |
Looks like it's deprecated, per the individual runtime packages https://www.nuget.org/packages/runtime.win10-x64.Microsoft.DotNet.Framework.NativeImageCompiler |
If it's really deprecated, it would be nice if the official documentation recommending its use was amended: https://learn.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-r2r
So, is the NgenR2R tool deprecated, or were those packages marked accidentally in some automated proces? Will it receive Arm64 support? @jkotas? |
Please give us an option to opt-in into ARM64 with the usual |
@hrumhurum Came here looking for the same, or at least a configuration manifest. I was also able to run a 4.5.2 target in ARM64 as long as it was being built with VS2022 and Platform set to ARM64, but I want my AnyCPU builds to run as ARM64 on ARM64 Windows :) |
The new Kind of wonder why it is in application manifest though, that would be a little bit tricky to backport to downlevel OS... https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#supportedarchitectures |
Tour of .NET Behavior on Windows 11 Arm64
We're going to take a tour of the basic behavior of .NET 6 and .NET Framework 4.8.1 on Windows 11 Arm64. This tour of the experience is going to give us a sense of which .NET variants can be installed on the machine, how to use them to target various architectures, and what various key APIs report. This tour will provide a good sense of the experience that you an expect and that is on offer.
The primary questions it answers are:
A subset of this document will be moved to official docs. In fact, it was created as a set of working notes to do just that. The author of those notes is sharing them with the hope that they will be a benefit for someone trying to figure out how best to target Windows Arm64.
The tour is on Windows 11 Arm64, however, you could just as easily use these instructions to build for Windows Arm64 on Windows x64 and then run the built apps on Windows Arm64. Also, much of what you'll see equally applies to macOS x64 and Arm64.
I'm using .NET 6.0.8 (.NET SDK 6.0.400) and .NET Framework 4.8.1. I'll be using Arm64, x64, and x86 variants of both. .NET 6 is not included in Windows 11 (or any other Windows version), while all three .NET Framework architecture variants are included. I'm using a Windows 11 Insider build (10.0.22621). The machine and OS are Arm64.
There is also one honorable mention of .NET 7, where its behavior differs from .NET 6.
Much of the tour relies on the
Developer Command Prompt
that comes with Visual Studio 2022. Visual Studio 2022 will soon be supported on Windows 11 Arm64.Test samples
We'll be using the following samples to test our environment:
Those are samples we maintain for containers, but they are also great for this use case.
PATH
All the .NET variants mentioned are installed on the Windows 11 Arm64 test machine that will be used for the tour.
The
PATH
is a key aspect of any dev experience and is critical to understand to using platform components (particularly tools).Let's take a look at the
PATH
:The following path segments relate to .NET:
C:\Program Files\dotnet\
C:\Users\rich\.dotnet\tools
There are two interesting aspects here:
There is no
PATH
entry for .NET Framework. That's nothing new. There has never been aPATH
entry for .NET Framework. It doesn't explicitly need one. If you want to use .NET Framework tools, you need to rely on theDeveloper Command Prompt for Visual Studio
.Building and running by default
First, .NET 6:
The app is running on Arm64, as expected.
Next, .NET Framework 4.8.1:
In contrast, the .NET Framework app is running on x64.
.NET Framework apps run as x64 by default on Windows Arm64. That was a key design decision made to aid compatibility. .NET Framework apps have been primarily running as x64 for over a decade on pervasively deployed x64 desktops, laptops, and servers. It's quite likely that many .NET Framework apps have x64 dependencies (like native libraries). Running them as emulated seemed like the safest bet, and to make Arm64 opt-in.
Targeting Arm64
Targeting Arm64 is easy with .NET 6. Your app will target the same architecture as the SDK you are using. You'll produce an Arm64 app by default as long as you are using the Arm64 SDK. That was just demonstrated above.
However, if you want to force targeting Arm64 (which will be more important later on the tour), you can use
-a arm64
as demonstrated in the following:There is a similar option with MSBuild, for .NET Framework, using the
Platform
property:You can also use the .NET SDK for the same purpose:
I'm using
dotnet run
for this demonstration, butdotnet build
works the same way.Targeting x64
Targeting x64 is just a variation on what you've just seen (mostly).
For .NET 6, we can continue using the Arm64 SDK, but target x64 with the same
-a
switch you just saw. That's the easiest and highest performance approach (the SDK is a lot of code and will run slowly when emulation).We can also use the x64 SDK, which will produce x64 apps by default. That's not the recommended approach (due to the emulation performance cost), but works fine.
There isn't a
C:\Program Files (x64)
on Windows Arm64. As a result, the x64 variant of .NET is installed inC:\Program Files\dotnet
in the childx64
directory. That's what you see demonstrated above. thex64
directory will only be there if you install the x64 .NET SDK. It doesn't come with the Arm64 SDK.To complete the demonstration, we can use the x64 SDK to produce Arm64 apps. That's also not recommended, but works.
For .NET Framework, x64 is the default target as demonstrated above. There isn't anything more to show.
Targeting x86
For .NET 6, this experience is very similar to targeting x64. We'll use the same approach.
First, using Arm64 SDK, again using the
-a
switch:Huh. Why did we drop from (near) 32GB to 2GB? Are 32-bit apps running in some small virtual machine used for emulation? How do I make it bigger? That's not it. 32-bit processes are limited to 4GB of memory and it's split 50/50 between kernel and user mode. That's why we're seeing 2GB.
We see slightly difference behavior with .NET 7 (specifically for
OSArchitecture
).Let's try with .NET Framework.
We can try the same thing with the .NET SDK:
Hmmm. That's interesting.
OSArchitecture
is reported differently for .NET 6 on one side and .NET 7 and .NET Framework 4.8.1 on the other when running x86 apps. They must use different Windows APIs to reportOSArchitecture
. They don't all report the same thing.Coercing already-built apps to Arm64
With .NET 6, you can (in some cases) run apps targeted for one architecture to another. We won't go into all the details on that, but will demonstrate the mechanism.
We'll first build and run the app natively, as has been demonstrated earlier.
Now, we'll use the x64
dotnet
to run the app.Every .NET (Core) app has a native launcher. It's a component of every app and ties each app to an operating system and architecture. However, you don't need to launch an app with the native launcher that the SDK provides. You can launch it with the
dotnet
host instead. That's what is being demonstrated above..NET Framework works differently. It has special executables that are not tied to an architecture in quite as direct a way. Lets try some techniques to coerce an x64 app to Arm64.
We'll rebuild the app again to ensure we're starting from a clean slate.
That looks good for our baseline.
Let's first try
start
:Our app now runs as Arm64.
runas
is another option that you can use to get a .NET Framework app to run as Arm64.This launches another console windows, which follows:
In this case, I had to add a
Console.ReadLine()
to the app. Otherwise, it would just open and close immediately.start
offers/wait
and/b
to work around that. For UI apps, that won't be a problem.You can also specify that you want your app to always run in the registry as Arm64.
Create the following registry key (rename for your app):
It would have this
DWORD
item and hex value:or as decimal:
You can see my registy entry.
Note: Is there a way to specify the absolute path for this key, not just the filename?
Let's try that:
That works as expected.
For folks that start processes from code, you can specify machine type by using the UpdateProcThreadAttribute function. See
PROC_THREAD_ATTRIBUTE_MACHINE_TYPE
.Visual Studio
The targeting experience is the same in Visual Studio as it is for MSBuild.
The text was updated successfully, but these errors were encountered: