Client-Side File Generation and File Downloads on Modern Browsers

Sometimes, you just need an extension method to generate and download files from the client-side of most modern browsers. The only real takeaway from the code below is that Internet Explorer browser agents like to behave differently and require a different function call to provide this type of functionality. Other than that its pretty clear-cut, with most other modern browsers supporting the download attribute.

$(document).ready(function() {
    saveFile("Example.txt", "data:attachment/text", "Hello, world.");
});

function saveFile (name, type, data) {
    if (data != null && navigator.msSaveBlob)
        return navigator.msSaveBlob(new Blob([data], { type: type }), name);
    var a = $("<a style='display: none;'/>");
    var url = window.URL.createObjectURL(new Blob([data], {type: type}));
    a.attr("href", url);
    a.attr("download", name);
    $("body").append(a);
    a[0].click();
    window.URL.revokeObjectURL(url);
    a.remove();
}

Generating Self-Signed X509 Certificates in Vanilla .NET and C#

To generate a self-signed X509 certificate out of the box with vanilla .NET and C#, you will need to Platform Invoke the Cryptography library in Windows. You don’t need to use any third party libraries for this, Windows is perfectly capable of tackling this task for you. Wired up for using the SHA512 hashing algorithm, here is the code to make the magic happen:

public struct SystemTime
{
	public Int16 Year;
	public Int16 Month;
	public Int16 DayOfWeek;
	public Int16 Day;
	public Int16 Hour;
	public Int16 Minute;
	public Int16 Second;
	public Int16 Milliseconds;
}

public static class MarshalHelper
{
	public static void CheckReturnValue(Boolean nativeCallSucceeded)
	{
		if (!nativeCallSucceeded)
			Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
	}
}

public static class DateTimeExtensions
{
	[DllImport("kernel32.dll", SetLastError = true)]
	static extern Boolean FileTimeToSystemTime(ref Int64 fileTime, out SystemTime systemTime);

	public static SystemTime ToSystemTime(this DateTime dateTime)
	{
		Int64 fileTime = dateTime.ToFileTime();
		SystemTime systemTime;
		MarshalHelper.CheckReturnValue(FileTimeToSystemTime(ref fileTime, out systemTime));
		return systemTime;
	}
}

class X509Certificate2Helper
{
	[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
	static extern Boolean CryptAcquireContextW(out IntPtr providerContext, String container, String provider, UInt32 providerType, UInt32 flags);
	
	[DllImport("advapi32.dll", SetLastError = true)]
	static extern Boolean CryptReleaseContext(IntPtr providerContext, Int32 flags);
	
	[DllImport("advapi32.dll", SetLastError = true)]
	static extern Boolean CryptGenKey(IntPtr providerContext, Int32 algorithmId, Int32 flags, out IntPtr cryptKeyHandle);
	
	[DllImport("advapi32.dll", SetLastError = true)]
	static extern Boolean CryptDestroyKey(IntPtr cryptKeyHandle);
	
	[DllImport("crypt32.dll", SetLastError = true)]
	static extern Boolean CertStrToNameW(Int32 certificateEncodingType, IntPtr x500, Int32 strType, IntPtr reserved, Byte[] encoded, ref Int32 encodedLength, out IntPtr errorString);
	
	[DllImport("crypt32.dll", SetLastError = true)]
	static extern IntPtr CertCreateSelfSignCertificate(IntPtr providerHandle, ref CryptoApiBlob subjectIssuerBlob, Int32 flags, ref CryptKeyProviderInformation keyProviderInformation, IntPtr signatureAlgorithm, ref SystemTime startTime, ref SystemTime endTime, IntPtr extensions);
	
	[DllImport("crypt32.dll", SetLastError = true)]
	static extern Boolean CertFreeCertificateContext(IntPtr certificateContext);
	
	[DllImport("crypt32.dll", SetLastError = true)]
	static extern Boolean CertSetCertificateContextProperty(IntPtr certificateContext, Int32 propertyId, Int32 flags, ref CryptKeyProviderInformation data);

	public static X509Certificate2 GenerateSelfSignedCertificate(String name = "", DateTime? startTime = null, DateTime? endTime = null)
	{
		if (startTime == null || (DateTime)startTime < DateTime.FromFileTimeUtc(0))
			startTime = DateTime.FromFileTimeUtc(0);
		var startSystemTime = ((DateTime)startTime).ToSystemTime();
		if (endTime == null)
			endTime = DateTime.MaxValue;
		var endSystemTime = ((DateTime)endTime).ToSystemTime();
		String containerName = Guid.NewGuid().ToString();
		GCHandle dataHandle = new GCHandle();
		IntPtr providerContext = IntPtr.Zero;
		IntPtr cryptKey = IntPtr.Zero;
		IntPtr certificateContext = IntPtr.Zero;
		IntPtr algorithmPoInt32er = IntPtr.Zero;
		RuntimeHelpers.PrepareConstrainedRegions();
		try
		{
			MarshalHelper.CheckReturnValue(CryptAcquireContextW(out providerContext, containerName, null, 0x1, 0x8));
			MarshalHelper.CheckReturnValue(CryptGenKey(providerContext, 0x1, 0x20000001, out cryptKey));
			IntPtr errorStringPtr;
			Int32 nameDataLength = 0;
			Byte[] nameData;
			dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
			if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, null, ref nameDataLength, out errorStringPtr))
			{
				String error = Marshal.PtrToStringUni(errorStringPtr);
				throw new ArgumentException(error);
			}
			nameData = new Byte[nameDataLength];
			if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr))
			{
				String error = Marshal.PtrToStringUni(errorStringPtr);
				throw new ArgumentException(error);
			}
			dataHandle.Free();
			dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
			CryptoApiBlob nameBlob = new CryptoApiBlob { cbData = (UInt32)nameData.Length, pbData = dataHandle.AddrOfPinnedObject() };
			dataHandle.Free();
			CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 };
			CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "1.2.840.113549.1.1.13", Parameters = new CryptoApiBlob() };
			algorithmPoInt32er = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm));
			Marshal.StructureToPtr(algorithm, algorithmPoInt32er, false);
			certificateContext = CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref keyProvider, algorithmPoInt32er, ref startSystemTime, ref endSystemTime, IntPtr.Zero);
			MarshalHelper.CheckReturnValue(certificateContext != IntPtr.Zero);
			return new X509Certificate2(certificateContext);
		}
		finally
		{
			if (dataHandle.IsAllocated)
				dataHandle.Free();
			if (certificateContext != IntPtr.Zero)
				CertFreeCertificateContext(certificateContext);
			if (cryptKey != IntPtr.Zero)
				CryptDestroyKey(cryptKey);
			if (providerContext != IntPtr.Zero)
				CryptReleaseContext(providerContext, 0);
			if (algorithmPoInt32er != IntPtr.Zero)
			{
				Marshal.DestroyStructure(algorithmPoInt32er, typeof(CryptAlgorithmIdentifier));
				Marshal.FreeHGlobal(algorithmPoInt32er);
			}
		}
   	}

	struct CryptoApiBlob
	{
		public UInt32 cbData;
		public IntPtr pbData;
	}
	
	struct CryptAlgorithmIdentifier {
		[MarshalAs(UnmanagedType.LPStr)]
		public String pszObjId;
		public CryptoApiBlob Parameters;
	}
	
	struct CryptKeyProviderInformation
	{
		[MarshalAs(UnmanagedType.LPWStr)]
		public String pwszContainerName;
		[MarshalAs(UnmanagedType.LPWStr)]
		public String pwszProvName;
		public UInt32 dwProvType;
		public UInt32 dwFlags;
		public UInt32 cProvParam;
		public IntPtr rgProvParam;
		public UInt32 dwKeySpec;
	}
}

