Are NativeMethods-Styled Classes Needed in .NET 5.0?
Hello everyone. If you haven’t heard the news, .NET 5.0 is now upon us. At the time of writing, it’s the newest of the new, and it is the latest entry in Microsoft’s rather confusing (if you stop paying attention for any time at all) versioning and release strategy for its .NET technologies.
After all the work that has been done on the .NET Core side of things, it seems like this is Microsoft’s official christening of their successor to the .NET Framework. So, anyone working on anything new, or anything wanting support in the distant future, will probably be trying it out.
Since its release has coincided with a number of efforts I’m making in the creation of an Omnified technology stack, I made sure to switch to it right away so I could start working with it. I’ve been buried in too much assembly code and older .NET Framework application code as of late, so I had a lot of makeup learning to do in order to start to fully grasp all the changes that came with .NET Core development.
One thing I became familiar with rather quickly was the new way that code analysis was being done, in particular the new Roslyn analyzers that ship with the .NET 5.0 SDK.
Many of the rules are, for some reason, essentially disabled by default. I enabled them so I could see (with masochistic glee) what would be flagged. Not long after, I get hit with the following:
CA1060: Move pinvokes to native methods class
Right. Of course! I’ve always put my P/Invoke declarations into a NativeMethods-styled class. But I didn’t this time, with a new project targeting .NET 5.0, for reasons which caused me to be surprised to see it being flagged by Code Analysis. It was my belief that this practice would now be null and dead with the latest .NET.
Why did I think this way? We’ll get to my reasoning in a bit. First, let’s just quickly go over what CA1060 is and what the whole NativeMethods rigmarole is all about.
No Native Methods Outside of NativeMethods!
If we want to call into some unmanaged code, we need to make use of P/Invoke, also known as Platform Invocation Services, via the use of a the DllImportAttribute. If any methods decorated with this attribute exist in your code outside of a class named either NativeMethods, SafeNativeMethods, or UnsafeNativeMethods, you will trigger a CA1060 Code Analysis warning.
The recommendation to corral the P/Invoke methods into one of these classes is mainly driven by Code Access Security concerns. If operating in a CAS environment, .NET code can only execute unmanaged code if it has the UnmanagedCode permission.
Ensuring that a particular caller has this permission requires a full stack walk when the P/Invoke code is called. This can be expensive in terms of performance.
The way to get around this hit to performance is through the use of the SuppressUnmanagedCodeAttribute. Throwing this on a class causes the permission check to no longer occur, avoiding the stack walk. It will also basically allow for even potentially untrusted code to be able to call these methods.
So, in order to make it clear which code is using this attribute, with all of its potential security implications, the NativeMethods triad of classes was born.
I tend to ignore what Microsoft says, and look at what they do. And indeed, you will find throughout the (soon to be old) .NET Framework class libraries a faithful following of this practice.
But Must I Use NativeMethods-Styled Classes with .NET 5.0?
No. It’s a practice that will eventually fade into antiquity. There simply is no reason to do so anymore. And that’s because of a big change that came with .NET Core: the dropping of Code Access Security.
I always viewed Code Access Security as a fine example of overengineering that resulted in a silly amount of unnecessary work and consideration, and I’m simply overjoyed to see Microsoft chucking it out the window.
The purpose behind the different NativeMethods classes was entirely related to security implications that arose with Code Access Security and the SuppressUnmanagedCodeAttribute. Thus, my surprise with this code analysis rule still being a thing in the new .NET 5.0 SDK.
Still, it makes some sense to have some sort of organization behind your P/Invoke declarations. Also, as I said before, I tend to ignore what Microsoft says and look at what they do. So, in the new .NET 5.0 library sources, is Microsoft still using NativeMethods-styled classes?
Looks like Microsoft has eschewed the NativeMethods type and is instead throwing P/Invokes into an Interop class, with multiple other nested classes for methods belonging to particular modules.
If we look at the online reference source, we can further see that the Interop class and the various nested classes are scattered among multiple files as static partial classes (yes, something I haven’t run into too much myself), with each file owing to some particular type of operation or theme (i.e. a file for anything to do with reading a directory).
So there you have it. When going forward into the great unknown with your revolutionary new ideas and fabulous new coded technologies: if it’s targeting .NET 5.0, drop the NativeMethods act! And disable code analysis rule CA1060.
I myself will do as our Microsoft overlords are doing, and throw all my stuff into an internal static class named Interop. Looks slicker than NativeMethods anyway…