Repository: dotnet/runtime
File: src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs (C#)

Code before:
        }

        /// <summary>
        /// Get the CPU usage, including the process time spent running the application code, the process time spent running the operating system code,
        /// and the total time spent running both the application and operating system code.
        /// </summary>
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [UnsupportedOSPlatform("browser")]
        [SupportedOSPlatform("maccatalyst")]
        public static ProcessCpuUsage CpuUsage
        {
            get => Interop.Kernel32.GetProcessTimes(Interop.Kernel32.GetCurrentProcess(), out _, out _, out long procKernelTime, out long procUserTime) ?
                    new ProcessCpuUsage { UserTime = new TimeSpan(procUserTime), PrivilegedTime = new TimeSpan(procKernelTime) } :
                    new ProcessCpuUsage { UserTime = TimeSpan.Zero, PrivilegedTime = TimeSpan.Zero };
        }

        /// <summary>Gets the number of milliseconds elapsed since the system started.</summary>
        /// <value>A 64-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</value>
        public static long TickCount64
        {
            get
            {
                unsafe
                {
                    // While GetTickCount64 returns the number of milliseconds elapsed since the system started, it
                    // has a fixed resolution of 10-16ms and will never be more accurate even if the underlying timer
                    // interrupt resolution used by the system is changed. This causes the Windows behavior to differ
                    // from Linux or MacOS systems which allow for more fine grained resolution by default and can cause
                    // the system to be less responsive in the case an app is trying to opt into things like multimedia
                    // scenarios.
                    //
                    // Instead, we use QueryInterruptTime which does become more accurate if the underlying system
                    // resolution is improved. This returns the amount of uptime, including sleep or hibernation,
                    // since the system was started using 100ns ticks, so we still need to normalize back to 1ms.

                    ulong interruptTime;
                    Interop.Kernel32.QueryInterruptTime(&interruptTime);
                    return (long)interruptTime / TimeSpan.TicksPerMillisecond;
                }
            }
        }
    }
}


Review comment (bug):
I assume that this means that timers in my app may start behaving differently if some other app opts into more precise timers. I guess it is the reason why Windows have not just fixed GetTickCount to be more precise to avoid breaking buggy apps that are not robust against GetTickCount precision changing.

I think it is fine to make this change in .NET runtime. We may see a few reports of non-obvious behavior over time (resolve "by-design").

Code after:
        }

        /// <summary>
        /// Get the CPU usage, including the process time spent running the application code, the process time spent running the operating system code,
        /// and the total time spent running both the application and operating system code.
        /// </summary>
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        [UnsupportedOSPlatform("browser")]
        [SupportedOSPlatform("maccatalyst")]
        public static ProcessCpuUsage CpuUsage
        {
            get => Interop.Kernel32.GetProcessTimes(Interop.Kernel32.GetCurrentProcess(), out _, out _, out long procKernelTime, out long procUserTime) ?
                    new ProcessCpuUsage { UserTime = new TimeSpan(procUserTime), PrivilegedTime = new TimeSpan(procKernelTime) } :
                    new ProcessCpuUsage { UserTime = TimeSpan.Zero, PrivilegedTime = TimeSpan.Zero };
        }

        /// <summary>Gets the number of milliseconds elapsed since the system started.</summary>
        /// <value>A 64-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</value>
        public static long TickCount64
        {
            get
            {
                unsafe
                {
                    // GetTickCount64 uses fixed resolution of 10-16ms for backward compatibility. Use
                    // QueryUnbiasedInterruptTime instead which becomes more accurate if the underlying system
                    // resolution is improved. This helps responsiveness in the case an app is trying to opt
                    // into things like multimedia scenarios and additionally does not include "bias" from time
                    // the system is spent asleep or in hibernation.

                    ulong unbiasedTime;

                    Interop.BOOL result = Interop.Kernel32.QueryUnbiasedInterruptTime(&unbiasedTime);
                    // The P/Invoke is documented to only fail if a null-ptr is passed in
                    Debug.Assert(result != Interop.BOOL.FALSE);

                    return (long)(unbiasedTime / TimeSpan.TicksPerMillisecond);
                }
            }
        }
    }
}