To call this class, specify the common name to your self-signed certificate, and actually get a certificate out of it (in PFX format with a simple password for it’s private key):

var certificate = X509Certificate2Helper.GenerateSelfSignedCertificate("CN = localhost");
File.WriteAllBytes(@"C:\Users\User\Desktop\Certificate.pfx", certificate.Export(X509ContentType.Pfx, "password"));

How to Bypass SSL Security Checks in Firefox

In Firefox, you can bypass SSL security checks in order to do local development on machines you trust the identity of. To do this:

  • Type about:config into the URL field of Firefox and hit the enter key.
  • Find security.tls.insecure_fallback_hosts, double click its value and type your domain(s) there. For example: localhost,dev.local.

This should resolve any Secure Connection Failed errors if you are seeing these on your development machines, but also, its not something you should do for any website out there, because it can easily compromise security.

How to Center an Image Vertically and Horizontally in HTML

It all depends on the size of the image. You need to set your top edge and left edge to 50%, then subtract half of the content size. So for example, my image below has a width of 300px, so I subtract 150px from the left edge by setting the left margin and do the same operation on the top margin.

<!DOCTYPE html>
<html>
<body>
	<div style="position:absolute;top:50%;margin-top:-179px;left:50%;margin-left:-150px;">
		<img src="image.png" width="300" height="358">
	</div>
</body>
</html>

How to Draw and Scale an XNA Sprite to Fit the Entire Screen of an iOS MonoGame Project

This example may be a little bit overboard for most intents and purposes, but this example shows you how you can draw a sprite such that it gets scaled to exactly fit the entire screen of the device you are using for simulation. I tested this using Xamarin Studio 5.9.1 and the latest MonoGame NuGet package available for iOS at the time.

void DrawToFitScreen(Texture2D texture)
{
	var destinationRectangle = new Rectangle (0, 0, GraphicsDevice.PresentationParameters.Bounds.Width, GraphicsDevice.PresentationParameters.Bounds.Height);
	var scale = new Vector2(texture.Width >= GraphicsDevice.PresentationParameters.Bounds.Width ? (float)GraphicsDevice.PresentationParameters.Bounds.Width / texture.Width : (float)texture.Width / GraphicsDevice.PresentationParameters.Bounds.Width,
		texture.Height >= GraphicsDevice.PresentationParameters.Bounds.Height ? (float)GraphicsDevice.PresentationParameters.Bounds.Height / texture.Height : (float)texture.Height / GraphicsDevice.PresentationParameters.Bounds.Height); 
	spriteBatch.Draw(texture, 
		null, 
		destinationRectangle,
		texture.Bounds,
		Vector2.Zero, 
		0, 
		scale,
		Color.White,
		SpriteEffects.None, 
		0);
}

It can easily be called within the Draw method:

spriteBatch.Begin();
DrawToFitScreen(someTexture);
spriteBatch.End();

How to Style a MenuItem with no Icon or Separator in WPF

First of all, you should be using Microsoft Expression Blend to do any sort of control styling in WPF. It’s probably already installed on your system as it comes with different flavors of Visual Studio. I created a copy of the default MenuItemStyle template and started surgery on the hideous icon and separator that normally gets packaged with the simple MenuItem control.

I had to set the following line (found in two separate places):

<ColumnDefinition MinWidth="22" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>

To a column with zero width:

<ColumnDefinition SharedSizeGroup="MenuItemIconColumnGroup" Width="0"/>

I also had to remove rectangles which normally fill with the separator. So for example, this line:

<Rectangle Fill="{StaticResource Menu.Static.Separator}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>

Had to be collapsed as follows:

<Rectangle Visibility="Collapsed" Fill="{StaticResource Menu.Static.Separator}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>

This line:

<Rectangle Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>

It also had to be collapsed:

<Rectangle Visibility="Collapsed" Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>

In the end, you get a set of resources as follows (its a heavy control, so it has a lot of markup):

<SolidColorBrush x:Key="MenuItem.Highlight.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="MenuItem.Highlight.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Menu.Disabled.Foreground" Color="#FF707070"/>
<SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Background" Color="#0A000000"/>
<SolidColorBrush x:Key="MenuItem.Highlight.Disabled.Border" Color="#21000000"/>
<SolidColorBrush x:Key="MenuItem.Selected.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="MenuItem.Selected.Background" Color="#3D26A0DA"/>
<Geometry x:Key="Checkmark">F1 M 10.0,1.2 L 4.7,9.1 L 4.5,9.1 L 0,5.2 L 1.3,3.5 L 4.3,6.1L 8.3,0 L 10.0,1.2 Z</Geometry>
<SolidColorBrush x:Key="Menu.Static.Foreground" Color="#FF212121"/>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
	<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Height="22" SnapsToDevicePixels="true">
		<Grid Margin="-1">
			<Grid.ColumnDefinitions>
				<ColumnDefinition SharedSizeGroup="MenuItemIconColumnGroup" Width="0"/>
				<ColumnDefinition Width="13"/>
				<ColumnDefinition Width="*"/>
				<ColumnDefinition Width="30"/>
				<ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
				<ColumnDefinition Width="20"/>
			</Grid.ColumnDefinitions>
			<ContentPresenter x:Name="Icon" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
			<Border x:Name="GlyphPanel" BorderBrush="{StaticResource MenuItem.Selected.Border}" BorderThickness="1" Background="{StaticResource MenuItem.Selected.Background}" ClipToBounds="False" HorizontalAlignment="Center" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22">
				<Path x:Name="Glyph" Data="{StaticResource Checkmark}" Fill="{StaticResource Menu.Static.Foreground}" FlowDirection="LeftToRight" Height="11" Width="10"/>
			</Border>
			<ContentPresenter x:Name="menuHeaderContainer" Grid.Column="2" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
			<TextBlock x:Name="menuGestureText" Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
		</Grid>
	</Border>
	<ControlTemplate.Triggers>
		<Trigger Property="Icon" Value="{x:Null}">
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsChecked" Value="True">
			<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsHighlighted" Value="True">
			<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Background}"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Border}"/>
		</Trigger>
		<Trigger Property="IsEnabled" Value="False">
			<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{StaticResource Menu.Disabled.Foreground}"/>
			<Setter Property="Fill" TargetName="Glyph" Value="{StaticResource Menu.Disabled.Foreground}"/>
		</Trigger>
		<MultiTrigger>
			<MultiTrigger.Conditions>
				<Condition Property="IsHighlighted" Value="True"/>
				<Condition Property="IsEnabled" Value="False"/>
			</MultiTrigger.Conditions>
			<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Background}"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Border}"/>
		</MultiTrigger>
	</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="Menu.Static.Border" Color="#FF999999"/>
