mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Remove most hand-authored native functions
This commit is contained in:
parent
1bc216ccd6
commit
fe562e8cf3
14 changed files with 204 additions and 1744 deletions
|
|
@ -21,6 +21,7 @@ using Dalamud.Utility;
|
|||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Dalamud.Configuration.Internal;
|
||||
|
||||
|
|
@ -595,11 +596,15 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
|||
{
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/animation/animation_win.cc;l=29?q=ReducedMotion&ss=chromium
|
||||
var winAnimEnabled = 0;
|
||||
var success = NativeFunctions.SystemParametersInfo(
|
||||
(uint)NativeFunctions.AccessibilityParameter.SPI_GETCLIENTAREAANIMATION,
|
||||
var success = false;
|
||||
unsafe
|
||||
{
|
||||
success = Windows.Win32.PInvoke.SystemParametersInfo(
|
||||
SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETCLIENTAREAANIMATION,
|
||||
0,
|
||||
ref winAnimEnabled,
|
||||
&winAnimEnabled,
|
||||
0);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using Dalamud.Utility;
|
|||
using Dalamud.Utility.Timing;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
#if DEBUG
|
||||
[assembly: InternalsVisibleTo("Dalamud.CorePlugin")]
|
||||
|
|
@ -29,7 +30,7 @@ namespace Dalamud;
|
|||
/// The main Dalamud class containing all subsystems.
|
||||
/// </summary>
|
||||
[ServiceManager.ProvidedService]
|
||||
internal sealed class Dalamud : IServiceType
|
||||
internal sealed unsafe class Dalamud : IServiceType
|
||||
{
|
||||
#region Internals
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ internal sealed class Dalamud : IServiceType
|
|||
{
|
||||
Log.Verbose("=============== GAME THREAD KICKOFF ===============");
|
||||
Timings.Event("Game thread kickoff");
|
||||
NativeFunctions.SetEvent(mainThreadContinueEvent);
|
||||
Windows.Win32.PInvoke.SetEvent(new HANDLE(mainThreadContinueEvent));
|
||||
}
|
||||
|
||||
void HandleServiceInitFailure(Task t)
|
||||
|
|
@ -116,9 +117,9 @@ internal sealed class Dalamud : IServiceType
|
|||
HandleServiceInitFailure(t);
|
||||
});
|
||||
|
||||
this.DefaultExceptionFilter = NativeFunctions.SetUnhandledExceptionFilter(nint.Zero);
|
||||
NativeFunctions.SetUnhandledExceptionFilter(this.DefaultExceptionFilter);
|
||||
Log.Debug($"SE default exception filter at {this.DefaultExceptionFilter.ToInt64():X}");
|
||||
this.DefaultExceptionFilter = SetExceptionHandler(nint.Zero);
|
||||
SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
Log.Debug($"SE default exception filter at {new IntPtr(this.DefaultExceptionFilter):X}");
|
||||
|
||||
var debugSig = "40 55 53 57 48 8D AC 24 70 AD FF FF";
|
||||
this.DebugExceptionFilter = Service<TargetSigScanner>.Get().ScanText(debugSig);
|
||||
|
|
@ -189,27 +190,29 @@ internal sealed class Dalamud : IServiceType
|
|||
/// Replace the current exception handler with the default one.
|
||||
/// </summary>
|
||||
internal void UseDefaultExceptionHandler() =>
|
||||
this.SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Replace the current exception handler with a debug one.
|
||||
/// </summary>
|
||||
internal void UseDebugExceptionHandler() =>
|
||||
this.SetExceptionHandler(this.DebugExceptionFilter);
|
||||
SetExceptionHandler(this.DebugExceptionFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Disable the current exception handler.
|
||||
/// </summary>
|
||||
internal void UseNoExceptionHandler() =>
|
||||
this.SetExceptionHandler(nint.Zero);
|
||||
SetExceptionHandler(nint.Zero);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to set the exception handler.
|
||||
/// </summary>
|
||||
private void SetExceptionHandler(nint newFilter)
|
||||
private static nint SetExceptionHandler(nint newFilter)
|
||||
{
|
||||
var oldFilter = NativeFunctions.SetUnhandledExceptionFilter(newFilter);
|
||||
Log.Debug("Set ExceptionFilter to {0}, old: {1}", newFilter, oldFilter);
|
||||
var oldFilter =
|
||||
Windows.Win32.PInvoke.SetUnhandledExceptionFilter((delegate* unmanaged[Stdcall]<global::Windows.Win32.System.Diagnostics.Debug.EXCEPTION_POINTERS*, int>)newFilter);
|
||||
Log.Debug("Set ExceptionFilter to {0}, old: {1}", newFilter, (nint)oldFilter);
|
||||
return (nint)oldFilter;
|
||||
}
|
||||
|
||||
private void SetupClientStructsResolver(DirectoryInfo cacheDir)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@
|
|||
<Deterministic>true</Deterministic>
|
||||
<Nullable>annotations</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- Enable when cswin32 properly supports implementing COM interfaces and we can
|
||||
make IDropTarget work -->
|
||||
<!-- <DisableRuntimeMarshalling>true</DisableRuntimeMarshalling> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ using Serilog.Events;
|
|||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
using static Dalamud.NativeFunctions;
|
||||
|
||||
namespace Dalamud;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -264,10 +262,12 @@ public sealed class EntryPoint
|
|||
var symbolPath = Path.Combine(info.AssetDirectory, "UIRes", "pdb");
|
||||
var searchPath = $".;{symbolPath}";
|
||||
|
||||
// Remove any existing Symbol Handler and Init a new one with our search path added
|
||||
SymCleanup(GetCurrentProcess());
|
||||
var currentProcess = Windows.Win32.PInvoke.GetCurrentProcess_SafeHandle();
|
||||
|
||||
if (!SymInitialize(GetCurrentProcess(), searchPath, true))
|
||||
// Remove any existing Symbol Handler and Init a new one with our search path added
|
||||
Windows.Win32.PInvoke.SymCleanup(currentProcess);
|
||||
|
||||
if (!Windows.Win32.PInvoke.SymInitialize(currentProcess, searchPath, true))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -55,4 +55,37 @@ internal sealed class WinSockHandlers : IInternalDisposableService
|
|||
|
||||
return socket;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native ws2_32 functions.
|
||||
/// </summary>
|
||||
private static class NativeFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-setsockopt.
|
||||
/// The setsockopt function sets a socket option.
|
||||
/// </summary>
|
||||
/// <param name="socket">
|
||||
/// A descriptor that identifies a socket.
|
||||
/// </param>
|
||||
/// <param name="level">
|
||||
/// The level at which the option is defined (for example, SOL_SOCKET).
|
||||
/// </param>
|
||||
/// <param name="optName">
|
||||
/// The socket option for which the value is to be set (for example, SO_BROADCAST). The optname parameter must be a
|
||||
/// socket option defined within the specified level, or behavior is undefined.
|
||||
/// </param>
|
||||
/// <param name="optVal">
|
||||
/// A pointer to the buffer in which the value for the requested option is specified.
|
||||
/// </param>
|
||||
/// <param name="optLen">
|
||||
/// The size, in bytes, of the buffer pointed to by the optval parameter.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If no error occurs, setsockopt returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error
|
||||
/// code can be retrieved by calling WSAGetLastError.
|
||||
/// </returns>
|
||||
[DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "setsockopt")]
|
||||
public static extern int SetSockOpt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,11 +200,11 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
|||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
useMinHook = true;
|
||||
|
||||
var moduleHandle = NativeFunctions.GetModuleHandleW(moduleName);
|
||||
if (moduleHandle == IntPtr.Zero)
|
||||
using var moduleHandle = Windows.Win32.PInvoke.GetModuleHandle(moduleName);
|
||||
if (moduleHandle.IsInvalid)
|
||||
throw new Exception($"Could not get a handle to module {moduleName}");
|
||||
|
||||
var procAddress = NativeFunctions.GetProcAddress(moduleHandle, exportName);
|
||||
var procAddress = (nint)Windows.Win32.PInvoke.GetProcAddress(moduleHandle, exportName);
|
||||
if (procAddress == IntPtr.Zero)
|
||||
throw new Exception($"Could not get the address of {moduleName}::{exportName}");
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ internal class AssertHandler : IDisposable
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertHandler"/> class.
|
||||
/// </summary>
|
||||
public AssertHandler()
|
||||
public unsafe AssertHandler()
|
||||
{
|
||||
this.callback = (expr, file, line) => this.OnImGuiAssert(expr, file, line);
|
||||
this.callback = this.OnImGuiAssert;
|
||||
}
|
||||
|
||||
private delegate void AssertCallbackDelegate(
|
||||
[MarshalAs(UnmanagedType.LPStr)] string expr,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string file,
|
||||
private unsafe delegate void AssertCallbackDelegate(
|
||||
void* expr,
|
||||
void* file,
|
||||
int line);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -53,15 +53,15 @@ internal class AssertHandler : IDisposable
|
|||
/// <summary>
|
||||
/// Register the cimgui assert handler with the native library.
|
||||
/// </summary>
|
||||
public void Setup()
|
||||
public unsafe void Setup()
|
||||
{
|
||||
CustomNativeFunctions.igCustom_SetAssertCallback(this.callback);
|
||||
CustomNativeFunctions.igCustom_SetAssertCallback(Marshal.GetFunctionPointerForDelegate(this.callback).ToPointer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister the cimgui assert handler with the native library.
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
public unsafe void Shutdown()
|
||||
{
|
||||
CustomNativeFunctions.igCustom_SetAssertCallback(null);
|
||||
}
|
||||
|
|
@ -72,8 +72,19 @@ internal class AssertHandler : IDisposable
|
|||
this.Shutdown();
|
||||
}
|
||||
|
||||
private void OnImGuiAssert(string expr, string file, int line)
|
||||
private unsafe void OnImGuiAssert(void* pExpr, void* pFile, int line)
|
||||
{
|
||||
var expr = Marshal.PtrToStringAnsi(new IntPtr(pExpr));
|
||||
var file = Marshal.PtrToStringAnsi(new IntPtr(pFile));
|
||||
if (expr == null || file == null)
|
||||
{
|
||||
Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line} (failed to parse)",
|
||||
expr,
|
||||
file,
|
||||
line);
|
||||
return;
|
||||
}
|
||||
|
||||
var key = $"{file}:{line}";
|
||||
if (this.ignoredAsserts.Contains(key))
|
||||
return;
|
||||
|
|
@ -218,11 +229,11 @@ internal class AssertHandler : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private static class CustomNativeFunctions
|
||||
private static unsafe class CustomNativeFunctions
|
||||
{
|
||||
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
|
||||
#pragma warning disable SA1300
|
||||
public static extern void igCustom_SetAssertCallback(AssertCallbackDelegate? callback);
|
||||
public static extern void igCustom_SetAssertCallback(void* cb);
|
||||
#pragma warning restore SA1300
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ using TerraFX.Interop.Windows;
|
|||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
using DWMWINDOWATTRIBUTE = Windows.Win32.Graphics.Dwm.DWMWINDOWATTRIBUTE;
|
||||
|
||||
// general dev notes, here because it's easiest
|
||||
|
||||
/*
|
||||
|
|
@ -487,12 +489,13 @@ internal partial class InterfaceManager : IInternalDisposableService
|
|||
{
|
||||
if (this.GameWindowHandle == 0)
|
||||
throw new InvalidOperationException("Game window is not yet ready.");
|
||||
|
||||
var value = enabled ? 1u : 0u;
|
||||
DwmSetWindowAttribute(
|
||||
this.GameWindowHandle,
|
||||
(uint)DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
global::Windows.Win32.PInvoke.DwmSetWindowAttribute(
|
||||
new(this.GameWindowHandle.Value),
|
||||
DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&value,
|
||||
sizeof(int)).ThrowOnError();
|
||||
sizeof(uint)).ThrowOnFailure();
|
||||
}
|
||||
|
||||
private static InterfaceManager WhenFontsReady()
|
||||
|
|
|
|||
|
|
@ -70,12 +70,12 @@ public interface IFontAtlasBuildToolkitPreBuild : IFontAtlasBuildToolkit
|
|||
/// Adds a font from memory region allocated using <see cref="ImGuiHelpers.AllocateMemory"/>.<br />
|
||||
/// <b>It WILL crash if you try to use a memory pointer allocated in some other way.</b><br />
|
||||
/// <b>
|
||||
/// Do NOT call <see cref="ImGuiNative.igMemFree"/> on the <paramref name="dataPointer"/> once this function has
|
||||
/// Do NOT call <see cref="ImGui.MemFree"/> on the <paramref name="dataPointer"/> once this function has
|
||||
/// been called, unless <paramref name="freeOnException"/> is set and the function has thrown an error.
|
||||
/// </b>
|
||||
/// </summary>
|
||||
/// <param name="dataPointer">Memory address for the data allocated using <see cref="ImGuiHelpers.AllocateMemory"/>.</param>
|
||||
/// <param name="dataSize">The size of the font file..</param>
|
||||
/// <param name="dataSize">The size of the font file.</param>
|
||||
/// <param name="fontConfig">The font config.</param>
|
||||
/// <param name="freeOnException">Free <paramref name="dataPointer"/> if an exception happens.</param>
|
||||
/// <param name="debugTag">A debug tag.</param>
|
||||
|
|
@ -155,7 +155,7 @@ public interface IFontAtlasBuildToolkitPreBuild : IFontAtlasBuildToolkit
|
|||
/// used as the font size. Specify -1 to use the default font size.
|
||||
/// </param>
|
||||
/// <param name="glyphRanges">The glyph ranges. Use <see cref="FontAtlasBuildToolkitUtilities"/>.ToGlyphRange to build.</param>
|
||||
/// <returns>A font returned from <see cref="ImFontAtlasPtr.AddFont"/>.</returns>
|
||||
/// <returns>A font returned from <see cref="ImFontAtlasPtr.AddFont(ImFontConfig*)"/>.</returns>
|
||||
ImFontPtr AddDalamudDefaultFont(float sizePx, ushort[]? glyphRanges = null);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ public interface IFontHandle : IDisposable
|
|||
/// You may not access the font once you dispose this object.
|
||||
/// </summary>
|
||||
/// <returns>A disposable object that will pop the font on dispose.</returns>
|
||||
/// <exception cref="InvalidOperationException">If called outside of the main thread.</exception>
|
||||
/// <exception cref="InvalidOperationException">If called outside the main thread.</exception>
|
||||
/// <remarks>
|
||||
/// <para>This function uses <see cref="ImGui.PushFont"/>, and may do extra things.
|
||||
/// <para>This function uses <see cref="ImGui.PushFont(ImFontPtr)"/>, and may do extra things.
|
||||
/// Use <see cref="IDisposable.Dispose"/> or <see cref="Pop"/> to undo this operation.
|
||||
/// Do not use <see cref="ImGui.PopFont"/>.</para>
|
||||
/// </remarks>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ using Dalamud.Utility;
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Memory;
|
||||
|
||||
using static Dalamud.NativeFunctions;
|
||||
|
||||
using LPayloadType = Lumina.Text.Payloads.PayloadType;
|
||||
using LSeString = Lumina.Text.SeString;
|
||||
|
||||
|
|
@ -28,6 +27,8 @@ public static unsafe class MemoryHelper
|
|||
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
|
||||
ObjectPool.Create(new StringBuilderPooledObjectPolicy());
|
||||
|
||||
private static readonly HANDLE ThisProcessPseudoHandle = new(unchecked((nint)0xFFFFFFFF));
|
||||
|
||||
#region Cast
|
||||
|
||||
/// <summary>Casts the given memory address as the reference to the live object.</summary>
|
||||
|
|
@ -863,7 +864,14 @@ public static unsafe class MemoryHelper
|
|||
unchecked
|
||||
{
|
||||
var length = value.Length;
|
||||
var result = NativeFunctions.ReadProcessMemory((nint)0xFFFFFFFF, memoryAddress, value, length, out _);
|
||||
fixed (byte* pVal = value)
|
||||
{
|
||||
var result = Windows.Win32.PInvoke.ReadProcessMemory(
|
||||
ThisProcessPseudoHandle,
|
||||
memoryAddress.ToPointer(),
|
||||
pVal,
|
||||
(nuint)length,
|
||||
null);
|
||||
|
||||
if (!result)
|
||||
throw new MemoryReadException($"Unable to read memory at {Util.DescribeAddress(memoryAddress)} of length {length} (result={result})");
|
||||
|
|
@ -873,6 +881,7 @@ public static unsafe class MemoryHelper
|
|||
throw new MemoryReadException($"Unable to read memory at {Util.DescribeAddress(memoryAddress)} of length {length} (error={last})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes raw data to a specified memory address via Windows API calls.
|
||||
|
|
@ -885,7 +894,14 @@ public static unsafe class MemoryHelper
|
|||
unchecked
|
||||
{
|
||||
var length = data.Length;
|
||||
var result = NativeFunctions.WriteProcessMemory((nint)0xFFFFFFFF, memoryAddress, data, length, out _);
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
var result = Windows.Win32.PInvoke.WriteProcessMemory(
|
||||
ThisProcessPseudoHandle,
|
||||
memoryAddress.ToPointer(),
|
||||
pData,
|
||||
(nuint)length,
|
||||
null);
|
||||
|
||||
if (!result)
|
||||
throw new MemoryWriteException($"Unable to write memory at {Util.DescribeAddress(memoryAddress)} of length {length} (result={result})");
|
||||
|
|
@ -895,6 +911,7 @@ public static unsafe class MemoryHelper
|
|||
throw new MemoryWriteException($"Unable to write memory at {Util.DescribeAddress(memoryAddress)} of length {length} (error={last})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,6 +12,11 @@ HWND_TOPMOST
|
|||
HWND_NOTOPMOST
|
||||
SET_WINDOW_POS_FLAGS
|
||||
|
||||
SetEvent
|
||||
|
||||
SymInitialize
|
||||
SymCleanup
|
||||
|
||||
OpenClipboard
|
||||
SetClipboardData
|
||||
CloseClipboard
|
||||
|
|
@ -22,14 +27,34 @@ GlobalLock
|
|||
GlobalUnlock
|
||||
GLOBAL_ALLOC_FLAGS
|
||||
|
||||
MEM_ALLOCATION_TYPE
|
||||
VirtualAlloc
|
||||
VirtualProtect
|
||||
VirtualFree
|
||||
VirtualQuery
|
||||
|
||||
SetUnhandledExceptionFilter
|
||||
|
||||
ReadProcessMemory
|
||||
WriteProcessMemory
|
||||
|
||||
FlashWindowEx
|
||||
|
||||
GetProcAddress
|
||||
GetModuleHandle
|
||||
GetForegroundWindow
|
||||
GetCurrentProcess
|
||||
GetWindowThreadProcessId
|
||||
|
||||
MessageBoxW
|
||||
|
||||
SystemParametersInfo
|
||||
|
||||
SystemParametersInfo
|
||||
|
||||
DwmSetWindowAttribute
|
||||
|
||||
setsockopt
|
||||
|
||||
RegisterDragDrop
|
||||
RevokeDragDrop
|
||||
DragQueryFileW
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ using Windows.Win32.System.Memory;
|
|||
using Windows.Win32.System.Ole;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
using FLASHWINFO = Windows.Win32.UI.WindowsAndMessaging.FLASHWINFO;
|
||||
using HWND = Windows.Win32.Foundation.HWND;
|
||||
using MEMORY_BASIC_INFORMATION = Windows.Win32.System.Memory.MEMORY_BASIC_INFORMATION;
|
||||
using Win32_PInvoke = Windows.Win32.PInvoke;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
|
@ -202,14 +202,14 @@ public static class Util
|
|||
}
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
if (Win32_PInvoke.VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
return $"0x{p:X}(???)";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"0x{p:X}(");
|
||||
for (int i = 0, c = 0; i < PageProtectionFlagNames.Length; i++)
|
||||
{
|
||||
if ((mbi.Protect & (1 << i)) == 0)
|
||||
if (((uint)mbi.Protect & (1 << i)) == 0)
|
||||
continue;
|
||||
if (c++ != 0)
|
||||
sb.Append(" | ");
|
||||
|
|
@ -577,25 +577,44 @@ public static class Util
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current application has focus, false otherwise.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// If the current application is focused.
|
||||
/// </returns>
|
||||
public static unsafe bool ApplicationIsActivated()
|
||||
{
|
||||
var activatedHandle = Win32_PInvoke.GetForegroundWindow();
|
||||
if (activatedHandle == IntPtr.Zero)
|
||||
return false; // No window is currently activated
|
||||
|
||||
uint pid;
|
||||
_ = Win32_PInvoke.GetWindowThreadProcessId(activatedHandle, &pid);
|
||||
if (Marshal.GetLastWin32Error() != 0)
|
||||
return false;
|
||||
|
||||
return pid == Environment.ProcessId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request that Windows flash the game window to grab the user's attention.
|
||||
/// </summary>
|
||||
/// <param name="flashIfOpen">Attempt to flash even if the game is currently focused.</param>
|
||||
public static void FlashWindow(bool flashIfOpen = false)
|
||||
public static unsafe void FlashWindow(bool flashIfOpen = false)
|
||||
{
|
||||
if (NativeFunctions.ApplicationIsActivated() && !flashIfOpen)
|
||||
if (ApplicationIsActivated() && !flashIfOpen)
|
||||
return;
|
||||
|
||||
var flashInfo = new NativeFunctions.FlashWindowInfo
|
||||
var flashInfo = new FLASHWINFO
|
||||
{
|
||||
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
|
||||
Count = uint.MaxValue,
|
||||
Timeout = 0,
|
||||
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
|
||||
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
|
||||
cbSize = (uint)sizeof(FLASHWINFO),
|
||||
uCount = uint.MaxValue,
|
||||
dwTimeout = 0,
|
||||
dwFlags = FLASHWINFO_FLAGS.FLASHW_ALL | FLASHWINFO_FLAGS.FLASHW_TIMERNOFG,
|
||||
hwnd = new HWND(Process.GetCurrentProcess().MainWindowHandle.ToPointer()),
|
||||
};
|
||||
|
||||
NativeFunctions.FlashWindowEx(ref flashInfo);
|
||||
Win32_PInvoke.FlashWindowEx(flashInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue