Add display Win32k stubs and callback plumbing

Introduces Win32k syscall handlers for monitor/display queries (`EnumDisplayMonitors`, `EnumDisplaySettings`, `GetDisplayConfigBufferSizes`, `GetDpiForMonitor`) and adds guest-callback trampoline support so kernel-side handlers can invoke guest callbacks safely. It also improves runtime robustness by adding PID generation fallback behavior, wiring primary monitor handle/rect helpers, and exposing display frequency via `EnumDisplaySettingsW`.

On the Vulkan side, the generator now tracks optional members and emits that metadata into generated struct descriptors, and rebuild-time validation now rejects non-optional null arrays with non-zero declared counts.
This commit is contained in:
AdvDebug
2026-07-25 11:33:14 +03:00
parent ff446474e0
commit 0c2b8cf26b
9 changed files with 406 additions and 11 deletions
+7 -5
View File
@@ -56,6 +56,7 @@ namespace Brovan.Generators
public string AltLength;
public int ArrayLen = 1;
public string Values;
public bool Optional;
}
private sealed class VkStruct
@@ -248,7 +249,7 @@ namespace Brovan.Generators
continue;
ParseTyped(mem, out string ty, out string nm, out int pd, out bool isc);
int alen = pd == 0 ? ResolveArrayLen(mem, m.Constants) : 1;
s.Members.Add(new Member { Name = nm, Type = ty, PtrDepth = pd, IsConst = isc, Length = (string)mem.Attribute("len"), AltLength = (string)mem.Attribute("altlen"), ArrayLen = alen, Values = (string)mem.Attribute("values") });
s.Members.Add(new Member { Name = nm, Type = ty, PtrDepth = pd, IsConst = isc, Length = (string)mem.Attribute("len"), AltLength = (string)mem.Attribute("altlen"), ArrayLen = alen, Values = (string)mem.Attribute("values"), Optional = ((string)mem.Attribute("optional") ?? "").StartsWith("true", StringComparison.Ordinal) });
}
m.Structs[name] = s;
}
@@ -760,6 +761,7 @@ namespace Brovan.Generators
public string MemName;
public string LenName;
public string SelName;
public bool Optional;
}
private static readonly Dictionary<string, byte> KindNum = new Dictionary<string, byte>
@@ -816,7 +818,7 @@ namespace Brovan.Generators
private static MDesc ClassifyMember(Model m, VkStruct owner, Member mem, Dictionary<string, int> offsets, Dictionary<string, Layout> cache)
{
MDesc d = new MDesc { Offset = offsets.TryGetValue(mem.Name, out int mo) ? mo : 0, MemName = mem.Name };
MDesc d = new MDesc { Offset = offsets.TryGetValue(mem.Name, out int mo) ? mo : 0, MemName = mem.Name, Optional = mem.Optional };
if (mem.Name == "pNext") { d.Kind = "PNext"; d.Size = 8; return d; }
if (owner.Name == "VkWriteDescriptorSet" && (mem.Name == "pImageInfo" || mem.Name == "pBufferInfo" || mem.Name == "pTexelBufferView"))
{
@@ -1987,8 +1989,8 @@ namespace Brovan.Generators
sm.AppendLine(" internal enum BvkMK : byte { Scalar, Handle, StructValue, StructPtr, StructArray, HandleArray, ScalarArray, StringZ, StringArray, PNext, Ignore, BlobPtr, SelectArray }");
sm.AppendLine(" internal readonly struct BvkM");
sm.AppendLine(" {");
sm.AppendLine(" public readonly BvkMK Kind; public readonly int Offset; public readonly int Size; public readonly int Sub; public readonly int LenOffset; public readonly string HandleType; public readonly int SelOffset; public readonly uint SelMask;");
sm.AppendLine(" public BvkM(BvkMK k, int o, int s, int sub, int lo, string ht, int so, uint sm) { Kind = k; Offset = o; Size = s; Sub = sub; LenOffset = lo; HandleType = ht; SelOffset = so; SelMask = sm; }");
sm.AppendLine(" public readonly BvkMK Kind; public readonly int Offset; public readonly int Size; public readonly int Sub; public readonly int LenOffset; public readonly string HandleType; public readonly int SelOffset; public readonly uint SelMask; public readonly bool Optional;");
sm.AppendLine(" public BvkM(BvkMK k, int o, int s, int sub, int lo, string ht, int so, uint sm, bool opt = false) { Kind = k; Offset = o; Size = s; Sub = sub; LenOffset = lo; HandleType = ht; SelOffset = so; SelMask = sm; Optional = opt; }");
sm.AppendLine(" }");
sm.AppendLine(" internal static class BrovVulkStructMeta");
sm.AppendLine(" {");
@@ -2032,7 +2034,7 @@ namespace Brovan.Generators
if (d.Kind == "Scalar" && d.Size > 8 && d.MemName != null)
ga.Append("BVK_SAME_SIZE(s").Append(si).Append('_').Append(d.MemName)
.Append(", sizeof(((").Append(n).Append("*)0)->").Append(d.MemName).Append("), ").Append(d.Size).Append(");\n");
sm.Append("new BvkM(BvkMK.").Append(d.Kind).Append(", ").Append(d.Offset).Append(", ").Append(d.Size).Append(", ").Append(sub).Append(", ").Append(d.LenOffset).Append(", \"").Append(d.HandleType).Append("\", ").Append(d.SelOffset).Append(", ").Append(d.SelMask).Append("u), ");
sm.Append("new BvkM(BvkMK.").Append(d.Kind).Append(", ").Append(d.Offset).Append(", ").Append(d.Size).Append(", ").Append(sub).Append(", ").Append(d.LenOffset).Append(", \"").Append(d.HandleType).Append("\", ").Append(d.SelOffset).Append(", ").Append(d.SelMask).Append("u, ").Append(d.Optional ? "true" : "false").Append("), ");
gs.Append("{").Append(KindNum[d.Kind]).Append(",").Append(GuestOffset(n, d.MemName, d.Offset)).Append(",").Append(d.Size)
.Append(",").Append(GuestNativeSize(n, d))
.Append(",").Append(sub)
@@ -24,8 +24,12 @@ namespace Brovan.Core.Emulation.OS.Windows
private static void VerifyArrayLen(IntPtr dst, in BvkM d, uint n)
{
if (n != 0 && n != *(uint*)(dst + d.LenOffset))
uint Declared = *(uint*)(dst + d.LenOffset);
if (n != 0 && n != Declared)
throw new InvalidOperationException("BrovVulk generic: array length mismatch.");
if (n == 0 && Declared != 0 && !d.Optional)
throw new InvalidOperationException("BrovVulk generic: required array is null but its count is non-zero.");
}
public static IntPtr Rebuild(int sid, GenReader r, GenState st)
@@ -0,0 +1,47 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserEnumDisplayMonitors : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong Hdc = Instance.WinHelper.GetArg(0);
ulong EnumProc = Instance.WinHelper.GetArg(2);
ulong CallbackData = Instance.WinHelper.GetArg(3);
ulong Monitor = Instance.WinHelper.GetPrimaryMonitorHandle();
if (Monitor == 0 || !Instance.WinHelper.TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
if (EnumProc == 0)
{
Instance.SetRawSyscallReturn(1);
return NTSTATUS.STATUS_SUCCESS;
}
ulong Rect = Instance.WinHelper.EnsureGuestCallScratch();
if (Rect == 0)
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Instance._emulator.WriteMemory(Rect + 0x00, (uint)Left, 4);
Instance._emulator.WriteMemory(Rect + 0x04, (uint)Top, 4);
Instance._emulator.WriteMemory(Rect + 0x08, (uint)Right, 4);
Instance._emulator.WriteMemory(Rect + 0x0C, (uint)Bottom, 4);
if (!Instance.WinHelper.BeginGuestCall(EnumProc, Monitor, Hdc, Rect, CallbackData, 1))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,75 @@
using System.Text;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserEnumDisplaySettings : IWinSyscall
{
private const uint EnumCurrentSettings = 0xFFFFFFFF;
private const uint EnumRegistrySettings = 0xFFFFFFFE;
private const int DevModeSize = 0xDC;
private const int OffsetDeviceName = 0x00;
private const int OffsetSpecVersion = 0x40;
private const int OffsetDriverVersion = 0x42;
private const int OffsetSize = 0x44;
private const int OffsetFields = 0x48;
private const int OffsetPositionX = 0x4C;
private const int OffsetPositionY = 0x50;
private const int OffsetDisplayOrientation = 0x54;
private const int OffsetLogPixels = 0xA6;
private const int OffsetBitsPerPel = 0xA8;
private const int OffsetPelsWidth = 0xAC;
private const int OffsetPelsHeight = 0xB0;
private const int OffsetDisplayFlags = 0xB4;
private const int OffsetDisplayFrequency = 0xB8;
private const uint DmPosition = 0x00000020;
private const uint DmDisplayOrientation = 0x00000080;
private const uint DmLogPixels = 0x00020000;
private const uint DmBitsPerPel = 0x00040000;
private const uint DmPelsWidth = 0x00080000;
private const uint DmPelsHeight = 0x00100000;
private const uint DmDisplayFlags = 0x00200000;
private const uint DmDisplayFrequency = 0x00400000;
private const ushort SpecVersion = 0x0401;
private const uint BitsPerPixel = 32;
private const uint LogPixels = 96;
private const string DeviceName = @"\\.\DISPLAY1";
public NTSTATUS Handle(BinaryEmulator Instance)
{
uint ModeNumber = (uint)Instance.WinHelper.GetArg(1);
ulong DevModePtr = Instance.WinHelper.GetArg(2);
if (DevModePtr == 0 || !Instance.IsRegionMapped(DevModePtr, DevModeSize))
return NTSTATUS.STATUS_INVALID_PARAMETER;
// Only the current mode is advertised, so an enumerating caller stops after index 0.
if (ModeNumber != EnumCurrentSettings && ModeNumber != EnumRegistrySettings && ModeNumber != 0)
return NTSTATUS.STATUS_INVALID_PARAMETER;
if (!Instance.WinHelper.TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom))
return NTSTATUS.STATUS_UNSUCCESSFUL;
Instance._emulator.WriteMemory(DevModePtr + OffsetDeviceName, DeviceName + "\0", Encoding.Unicode);
Instance._emulator.WriteMemory(DevModePtr + OffsetSpecVersion, SpecVersion, 2);
Instance._emulator.WriteMemory(DevModePtr + OffsetDriverVersion, SpecVersion, 2);
Instance._emulator.WriteMemory(DevModePtr + OffsetSize, (ushort)DevModeSize, 2);
Instance._emulator.WriteMemory(DevModePtr + OffsetFields,
DmPosition | DmDisplayOrientation | DmLogPixels | DmBitsPerPel | DmPelsWidth | DmPelsHeight | DmDisplayFlags | DmDisplayFrequency, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetPositionX, (uint)Left, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetPositionY, (uint)Top, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayOrientation, 0u, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetLogPixels, (ushort)LogPixels, 2);
Instance._emulator.WriteMemory(DevModePtr + OffsetBitsPerPel, BitsPerPixel, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetPelsWidth, (uint)(Right - Left), 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetPelsHeight, (uint)(Bottom - Top), 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayFlags, 0u, 4);
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayFrequency, Instance.WinHelper.GetPrimaryDisplayFrequency(), 4);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,24 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserGetDisplayConfigBufferSizes : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong PathCountPtr = Instance.WinHelper.GetArg(1);
ulong ModeCountPtr = Instance.WinHelper.GetArg(2);
if (PathCountPtr == 0 || !Instance.IsRegionMapped(PathCountPtr, 4))
return NTSTATUS.STATUS_INVALID_PARAMETER;
Instance._emulator.WriteMemory(PathCountPtr, 1u, 4);
if (ModeCountPtr != 0 && Instance.IsRegionMapped(ModeCountPtr, 4))
Instance._emulator.WriteMemory(ModeCountPtr, 2u, 4);
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,22 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserGetDpiForMonitor : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong DpiXPtr = Instance.WinHelper.GetArg(2);
ulong DpiYPtr = Instance.WinHelper.GetArg(3);
if (DpiXPtr != 0 && Instance.IsRegionMapped(DpiXPtr, 4))
Instance._emulator.WriteMemory(DpiXPtr, Win32kHelper.DEFAULT_SCREEN_DPI, 4);
if (DpiYPtr != 0 && Instance.IsRegionMapped(DpiYPtr, 4))
Instance._emulator.WriteMemory(DpiYPtr, Win32kHelper.DEFAULT_SCREEN_DPI, 4);
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -175,6 +175,9 @@ namespace Brovan.Core.Emulation.OS.Windows
STATUS_PRIVILEGED_INSTRUCTION = 0xC0000096,
STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094,
STATUS_INTEGER_OVERFLOW = 0xC0000095,
STATUS_ARRAY_BOUNDS_EXCEEDED = 0xC000008C,
STATUS_FLOAT_INVALID_OPERATION = 0xC0000090,
STATUS_FLOAT_MULTIPLE_TRAPS = 0xC00002B5,
STATUS_MEMORY_NOT_ALLOCATED = 0xC00000A0,
STATUS_CANT_TERMINATE_SELF = 0xC00000DB,
STATUS_DEBUGGER_INACTIVE = 0xC0000354,
@@ -954,15 +954,24 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint GetArg32(int Index) => (uint)GetArg(Index);
private uint SequentialIdCursor = 30000;
public uint GenerateRandomPID()
{
uint PID = (uint)RandomGen.Next(500, 29999);
if (PIDs.TryGetValue(PID, out _))
for (int Attempt = 0; Attempt < 64; Attempt++)
{
return GenerateRandomPID();
uint Candidate = (uint)RandomGen.Next(500, 29999);
if (PIDs.TryAdd(Candidate, true))
return Candidate;
}
// Long-running guests exhaust the random pool, so hand out sequential ids instead of retrying.
while (true)
{
uint Candidate = SequentialIdCursor++;
if (Candidate != 0 && PIDs.TryAdd(Candidate, true))
return Candidate;
}
PIDs.Add(PID, true);
return PID;
}
private User GenerateRandomSvchostUser()
@@ -1196,7 +1205,15 @@ namespace Brovan.Core.Emulation.OS.Windows
private ushort NextGdiHandleUniq = 0x77;
private const int SmCxScreen = 0;
private const int SmCyScreen = 1;
private const int EnumCurrentSettings = -1;
private const ushort DevModeSize = 0xDC;
private const int DevModeOffsetSize = 0x44;
private const int DevModeOffsetDisplayFrequency = 0xB8;
private const uint DefaultDisplayFrequency = 60;
private uint PrimaryDisplayFrequency;
private const byte UserHandleTypeWindow = 1;
private const byte UserHandleTypeMonitor = 0xC;
private ulong UserPrimaryMonitorHandle;
private ushort NextUserHandleIndex = 1;
private ushort NextUserHandleUniq = 1;
private ulong UserServerInfoAddress;
@@ -2575,6 +2592,171 @@ namespace Brovan.Core.Emulation.OS.Windows
return true;
}
private ulong GuestCallTrampoline;
private const ulong GuestCallTrampolineSize = 0x1000;
private const int GuestCallResultOffset = 0x00;
private const int GuestCallScratchOffset = 0x08;
private const int GuestCallCodeOffset = 0x20;
private bool TryGetNtSyscallNumber(string Name, out uint Number)
{
Number = 0;
WinModule Ntdll = WinModules.FirstOrDefault(m => m != null && m.Name != null && m.Name.Equals("ntdll.dll", StringComparison.OrdinalIgnoreCase));
if (Ntdll == null)
return false;
ulong Stub = GetExportAddress(Ntdll, Name);
if (Stub == 0 || !Emulator.IsRegionMapped(Stub, 8))
return false;
// mov r10, rcx ; mov eax, imm32
if (Emulator.ReadMemoryUInt(Stub) != 0xB8D18B4Cu)
return false;
Number = Emulator.ReadMemoryUInt(Stub + 4);
return true;
}
/// <summary>
/// Builds the page a guest callback returns to. Its code hands the stored result back through
/// NtCallbackReturn, which unwinds the frame pushed by BeginGuestCall.
/// </summary>
private ulong EnsureGuestCallTrampoline()
{
if (GuestCallTrampoline != 0 && Emulator.IsRegionMapped(GuestCallTrampoline, GuestCallTrampolineSize))
return GuestCallTrampoline;
if (!TryGetNtSyscallNumber("NtCallbackReturn", out uint CallbackReturn))
return 0;
ulong Page = Emulator.MapUniqueAddress(GuestCallTrampolineSize, MemoryProtection.All);
if (Page == 0 || !WriteZeroMemory(Page, (uint)GuestCallTrampolineSize))
return 0;
Span<byte> Code = stackalloc byte[26];
int At = 0;
Code[At++] = 0x48; Code[At++] = 0x8D; Code[At++] = 0x0D;
int Displacement = GuestCallResultOffset - (GuestCallCodeOffset + 7);
BinaryPrimitives.WriteInt32LittleEndian(Code.Slice(At, 4), Displacement);
At += 4;
Code[At++] = 0x49; Code[At++] = 0x89; Code[At++] = 0xCA;
Code[At++] = 0xBA;
BinaryPrimitives.WriteUInt32LittleEndian(Code.Slice(At, 4), 8u);
At += 4;
Code[At++] = 0x45; Code[At++] = 0x31; Code[At++] = 0xC0;
Code[At++] = 0xB8;
BinaryPrimitives.WriteUInt32LittleEndian(Code.Slice(At, 4), CallbackReturn);
At += 4;
Code[At++] = 0x0F; Code[At++] = 0x05;
Code[At++] = 0xCC;
if (!Emulator._emulator.WriteMemory(Page + GuestCallCodeOffset, Code.Slice(0, At)))
return 0;
GuestCallTrampoline = Page;
return GuestCallTrampoline;
}
/// <summary>
/// Calls a guest function from inside a syscall and resumes the syscall's caller afterwards with
/// ResultValue in RAX. Used for Win32 APIs whose kernel side calls back into user code.
/// </summary>
public ulong EnsureGuestCallScratch()
{
ulong Page = EnsureGuestCallTrampoline();
return Page == 0 ? 0 : Page + GuestCallScratchOffset;
}
public bool BeginGuestCall(ulong Function, ulong Arg0, ulong Arg1, ulong Arg2, ulong Arg3, ulong ResultValue)
{
if (PointerSize != 8 || Function == 0 || !Emulator.IsRegionMapped(Function, 1))
return false;
EmulatedThread Thread = Emulator.CurrentThread;
if (Thread == null)
return false;
ulong Page = EnsureGuestCallTrampoline();
if (Page == 0)
return false;
Emulator._emulator.WriteMemory(Page + GuestCallResultOffset, ResultValue, 8);
ulong CurrentRsp = Emulator.ReadRegister(Registers.UC_X86_REG_RSP);
WinUserCallbackFrame Frame = new WinUserCallbackFrame
{
SavedRsp = CurrentRsp,
SavedReturnAddress = Emulator.ReadMemoryULong(CurrentRsp),
};
WinEmulatedThread.GetState(Thread).UserCallbackFrames.Push(Frame);
ulong CallRsp = ((CurrentRsp - 0x200) & ~0xFUL) - 8;
Emulator._emulator.WriteMemory(CallRsp, Page + GuestCallCodeOffset, 8);
Emulator.WriteRegister(Registers.UC_X86_REG_RCX, Arg0);
Emulator.WriteRegister(Registers.UC_X86_REG_RDX, Arg1);
Emulator.WriteRegister(Registers.UC_X86_REG_R8, Arg2);
Emulator.WriteRegister(Registers.UC_X86_REG_R9, Arg3);
Emulator.WriteRegister(Registers.UC_X86_REG_RSP, CallRsp);
Emulator.WriteRegister(Emulator.IPRegister, Function - 2);
Emulator.SuppressSyscallStatusWrite = true;
return true;
}
public ulong GetPrimaryMonitorHandle()
{
ulong Monitor = EnsureUserPrimaryMonitor();
if (Monitor == 0)
return 0;
if (UserPrimaryMonitorHandle != 0)
return UserPrimaryMonitorHandle;
if (!EnsureUserSharedInfo(out _, out ulong HandleTable, out uint EntrySize))
return 0;
ulong Handle = AllocateUserHandle();
if (Handle == 0)
return 0;
ushort Index = (ushort)(Handle & 0xFFFF);
ushort Uniq = (ushort)((Handle >> 16) & 0x7FFF);
ulong Entry = HandleTable + (ulong)Index * EntrySize;
Span<byte> Data = Shared.GetSpan(0x20);
Data.Slice(0, 0x20).Clear();
WriteU64(Data, 0x00, Monitor);
WriteU64(Data, 0x08, EnsureCurrentUserThreadInfo());
WriteU64(Data, 0x10, UserDesktopOwnerAddress);
Data[0x18] = UserHandleTypeMonitor;
Data[0x19] = 0;
WriteU16(Data, 0x1A, Uniq);
if (!Emulator._emulator.WriteMemory(Entry, Data.Slice(0, 0x20)))
return 0;
UserPrimaryMonitorHandle = Handle;
return UserPrimaryMonitorHandle;
}
public bool TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom)
{
Left = Top = Right = Bottom = 0;
ulong Monitor = EnsureUserPrimaryMonitor();
if (Monitor == 0)
return false;
Left = (int)Emulator.ReadMemoryUInt(Monitor + 0x1C);
Top = (int)Emulator.ReadMemoryUInt(Monitor + 0x20);
Right = (int)Emulator.ReadMemoryUInt(Monitor + 0x24);
Bottom = (int)Emulator.ReadMemoryUInt(Monitor + 0x28);
return Right > Left && Bottom > Top;
}
public bool CompleteUserCallback(ulong ResultAddress, uint ResultLength)
{
EmulatedThread Thread = Emulator.CurrentThread;
@@ -3424,6 +3606,39 @@ namespace Brovan.Core.Emulation.OS.Windows
return (1920, 1080);
}
public unsafe uint GetPrimaryDisplayFrequency()
{
if (PrimaryDisplayFrequency != 0)
return PrimaryDisplayFrequency;
PrimaryDisplayFrequency = DefaultDisplayFrequency;
try
{
if (OperatingSystem.IsWindows())
{
Span<byte> DevMode = stackalloc byte[DevModeSize];
DevMode.Clear();
BinaryPrimitives.WriteUInt16LittleEndian(DevMode.Slice(DevModeOffsetSize), DevModeSize);
fixed (byte* Buffer = DevMode)
{
if (NativeWinImports.EnumDisplaySettingsW(null, EnumCurrentSettings, Buffer))
{
uint Frequency = BinaryPrimitives.ReadUInt32LittleEndian(DevMode.Slice(DevModeOffsetDisplayFrequency));
if (Frequency > 1)
PrimaryDisplayFrequency = Frequency;
}
}
}
}
catch
{
}
return PrimaryDisplayFrequency;
}
public ulong EnsureUserDesktopInfo()
{
if (UserDesktopInfoAddress != 0 && Emulator.IsRegionMapped(UserDesktopInfoAddress, UserDesktopInfoSize))
+3
View File
@@ -138,6 +138,9 @@ namespace Brovan
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern unsafe bool EnumDisplaySettingsW(string lpszDeviceName, int iModeNum, byte* lpDevMode);
[DllImport("user32.dll")]
public static extern int GetSystemMetricsForDpi(int nIndex, uint dpi);
}