<SolidColorBrush x:Key="Menu.Static.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="Menu.Static.Separator" Color="#FFD7D7D7"/>
<Geometry x:Key="UpArrow">M 0,4 L 3.5,0 L 7,4 Z</Geometry>
<Style x:Key="MenuScrollButton" BasedOn="{x:Null}" TargetType="{x:Type RepeatButton}">
	<Setter Property="ClickMode" Value="Hover"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type RepeatButton}">
				<Border x:Name="templateRoot" BorderBrush="Transparent" BorderThickness="1" Background="Transparent" SnapsToDevicePixels="true">
					<ContentPresenter HorizontalAlignment="Center" Margin="6" VerticalAlignment="Center"/>
				</Border>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>
<MenuScrollingVisibilityConverter x:Key="MenuScrollingVisibilityConverter"/>
<Geometry x:Key="DownArrow">M 0,0 L 3.5,4 L 7,0 Z</Geometry>
<Style x:Key="{ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}" BasedOn="{x:Null}" TargetType="{x:Type ScrollViewer}">
	<Setter Property="HorizontalScrollBarVisibility" Value="Hidden"/>
	<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type ScrollViewer}">
				<Grid SnapsToDevicePixels="true">
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="*"/>
					</Grid.ColumnDefinitions>
					<Grid.RowDefinitions>
						<RowDefinition Height="Auto"/>
						<RowDefinition Height="*"/>
						<RowDefinition Height="Auto"/>
					</Grid.RowDefinitions>
					<Border Grid.Column="0" Grid.Row="1">
						<ScrollContentPresenter CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}"/>
					</Border>
					<RepeatButton Grid.Column="0" CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Command="{x:Static ScrollBar.LineUpCommand}" Focusable="false" Grid.Row="0" Style="{StaticResource MenuScrollButton}">
						<RepeatButton.Visibility>
							<MultiBinding ConverterParameter="0" Converter="{StaticResource MenuScrollingVisibilityConverter}" FallbackValue="Visibility.Collapsed">
								<Binding Path="ComputedVerticalScrollBarVisibility" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="VerticalOffset" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="ExtentHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="ViewportHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
							</MultiBinding>
						</RepeatButton.Visibility>
						<Path Data="{StaticResource UpArrow}" Fill="{StaticResource Menu.Static.Foreground}"/>
					</RepeatButton>
					<RepeatButton Grid.Column="0" CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Command="{x:Static ScrollBar.LineDownCommand}" Focusable="false" Grid.Row="2" Style="{StaticResource MenuScrollButton}">
						<RepeatButton.Visibility>
							<MultiBinding ConverterParameter="100" Converter="{StaticResource MenuScrollingVisibilityConverter}" FallbackValue="Visibility.Collapsed">
								<Binding Path="ComputedVerticalScrollBarVisibility" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="VerticalOffset" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="ExtentHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
								<Binding Path="ViewportHeight" RelativeSource="{RelativeSource TemplatedParent}"/>
							</MultiBinding>
						</RepeatButton.Visibility>
						<Path Data="{StaticResource DownArrow}" Fill="{StaticResource Menu.Static.Foreground}"/>
					</RepeatButton>
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
	<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
		<Grid VerticalAlignment="Center">
			<Grid.ColumnDefinitions>
				<ColumnDefinition Width="Auto"/>
				<ColumnDefinition Width="Auto"/>
			</Grid.ColumnDefinitions>
			<ContentPresenter x:Name="Icon" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
			<Path x:Name="GlyphPanel" Data="{StaticResource Checkmark}" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
			<ContentPresenter Grid.Column="1" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
			<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" PlacementTarget="{Binding ElementName=templateRoot}">
				<Border x:Name="SubMenuBorder" BorderBrush="{StaticResource Menu.Static.Border}" BorderThickness="1" Background="{StaticResource Menu.Static.Background}" Padding="2">
					<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
						<Grid RenderOptions.ClearTypeHint="Enabled">
							<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
								<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
							</Canvas>
							<Rectangle Visibility="Collapsed" Fill="{StaticResource Menu.Static.Separator}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
							<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
						</Grid>
					</ScrollViewer>
				</Border>
			</Popup>
		</Grid>
	</Border>
	<ControlTemplate.Triggers>
		<Trigger Property="IsSuspendingPopupAnimation" Value="true">
			<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
		</Trigger>
		<Trigger Property="Icon" Value="{x:Null}">
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsChecked" Value="true">
			<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsHighlighted" Value="True">
			<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Background}"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Border}"/>
		</Trigger>
		<Trigger Property="IsEnabled" Value="False">
			<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{StaticResource Menu.Disabled.Foreground}"/>
			<Setter Property="Fill" TargetName="GlyphPanel" Value="{StaticResource Menu.Disabled.Foreground}"/>
		</Trigger>
		<Trigger Property="ScrollViewer.CanContentScroll" SourceName="SubMenuScrollViewer" Value="false">
			<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
			<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
		</Trigger>
	</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
	<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
		<Grid VerticalAlignment="Center">
			<Grid.ColumnDefinitions>
				<ColumnDefinition Width="Auto"/>
				<ColumnDefinition Width="Auto"/>
			</Grid.ColumnDefinitions>
			<ContentPresenter x:Name="Icon" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
			<Path x:Name="GlyphPanel" Data="{StaticResource Checkmark}" Fill="{StaticResource Menu.Static.Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
			<ContentPresenter Grid.Column="1" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
		</Grid>
	</Border>
	<ControlTemplate.Triggers>
		<Trigger Property="Icon" Value="{x:Null}">
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsChecked" Value="true">
			<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsHighlighted" Value="True">
			<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Background}"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Border}"/>
		</Trigger>
		<Trigger Property="IsEnabled" Value="False">
			<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{StaticResource Menu.Disabled.Foreground}"/>
			<Setter Property="Fill" TargetName="GlyphPanel" Value="{StaticResource Menu.Disabled.Foreground}"/>
		</Trigger>
		<MultiTrigger>
			<MultiTrigger.Conditions>
				<Condition Property="IsHighlighted" Value="True"/>
				<Condition Property="IsEnabled" Value="False"/>
			</MultiTrigger.Conditions>
			<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Background}"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Disabled.Border}"/>
		</MultiTrigger>
	</ControlTemplate.Triggers>
