After many hours of reverse engineering of assembly 'Microsoft.HyperV.PowerShell.Cmdlets' and 'Microsoft.HyperV.PowerShell.Objects', I finally found the best way to make a Hyper-V VM "enable NumLock by default".
TL;DR; Here's the Powershell code:
function Set-VMNumLock {
param(
[Microsoft.HyperV.PowerShell.ComputeResource]$vm,
[bool]$NumLockEnabled
)
# Get underlying settings object with reflection
$ComputerResourceType = [Microsoft.HyperV.PowerShell.ComputeResource]
$SettingField = $ComputerResourceType.GetDeclaredField("_settings")
$settingUpdater = $SettingField.GetValue($vm)
$vmComputerSystemView = $settingUpdater.GetData([Microsoft.HyperV.Powershell.UpdatePolicy]::EnsureUpdated)
$vmComputerSystemView.BiosNumLock = $NumLockEnabled
$vmComputerSystemView.Put()
}
Note: This setting is not modifiable when VM is powered on.
Here’s how to use it
$vm = Get-VM -VMName “SampleVM”
Set-VMNumLock -vm $vm -NumLockEnabled $true
FullStory
First I was inspired by some "WMI"/"CIM" Powershell code on some other website.(I'm not sure if I can write hyper-link of other websites here) Although that code does not work, it however did show me the underlying stuff about "Where is the numlock setting stored".
Eventually I decompiled 'Microsoft.HyperV.PowerShell.Cmdlets.dll' and 'Microsoft.HyperV.PowerShell.Objects.dll'. In class Microsoft.HyperV.PowerShell.Commands.SetVMFirmware, you will find everything is defined in Microsoft.HyperV.PowerShell.VMFirmware. Dig deeper, it's underlying data structure was
Microsoft.Virtualization.Client.Management.IVMComputerSystemSetting (It's actually shared by both Gen1 and Gen2 VMs), there's a boolean property called BiosNumLock.
Actually Microsoft's programmer is just one step away of supporting NumLock in Set-VMFirmware cmdlet.
In class VMBios, there's a property called NumLockEnabled which is basically a warp of that BiosNumLock
namespace Microsoft.HyperV.PowerShell;
public sealed class VMBios : VMComponentObject, IUpdatable
{
private readonly DataUpdater<IVMComputerSystemSetting> m_VMSetting;
public bool NumLockEnabled
{
get
{
return m_VMSetting.GetData(UpdatePolicy.EnsureUpdated).BiosNumLock;
}
internal set
{
m_VMSetting.GetData(UpdatePolicy.None).BiosNumLock = value;
}
}
// ... other code
If Microsoft want to support NumLock in Gen2 VMs, they just paste this property to VMFirmware class. I don't know why they're too lazy to do it.