</ControlTemplate>
<Geometry x:Key="RightArrow">M 0,0 L 4,3.5 L 0,7 Z</Geometry>
<ControlTemplate x:Key="{ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}" TargetType="{x:Type MenuItem}">
	<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Height="22" SnapsToDevicePixels="true">
		<Grid Margin="-1">
			<Grid.ColumnDefinitions>
				<ColumnDefinition SharedSizeGroup="MenuItemIconColumnGroup" Width="0"/>
				<ColumnDefinition Width="13"/>
				<ColumnDefinition Width="*"/>
				<ColumnDefinition Width="30"/>
				<ColumnDefinition SharedSizeGroup="MenuItemIGTColumnGroup" Width="Auto"/>
				<ColumnDefinition Width="20"/>
			</Grid.ColumnDefinitions>
			<ContentPresenter x:Name="Icon" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
			<Border x:Name="GlyphPanel" BorderBrush="{StaticResource MenuItem.Highlight.Border}" BorderThickness="1" Background="{StaticResource MenuItem.Highlight.Background}" Height="22" Margin="-1,0,0,0" Visibility="Hidden" VerticalAlignment="Center" Width="22">
				<Path x:Name="Glyph" Data="{DynamicResource Checkmark}" Fill="{StaticResource Menu.Static.Foreground}" FlowDirection="LeftToRight" Height="11" Width="9"/>
			</Border>
			<ContentPresenter Grid.Column="2" ContentSource="Header" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
			<TextBlock Grid.Column="4" Margin="{TemplateBinding Padding}" Opacity="0.7" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
			<Path x:Name="RightArrow" Grid.Column="5" Data="{StaticResource RightArrow}" Fill="{StaticResource Menu.Static.Foreground}" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Center"/>
			<Popup x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" HorizontalOffset="-2" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Right" VerticalOffset="-3">
				<Border x:Name="SubMenuBorder" BorderBrush="{StaticResource Menu.Static.Border}" BorderThickness="1" Background="{StaticResource Menu.Static.Background}" Padding="2">
					<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
						<Grid RenderOptions.ClearTypeHint="Enabled">
							<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
								<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
							</Canvas>
							<Rectangle Visibility="Collapsed" Fill="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
							<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
						</Grid>
					</ScrollViewer>
				</Border>
			</Popup>
		</Grid>
	</Border>
	<ControlTemplate.Triggers>
		<Trigger Property="IsSuspendingPopupAnimation" Value="true">
			<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
		</Trigger>
		<Trigger Property="Icon" Value="{x:Null}">
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsChecked" Value="True">
			<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
			<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
		</Trigger>
		<Trigger Property="IsHighlighted" Value="True">
			<Setter Property="Background" TargetName="templateRoot" Value="Transparent"/>
			<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource MenuItem.Highlight.Border}"/>
		</Trigger>
		<Trigger Property="IsEnabled" Value="False">
			<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{StaticResource Menu.Disabled.Foreground}"/>
			<Setter Property="Fill" TargetName="Glyph" Value="{StaticResource Menu.Disabled.Foreground}"/>
			<Setter Property="Fill" TargetName="RightArrow" Value="{StaticResource Menu.Disabled.Foreground}"/>
		</Trigger>
		<Trigger Property="ScrollViewer.CanContentScroll" SourceName="SubMenuScrollViewer" Value="false">
			<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
			<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
		</Trigger>
	</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="MenuItemStyle1" TargetType="{x:Type MenuItem}">
	<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
	<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
	<Setter Property="Background" Value="Transparent"/>
	<Setter Property="BorderBrush" Value="Transparent"/>
	<Setter Property="BorderThickness" Value="1"/>
	<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
	<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
	<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
	<Style.Triggers>
		<Trigger Property="Role" Value="TopLevelHeader">
			<Setter Property="Background" Value="Transparent"/>
			<Setter Property="BorderBrush" Value="Transparent"/>
			<Setter Property="Foreground" Value="{StaticResource Menu.Static.Foreground}"/>
			<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
			<Setter Property="Padding" Value="6,0"/>
		</Trigger>
		<Trigger Property="Role" Value="TopLevelItem">
			<Setter Property="Background" Value="{StaticResource Menu.Static.Background}"/>
			<Setter Property="BorderBrush" Value="{StaticResource Menu.Static.Border}"/>
			<Setter Property="Foreground" Value="{StaticResource Menu.Static.Foreground}"/>
			<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
			<Setter Property="Padding" Value="6,0"/>
		</Trigger>
		<Trigger Property="Role" Value="SubmenuHeader">
			<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
		</Trigger>
	</Style.Triggers>
</Style>

You can add the above into your window’s resources and set the style on each menu item:

<Menu>
	<MenuItem Header="File" Style="{DynamicResource MenuItemStyle1}">
		<MenuItem Header="Open" Style="{DynamicResource MenuItemStyle1}"/>
		<MenuItem Header="Save" Style="{DynamicResource MenuItemStyle1}"/>
		<MenuItem Header="Close" Style="{DynamicResource MenuItemStyle1}"/>
	</MenuItem>
	<MenuItem Header="Tools" Style="{DynamicResource MenuItemStyle1}">
		<MenuItem Header="Settings" Style="{DynamicResource MenuItemStyle1}">
			<MenuItem Header="Local" Style="{DynamicResource MenuItemStyle1}"/>
			<MenuItem Header="Remote" Style="{DynamicResource MenuItemStyle1}"/>
		</MenuItem>
	</MenuItem>
</Menu>

Environment.Is64BitOperatingSystem Alternative for Older .NET Versions

If you are using a version of .NET older than .NET 4, you can use the following logic to check if the current environment is x64 or x86 (this method works almost the same way the .NET framework getter for the Is64BitOperatingSystem flag works; if you don’t believe me, you can use dotPeek to find out for yourself):

public static class EnvironmentHelper
{
    [DllImport("kernel32.dll")]
    static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll")]
    static extern IntPtr GetModuleHandle(string moduleName);

    [DllImport("kernel32.dll")]
    static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("kernel32.dll")]
    static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);

    public static bool Is64BitOperatingSystem()
    {
        // Check if this process is natively an x64 process. If it is, it will only run on x64 environments, thus, the environment must be x64.
        if (IntPtr.Size == 8)
            return true;
        // Check if this process is an x86 process running on an x64 environment.
        IntPtr moduleHandle = GetModuleHandle("kernel32");
        if (moduleHandle != IntPtr.Zero)
        {
            IntPtr processAddress = GetProcAddress(moduleHandle, "IsWow64Process");
            if (processAddress != IntPtr.Zero)
            {
                bool result;
                if (IsWow64Process(GetCurrentProcess(), out result))
                    return result;
            }
        }
        // The environment must be an x86 environment.
        return false;
    }
}

Example usage:

EnvironmentHelper.Is64BitOperatingSystem();

How to Obtain a RegistryKey Object for the 64-Bit Windows Registry from a 32-Bit Process without .NET 4 or Above

It is totally doable to fetch a RegistryKey object for the 64-bit and 32-bit Windows registries with .NET versions prior to .NET 4. Using reflection this can be achieved as follows, by calling some unexposed constructors that are a part of these frameworks (even previous framework editions contain them, but just don’t necessarily expose them):

public static class RegistryExtensions
{
	public enum RegistryHiveType
	{
		X86,
		X64
	}

	static Dictionary<RegistryHive, UIntPtr> _hiveKeys = new Dictionary<RegistryHive, UIntPtr> {
		{ RegistryHive.ClassesRoot, new UIntPtr(0x80000000u) },
		{ RegistryHive.CurrentConfig, new UIntPtr(0x80000005u) },
		{ RegistryHive.CurrentUser, new UIntPtr(0x80000001u) },
		{ RegistryHive.DynData, new UIntPtr(0x80000006u) },
		{ RegistryHive.LocalMachine, new UIntPtr(0x80000002u) },
		{ RegistryHive.PerformanceData, new UIntPtr(0x80000004u) },
		{ RegistryHive.Users, new UIntPtr(0x80000003u) }
	};

	static Dictionary<RegistryHiveType, RegistryAccessMask> _accessMasks = new Dictionary<RegistryHiveType, RegistryAccessMask> {
		{ RegistryHiveType.X64, RegistryAccessMask.Wow6464 },
		{ RegistryHiveType.X86, RegistryAccessMask.WoW6432 }
	};

	[Flags]
	public enum RegistryAccessMask
	{
		QueryValue = 0x0001,
		SetValue = 0x0002,
		CreateSubKey = 0x0004,
		EnumerateSubKeys = 0x0008,
		Notify = 0x0010,
		CreateLink = 0x0020,
		WoW6432 = 0x0200,
		Wow6464 = 0x0100,
		Write = 0x20006,
		Read = 0x20019,
		Execute = 0x20019,
		AllAccess = 0xF003F
	}

	[DllImport("advapi32.dll")]
	public static extern int RegOpenKeyEx(
	  UIntPtr hKey,
	  string subKey,
	  uint ulOptions,
	  uint samDesired,
	  out IntPtr hkResult);

	public static RegistryKey OpenBaseKey(RegistryHive registryHive, RegistryHiveType registryType)
	{
		UIntPtr hiveKey = _hiveKeys[registryHive];
		if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major > 5)
		{
			RegistryAccessMask flags = RegistryAccessMask.QueryValue | RegistryAccessMask.EnumerateSubKeys | RegistryAccessMask.SetValue | RegistryAccessMask.CreateSubKey | _accessMasks[registryType];
			IntPtr keyHandlePointer = IntPtr.Zero;
			int result = RegOpenKeyEx(hiveKey, null, 0, (uint)flags, out keyHandlePointer);
			if (result == 0)
			{
				var safeRegistryHandleType = typeof(SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
				var safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET < 4
				if (safeRegistryHandleConstructor == null)
					safeRegistryHandleConstructor = safeRegistryHandleType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(IntPtr), typeof(bool) }, null); // .NET >= 4
				var keyHandle = safeRegistryHandleConstructor.Invoke(new object[] { keyHandlePointer, true });
				var net3Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { safeRegistryHandleType, typeof(bool) }, null);
				var net4Constructor = typeof(RegistryKey).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }, null);
				object key;
				if (net4Constructor != null)
					key = net4Constructor.Invoke(new object[] { keyHandlePointer, true, false, false, hiveKey == _hiveKeys[RegistryHive.PerformanceData] });
				else if (net3Constructor != null)
					key = net3Constructor.Invoke(new object[] { keyHandle, true });
				else
				{
					var keyFromHandleMethod = typeof(RegistryKey).GetMethod("FromHandle", BindingFlags.Static | BindingFlags.Public, null, new[] { safeRegistryHandleType }, null);
					key = keyFromHandleMethod.Invoke(null, new object[] { keyHandle });
				}
				var field = typeof(RegistryKey).GetField("keyName", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
					field.SetValue(key, String.Empty);
				return (RegistryKey)key;
			}
			else if (result == 2) // The key does not exist.
				return null;
			throw new Win32Exception(result);
		}
		throw new PlatformNotSupportedException("The platform or operating system must be Windows 2000 or later.");
	}
}

Example usage:

RegistryKey localMachineX64 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X64);
RegistryKey localMachineX86 = RegistryExtensions.OpenBaseKey(RegistryHive.LocalMachine, RegistryExtensions.RegistryHiveType.X86);

Once you have a RegistryKey object reference, it should work just fine with the existing RegistryKey methods.

How to Enumerate All Open Handles for All Processes on a Windows Machine

Have you ever wanted to walk through all of the open handles across all processes that are active on a Windows machine and even be able to do things like close specific handles that those processes have open? I sure have, and I did…but more specifically for my needs I wanted to know what processes had file handles open to specific files on my machine, or what processes had locked specific folders, and so forth. The following code snippet I am going to share is working code, complete with a nice unmanaged library wrapper in case you want to use it through the CLR with your favourite C# projects. It calls on some very low level APIs. These APIs are not your run of the mill, Windows application-friendly type of APIs, so be warned that like all other code on my site, you are responsible for what you do with it. I take no responsibility for any damages. If you happen to bring open handles to a close, that could result in a system-wide catastrophe, the results of which are completely unpredictable relative to what process had that handle open.

UnmanagedLibrary.h:

#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <sstream>
#include <map>
#include <vector>

using namespace std;

void CheckForLocks(wstring fullPath, vector<unsigned long> *processes);
bool CloseHandles(wstring fullPath, int process);

UnmanagedLibrary.cpp:

#include "UnmanagedLibrary.h"

#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2

typedef NTSTATUS(NTAPI *_NtQuerySystemInformation)(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
typedef NTSTATUS(NTAPI *_NtDuplicateObject)(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, ULONG Options);
typedef NTSTATUS(NTAPI *_NtQueryObject)(HANDLE ObjectHandle, ULONG ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength);

typedef struct _UNICODE_STRING
{
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _SYSTEM_HANDLE
{
	ULONG ProcessId;
	BYTE ObjectTypeNumber;
	BYTE Flags;
	USHORT Handle;
	PVOID Object;
	ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	ULONG HandleCount;
	SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef enum _POOL_TYPE
{
	NonPagedPool,
	PagedPool,
	NonPagedPoolMustSucceed,
	DontUseThisType,
	NonPagedPoolCacheAligned,
	PagedPoolCacheAligned,
	NonPagedPoolCacheAlignedMustS
} POOL_TYPE, *PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION
{
	UNICODE_STRING Name;
	ULONG TotalNumberOfObjects;
	ULONG TotalNumberOfHandles;
	ULONG TotalPagedPoolUsage;
	ULONG TotalNonPagedPoolUsage;
	ULONG TotalNamePoolUsage;
	ULONG TotalHandleTableUsage;
	ULONG HighWaterNumberOfObjects;
	ULONG HighWaterNumberOfHandles;
	ULONG HighWaterPagedPoolUsage;
	ULONG HighWaterNonPagedPoolUsage;
	ULONG HighWaterNamePoolUsage;
	ULONG HighWaterHandleTableUsage;
	ULONG InvalidAttributes;
	GENERIC_MAPPING GenericMapping;
	ULONG ValidAccess;
	BOOLEAN SecurityRequired;
	BOOLEAN MaintainHandleCount;
	USHORT MaintainTypeList;
	POOL_TYPE PoolType;
	ULONG PagedPoolUsage;
	ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

using namespace std;

PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
{
	return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}

_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");

struct QueryStructure
{
	HANDLE dupHandle;
	PVOID objectNameInfo;
	ULONG objectInfoLength;
	ULONG returnLength;
	NTSTATUS result;
};

HANDLE beginQuery = CreateEvent(0, FALSE, FALSE, 0);
HANDLE endQuery = CreateEvent(0, FALSE, FALSE, 0);
QueryStructure queryStructure;

HANDLE beginQueryCloseHandle = CreateEvent(0, FALSE, FALSE, 0);
HANDLE endQueryCloseHandle = CreateEvent(0, FALSE, FALSE, 0);
QueryStructure queryStructureCloseHandle;

DWORD WINAPI queryThread(LPVOID parameter)
{
	while (WaitForSingleObject(beginQuery, INFINITE) == WAIT_OBJECT_0)
	{
		queryStructure.result = NtQueryObject(queryStructure.dupHandle, ObjectNameInformation, queryStructure.objectNameInfo, queryStructure.objectInfoLength, &queryStructure.returnLength);
		SetEvent(endQuery);
	}
	return 0;
}

DWORD WINAPI queryThreadCloseHandle(LPVOID parameter)
{
	while (WaitForSingleObject(beginQueryCloseHandle, INFINITE) == WAIT_OBJECT_0)
	{
		queryStructureCloseHandle.result = NtQueryObject(queryStructureCloseHandle.dupHandle, ObjectNameInformation, queryStructureCloseHandle.objectNameInfo, queryStructureCloseHandle.objectInfoLength, &queryStructureCloseHandle.returnLength);
		SetEvent(endQueryCloseHandle);
	}
	return 0;
}

HANDLE queryThreadHandle = CreateThread(0, 0, &queryThread, 0, 0, 0);
HANDLE queryThreadCloseHandleHandle = CreateThread(0, 0, &queryThreadCloseHandle, 0, 0, 0);

void ConvertPath(wstring *path, map<wstring, wstring> *volumes)
{
	for (map<wstring, wstring>::iterator i = (*volumes).begin(); i != (*volumes).end(); ++i)
	{
		if ((*path).compare(0, (*i).first.size(), (*i).first) == 0)
		{
			*path = (*path).replace(0, (*i).first.size(), (*i).second);
			break;
		}
	}
}

void CheckForLocks(wstring fullPath, vector<unsigned long> *processes)
{
	DWORD	CharCount = 0;
	WCHAR	DeviceName[MAX_PATH] = L"";
	HANDLE	FindHandle = INVALID_HANDLE_VALUE;
	size_t	Index = 0;
	BOOL	Success = FALSE;
	WCHAR	VolumeName[MAX_PATH] = L"";
	map<wstring, wstring> volumes;
	FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
	while (true)
	{
		Index = wcslen(VolumeName) - 1;
		if (VolumeName[0] != L'\\' || VolumeName[1] != L'\\' || VolumeName[2] != L'?' || VolumeName[3] != L'\\' || VolumeName[Index] != L'\\')
		{
			break;
		}
		VolumeName[Index] = L'\0';
		CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));
		VolumeName[Index] = L'\\';
		if (CharCount == 0)
		{
			break;
		}
		DWORD	size = MAX_PATH + 1;
		PWCHAR	name = NULL;
		BOOL	success = FALSE;
		while (!success)
		{
			name = (PWCHAR) new BYTE[size * sizeof(WCHAR)];
			success = GetVolumePathNamesForVolumeNameW(VolumeName, name, size, &size);
			if (!success)
			{
				delete[] name;
				name = NULL;
			}
		}
		volumes[DeviceName + wstring(L"\\")] = name;
		if (name != NULL)
		{
			delete[] name;
			name = NULL;
		}
		Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
		if (!Success)
		{
			if (GetLastError() != ERROR_NO_MORE_FILES)
			{
				break;
			}
			break;
		}
	}
	FindVolumeClose(FindHandle);
	FindHandle = INVALID_HANDLE_VALUE;
	NTSTATUS status;
	PSYSTEM_HANDLE_INFORMATION handleInfo;
	ULONG handleInfoSize = 0x10000;
	HANDLE processHandle;
	ULONG i;
	DWORD pid;
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 process;
	ZeroMemory(&process, sizeof(process));
	process.dwSize = sizeof(process);
	if (Process32First(snapshot, &process))
	{
		do
		{
			pid = process.th32ProcessID;
			if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid)))
			{
				continue;
			}
			handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
			while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
			{
				handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
			}
			if (!NT_SUCCESS(status))
			{
				return;
			}
			for (i = 0; i < handleInfo->HandleCount; i++)
			{
				SYSTEM_HANDLE handle = handleInfo->Handles[i];
				HANDLE dupHandle = NULL;
				POBJECT_TYPE_INFORMATION objectTypeInfo;
				PVOID objectNameInfo;
				UNICODE_STRING objectName;
				ULONG returnLength = 0;
				if (handle.ProcessId != pid)
					continue;
				if (!NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, 0)))
				{
					continue;
				}
				objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
				size_t currentSize = 0x1000;
				objectNameInfo = malloc(currentSize);
				queryStructure.dupHandle = dupHandle;
				queryStructure.objectNameInfo = objectNameInfo;
				queryStructure.objectInfoLength = 0x1000;
				queryStructure.returnLength = returnLength;
				queryStructure.result = -1;
				SetEvent(beginQuery);
				if (WaitForSingleObject(endQuery, 100) == WAIT_TIMEOUT)
				{
					TerminateThread(queryThreadHandle, 1);
					CloseHandle(queryThreadHandle);
					queryThreadHandle = CreateThread(0, 0, &queryThread, 0, 0, 0);
					CloseHandle(dupHandle);
					continue;
				}
				if (!NT_SUCCESS(queryStructure.result))
				{
					objectNameInfo = realloc(objectNameInfo, currentSize *= 2);
					if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, returnLength, NULL)))
					{
						free(objectTypeInfo);
						free(objectNameInfo);
						CloseHandle(dupHandle);
						continue;
					}
				}
				objectName = *(PUNICODE_STRING)objectNameInfo;
				if (objectName.Length)
				{
					wstring objectNameAsWString = wstring(objectName.Buffer, objectName.Length / sizeof(WCHAR));
					ConvertPath(&objectNameAsWString, &volumes);
					if ((int)objectNameAsWString.find(fullPath) >= 0)
					{
						(*processes).push_back(pid);
						free(objectTypeInfo);
						free(objectNameInfo);
						CloseHandle(dupHandle);
						break;
					}
				}
				free(objectTypeInfo);
				free(objectNameInfo);
				CloseHandle(dupHandle);
			}
			free(handleInfo);
			CloseHandle(processHandle);
		} while (Process32Next(snapshot, &process));
	}
}

bool CloseHandles(wstring fullPath, int process)
{
	DWORD	CharCount = 0;
	WCHAR	DeviceName[MAX_PATH] = L"";
	HANDLE	FindHandle = INVALID_HANDLE_VALUE;
	size_t	Index = 0;
	BOOL	Success = FALSE;
	WCHAR	VolumeName[MAX_PATH] = L"";
	map<wstring, wstring> volumes;
	FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
	while (true)
	{
		Index = wcslen(VolumeName) - 1;
		if (VolumeName[0] != L'\\' || VolumeName[1] != L'\\' || VolumeName[2] != L'?' || VolumeName[3] != L'\\' || VolumeName[Index] != L'\\')
		{
			break;
		}
		VolumeName[Index] = L'\0';
		CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));
		VolumeName[Index] = L'\\';
		if (CharCount == 0)
		{
			break;
		}
		DWORD	size = MAX_PATH + 1;
		PWCHAR	name = NULL;
		BOOL	success = FALSE;
		while (!success)
		{
			name = (PWCHAR) new BYTE[size * sizeof(WCHAR)];
			success = GetVolumePathNamesForVolumeNameW(VolumeName, name, size, &size);
			if (!success)
			{
				delete[] name;
				name = NULL;
			}
		}
		volumes[DeviceName + wstring(L"\\")] = name;
		if (name != NULL)
		{
			delete[] name;
			name = NULL;
		}
		Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
		if (!Success)
		{
			if (GetLastError() != ERROR_NO_MORE_FILES)
			{
				break;
			}
			break;
		}
	}
	FindVolumeClose(FindHandle);
	FindHandle = INVALID_HANDLE_VALUE;
	_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
	_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
	_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
	NTSTATUS status;
	PSYSTEM_HANDLE_INFORMATION handleInfo;
	ULONG handleInfoSize = 0x10000;
	HANDLE processHandle;
	ULONG i;
	if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, process)))
	{
		return false;
	}
	handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
	while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)
	{
		handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
	}
	if (!NT_SUCCESS(status))
	{
		return false;
	}
	for (i = 0; i < handleInfo->HandleCount; i++)
	{
		SYSTEM_HANDLE handle = handleInfo->Handles[i];
		HANDLE dupHandle = NULL;
		POBJECT_TYPE_INFORMATION objectTypeInfo;
		PVOID objectNameInfo;
		UNICODE_STRING objectName;
		ULONG returnLength = 0;
		if (handle.ProcessId != process)
			continue;
		if (!NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, 0)))
		{
			continue;
		}
		objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
		size_t currentSize = 0x1000;
		objectNameInfo = malloc(currentSize);
		queryStructureCloseHandle.dupHandle = dupHandle;
		queryStructureCloseHandle.objectNameInfo = objectNameInfo;
		queryStructureCloseHandle.objectInfoLength = 0x1000;
		queryStructureCloseHandle.returnLength = returnLength;
		queryStructureCloseHandle.result = -1;
		SetEvent(beginQueryCloseHandle);
		if (WaitForSingleObject(endQueryCloseHandle, 100) == WAIT_TIMEOUT)
		{
			TerminateThread(queryThreadCloseHandleHandle, 1);
			CloseHandle(queryThreadCloseHandleHandle);
			queryThreadCloseHandleHandle = CreateThread(0, 0, &queryThreadCloseHandle, 0, 0, 0);
			CloseHandle(dupHandle);
			continue;
		}
		if (!NT_SUCCESS(queryStructureCloseHandle.result))
		{
			objectNameInfo = realloc(objectNameInfo, currentSize *= 2);
			if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, returnLength, NULL)))
			{
				free(objectTypeInfo);
				free(objectNameInfo);
				CloseHandle(dupHandle);
				continue;
			}
		}
		objectName = *(PUNICODE_STRING)objectNameInfo;
		if (objectName.Length)
		{
			wstring objectNameAsWString = wstring(objectName.Buffer, objectName.Length / sizeof(WCHAR));
			ConvertPath(&objectNameAsWString, &volumes);
			if ((int)objectNameAsWString.find(fullPath) >= 0)
			{
				NtDuplicateObject(processHandle, (HANDLE)handle.Handle, 0, 0, 0, 0, 1);
			}
		}
		free(objectTypeInfo);
		free(objectNameInfo);
		CloseHandle(dupHandle);
	}
	free(handleInfo);
	CloseHandle(processHandle);
	return true;
}

Note: The two most important methods above are firstly, CheckForLocks, which takes either a system drive path, file path, or folder path, and returns a vector of processes with open handles at the given path, and secondly CloseHandles, which takes the same types of paths and a process ID. CloseHandles closes any open handles (containing the specified path) of a target process via a specified process ID. Below, I will even include some code to produce a CLR wrapper around this Static Library so that you can call it from C#.

Unmanaged.h:

#include "..\UnmanagedLibrary\UnmanagedLibrary.h"

using namespace System;
using namespace System::Collections::Generic;

namespace Unmanaged
{
	public ref class Handles
	{
	public:
		static List<int>^ GetProcessesWithOpenHandlesToPath(String^ path);
		static bool CloseOpenHandlesToPathInProcess(String^ path, int process);
	};
}

Unmanaged.cpp:

#include "Unmanaged.h"
#include "msclr\marshal_cppstd.h"

List<int>^ Unmanaged::Handles::GetProcessesWithOpenHandlesToPath(String^ path)
{
	List<int>^ list = gcnew List<int>();
	wstring convertedString = msclr::interop::marshal_as<wstring>(path);
	vector<unsigned long> processes;
	CheckForLocks(convertedString, &processes);
	for (vector<unsigned long>::iterator i = processes.begin(); i != processes.end(); i++) {
		list->Add(*i);
	}
	return list;
}

bool Unmanaged::Handles::CloseOpenHandlesToPathInProcess(String^ path, int process)
{
	List<int>^ list = gcnew List<int>();
	wstring convertedString = msclr::interop::marshal_as<wstring>(path);
	return CloseHandles(convertedString, process);
}

From C#, you simply call these two functions of the Unmanaged DLL:

List<int> processes = Unmanaged.Handles.GetProcessesWithOpenHandlesToPath(path);
Unmanaged.Handles.CloseOpenHandlesToPathInProcess(path, processID);

If you study the code well, you will realize that the only specialty item here is that you need to terminate threads while you enumerate handles in order to stop deadlocks from happening. There is one special, odd, weird (whatever you want to call it) case in which deadlocks happen, and that is when you call NtQueryObject on handles which reference synchronous file objects (chances are that the file object was opened by a kernel-mode driver with FILE_SYNCHRONOUS_IO_NONALERT). The thread will wait indefinitely to acquire the file lock because interrupting the wait isn’t possible, which means you have to terminate the thread.

Working with Big Numbers in C#

I recently set out to do some universal gravitation calculations in code, but I soon found that a lot of the existing or built-in data-types (Int64, Decimal, etc.) for doing so in .NET were rather inadequate (they either did not allow a high degree of precision, or they did not allow numbers large enough to suit my needs), so I searched for something that I could use to model very large numbers to an acceptably large degree of accuracy. That’s when I came across the BigRational class on CodePlex. This class decomposes large fractions into two BigInteger values (one to represent the numerator and another to represent the denominator), since any fractional number can be decomposed as such. I kept on royally butchering my by-hand calculations (since there are so many big numbers at play, along with unit conversions, etc.), and I got fed up to the point that I set out to create a code-based model in order to calculate surface gravities for the Sun and all of the planets in our Solar System using Newton’s law of universal gravitation:

using Numerics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace UniversalPhysics
{

    public static class BigRationals
    {
        public static BigRational WithExponent(BigRational value, BigInteger exponent)
        {
            return BigRational.Multiply(value, BigRational.Pow(new BigRational(10, 1), exponent));
        }

        public static BigRational FromInteger(BigInteger value)
        {
            return new BigRational(value);
        }

        public static BigRational FromDouble(double value)
        {
            if (value == 0 || Double.IsNaN(value))
            {
                return new BigRational(0, 1);
            }
            if (Double.IsPositiveInfinity(value))
            {
                value = Double.MaxValue;
            }
            if (Double.IsNegativeInfinity(value))
            {
                value = Double.MinValue;
            }
            double fraction = value % 1;
            double numerator = value - fraction;
            double denominator = 1;
            // Notes:
            // 
            // - You are at the mercy of the precision of the double data type.
            // - If you debug this, you will see that the fraction variable changes in precision.
            // - The double data type does this to accomodate similar levels of precision.
            // - This may result in a slight skew of a very large value variable or a very small value variable.
            // - This effect is negligible when the value's precision is much less than Double.MaxValue's precision.
            while (!Double.IsNaN(fraction))
            {
                double decimalPlace = fraction * 10 * denominator - (fraction * 10 * denominator) % 1;
                if (Double.IsInfinity(numerator * 10 + decimalPlace))
                {
                    break;
                }
                if (Double.IsInfinity(denominator * 10))
                {
                    break;
                }
                denominator *= 10;
                numerator = numerator * 10 + decimalPlace;
                fraction = fraction - decimalPlace / denominator;
            }
            BigInteger numeratorAsBigInteger = new BigInteger(numerator);
            BigInteger denominatorAsBigInteger = new BigInteger(denominator);
            return new BigRational(numeratorAsBigInteger, denominatorAsBigInteger);
        }

        public static BigRational FromDoubleWithExponent(double value, BigInteger exponent)
        {
            return WithExponent(FromDouble(value), exponent);
        }
    }

    class Program
    {
        
        static SolarSystem System = new SolarSystem();

        static void Main(string[] args)
        {
            Console.WriteLine("Sun's Surface Gravity = " + (double)System.Sun.SurfaceGravity);
            Console.WriteLine("Mercury's Surface Gravity = " + (double)System.Mercury.SurfaceGravity);
            Console.WriteLine("Venus' Surface Gravity = " + (double)System.Venus.SurfaceGravity);
            Console.WriteLine("Earth's Surface Gravity = " + (double)System.Earth.SurfaceGravity);
            Console.WriteLine("Mars' Surface Gravity = " + (double)System.Mars.SurfaceGravity);
            Console.WriteLine("Jupiter's Surface Gravity = " + (double)System.Jupiter.SurfaceGravity);
            Console.WriteLine("Saturn's Surface Gravity = " + (double)System.Saturn.SurfaceGravity);
            Console.WriteLine("Uranus' Surface Gravity = " + (double)System.Uranus.SurfaceGravity);
            Console.WriteLine("Neptune's Surface Gravity = " + (double)System.Neptune.SurfaceGravity);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }

    public class SolarSystem
    {

        public Planet Sun;
        public Planet Mercury;
        public Planet Venus;
        public Planet Earth;
        public Planet Mars;
        public Planet Jupiter;
        public Planet Saturn;
        public Planet Uranus;
        public Planet Neptune;

        public SolarSystem()
        {
            // Notes:
            //
            // - When viewed from above the Sun's north pole, the sun spins counter-clockwise on its axis.
            // - Prograde motion, when viewed from above the Sun's north pole, means counter-clockwise motion.
            // - Retrograde motion, when viewed from above the Sun's north pole, means clockwise motion.
            //
            // Putting this all together, when viewed from above the Sun's north pole:
            //
            // - The sun spins counter-clockwise.
            // - Venus and Uranus are the only two planets which have clockwise orbits.
            //      - All other planets have counter-clockwise orbits.
            // - Venus is the only planet that spins clockwise on its axis.
            //      - All other planets spin counter-clockwise on their axes.
            //      - Uranus kind-of also spins counter-clockwise, but the angle defined by its axial tilt makes it quite an oddball.
            //          - It doesn't spin like the other planets.
            //          - If you were the Sun, and a car in front of you was facing you with its front, and that car started to drive towards you, the way the wheels would spin on that car would be similar to the direction Uranus spins.
            //
            // What does this all mean?
            //
            // - Well, a planet's sidereal rotation period is really defined by axial tilt.
            // - Axial tilt (or obliquity), is the angle between an object's rotational axis and its orbital axis.
            //      - Equivalently, its the angle between a planet's equatorial plane and orbital plane.
            //      - It differs from orbital inclination.
            // - Don't take what I say for granted until you apply axial tilt for yourself, because I've made approximations with terms like clockwise and counter-clockwise.
            Sun = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.98855, 30),
                MeanRadius = BigRationals.FromInteger(696342000),
                SiderealRotationPeriod = TimeSpan.FromDays(25.05)
            };
            Mercury = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(3.3022, 23),
                MeanRadius = BigRationals.FromInteger(2439700),
                SiderealRotationPeriod = TimeSpan.FromHours(1407.5)
            };
            Venus = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(4.8676, 24),
                MeanRadius = BigRationals.FromInteger(6051800),
                SiderealRotationPeriod = TimeSpan.FromDays(243.0185) // Retrograde (this planet orbits the Sun in the opposite spin direction of the Sun).
            };
            Earth = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(5.97219, 24),
                MeanRadius = BigRationals.FromInteger(6371000),
                SiderealRotationPeriod = new TimeSpan(0, 23, 56, 4, 100)
            };
            Mars = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(6.4185, 23),
                MeanRadius = BigRationals.FromInteger(3389500),
                SiderealRotationPeriod = new TimeSpan(24, 37, 22)
            };
            Jupiter = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.8986, 27),
                MeanRadius = BigRationals.FromInteger(69911000),
                SiderealRotationPeriod = new TimeSpan(9, 55, 30)
            };
            Saturn = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(5.6846, 26),
                MeanRadius = BigRationals.FromInteger(58232000),
                SiderealRotationPeriod = new TimeSpan(10, 34, 0)
            };
            Uranus = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(8.681, 25),
                MeanRadius = BigRationals.FromInteger(25362000),
                SiderealRotationPeriod = new TimeSpan(17, 14, 24) // Retrograde (this planet orbits the Sun in the opposite spin direction of the Sun).
            };
            Neptune = new Planet
            {
                Mass = BigRationals.FromDoubleWithExponent(1.0243, 26),
                MeanRadius = BigRationals.FromInteger(24622000),
                SiderealRotationPeriod = new TimeSpan(16, 6, 36)
            };
        }
    }

    public class Planet
    {
        public BigRational Mass;
        public BigRational MeanRadius;
        public TimeSpan SiderealRotationPeriod;
        public BigRational AngularVelocity
        {
            get
            {
                return Laws.GetAngularVelocity(SiderealRotationPeriod);
            }
        }
        public BigRational SurfaceGravity
        {
            get
            {
                return Laws.GetNewtonianSurfaceGravity(Mass, MeanRadius);
            }
        }
    }

    public static class Laws
    {
        public static BigRational GetAngularVelocity(TimeSpan siderealRotationPeriod)
        {
            return BigRational.Divide(BigRational.Multiply(new BigRational(2.0), Constants.PI), BigRationals.FromDouble(siderealRotationPeriod.TotalSeconds));
        }

        public static BigRational GetNewtonianSurfaceGravity(BigRational mass, BigRational meanRadius)
        {
            return BigRational.Divide(BigRational.Multiply(Constants.GravitationalConstant, mass), BigRational.Pow(meanRadius, 2));
        }
    }

    public static class Constants
    {
        public static BigRational PI = BigRationals.FromDouble(3.141592654);
        public static BigRational GravitationalConstant = BigRationals.FromDoubleWithExponent(6.67384, -11);
        public static BigRational SpeedOfLight = BigRationals.FromInteger(299792458);
    }
}

Newton’s law of universal gravitation has been superseded by Einstein’s theory of general relativity, but it is still used as a good approximation of gravitational force. Relativity is for extreme precision, or for cases around the boundaries of Newton’s law of universal gravitation (very big masses, or very close massive objects). Alas, the output to all of that code above is as follows (the values obtained from Newton’s formulae are fairly close to the values from NASA’s Planetary Fact Sheet):

Sun's Surface Gravity = 273.695164677966
Mercury's Surface Gravity = 3.70259586050561
Venus' Surface Gravity = 8.86995750090755
Earth's Surface Gravity = 9.81960902526831
Mars' Surface Gravity = 3.72853358713613
Jupiter's Surface Gravity = 25.9249685707583
Saturn's Surface Gravity = 11.1879956428603
Uranus' Surface Gravity = 9.00696656052567
Neptune's Surface Gravity = 11.2760322511266
Press any key to exit.