diff --git a/STROOP.Core/GameMemoryAccess/DolphinProcessIO.cs b/STROOP.Core/GameMemoryAccess/DolphinProcessIO.cs index 276bd7267..4e9bf04b9 100644 --- a/STROOP.Core/GameMemoryAccess/DolphinProcessIO.cs +++ b/STROOP.Core/GameMemoryAccess/DolphinProcessIO.cs @@ -1,6 +1,8 @@ -using System.Diagnostics; +using STROOP.Win32; +using System.Diagnostics; using System.Runtime.InteropServices; -using static STROOP.Core.Kernal32NativeMethods; +using Windows.Win32.Foundation; +using Windows.Win32.System.ProcessStatus; namespace STROOP.Core.GameMemoryAccess; @@ -13,14 +15,13 @@ public DolphinProcessIO(Process process, Emulator emulator) protected override void CalculateOffset() { - MemoryBasicInformation info; - IntPtr infoSize = (IntPtr)Marshal.SizeOf(typeof(MemoryBasicInformation)); - uint setInfoSize = (uint)Marshal.SizeOf(typeof(PsapiWorkingSetExInformation)); + VirtualQueryEx.MemoryBasicInformation info; + IntPtr infoSize = (IntPtr)Marshal.SizeOf(typeof(VirtualQueryEx.MemoryBasicInformation)); _baseOffset = (UIntPtr)0; bool mem1Found = false; for (IntPtr p = new IntPtr(); - VQueryEx(_processHandle, p, out info, infoSize) == infoSize; + VirtualQueryEx.Invoke(_processHandle, p, out info, infoSize) == infoSize; p = (IntPtr)(p.ToInt64() + info.RegionSize.ToInt64())) { if (mem1Found) @@ -37,17 +38,15 @@ protected override void CalculateOffset() continue; } - if (info.RegionSize == (IntPtr)0x2000000 && info.Type == MemoryType.MEM_MAPPED) + if (info.RegionSize == (IntPtr)0x2000000 && info.Type == VirtualQueryEx.MemoryType.MEM_MAPPED) { // Here, it's likely the right page, but it can happen that multiple pages with these criteria // exists and have nothing to do with the emulated memory. Only the right page has valid // working set information so an additional check is required that it is backed by physical // memory. - PsapiWorkingSetExInformation wsInfo; - wsInfo.VirtualAddress = (IntPtr)info.BaseAddress.ToUInt64(); - if (QWorkingSetEx(_processHandle, out wsInfo, setInfoSize)) + if (NativeMethodWrappers.QueryWorkingSetEx((HANDLE)_processHandle, info.BaseAddress, out PSAPI_WORKING_SET_EX_INFORMATION wsInfo)) { - if ((wsInfo.VirtualAttributes & 0x01) != 0) + if ((wsInfo.VirtualAttributes.Flags & 0x01) != 0) { _baseOffset = info.BaseAddress; mem1Found = true; diff --git a/STROOP.Core/GameMemoryAccess/SigScanSharp.cs b/STROOP.Core/GameMemoryAccess/SigScanSharp.cs index 0db9d8aa0..7c8425176 100644 --- a/STROOP.Core/GameMemoryAccess/SigScanSharp.cs +++ b/STROOP.Core/GameMemoryAccess/SigScanSharp.cs @@ -25,7 +25,7 @@ */ using System.Diagnostics; -using System.Runtime.InteropServices; +using STROOP.Win32; namespace STROOP.Core.GameMemoryAccess; @@ -52,7 +52,7 @@ public bool SelectModule(ProcessModule targetModule) g_dictStringPatterns.Clear(); try { - return Win32.ReadProcessMemory(g_hProcess, g_lpModuleBase, g_arrModuleBuffer, (IntPtr)targetModule.ModuleMemorySize); + return NativeMethodWrappers.ReadProcessMemory(g_hProcess, (UIntPtr)g_lpModuleBase, g_arrModuleBuffer); } catch (AccessViolationException) { @@ -112,10 +112,4 @@ private byte[] ParsePatternString(string szPattern) return patternbytes.ToArray(); } - - private static class Win32 - { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead = default(IntPtr)); - } } diff --git a/STROOP.Core/GameMemoryAccess/WindowsProcessIO.cs b/STROOP.Core/GameMemoryAccess/WindowsProcessIO.cs index 914437a72..f0271e1c6 100644 --- a/STROOP.Core/GameMemoryAccess/WindowsProcessIO.cs +++ b/STROOP.Core/GameMemoryAccess/WindowsProcessIO.cs @@ -1,6 +1,10 @@ -using System.ComponentModel; +using STROOP.Win32; +using System.ComponentModel; using System.Diagnostics; -using static STROOP.Core.Kernal32NativeMethods; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Threading; +using static STROOP.Core.ProcessHelper; namespace STROOP.Core.GameMemoryAccess; @@ -29,16 +33,20 @@ public WindowsProcessRamIO(Process process, Emulator emulator) : base() _process.EnableRaisingEvents = true; - ProcessAccess accessFlags = ProcessAccess.PROCESS_QUERY_LIMITED_INFORMATION | ProcessAccess.SUSPEND_RESUME - | ProcessAccess.VM_OPERATION | ProcessAccess.VM_READ | ProcessAccess.VM_WRITE; - _processHandle = ProcessGetHandleFromId(accessFlags, false, _process.Id); + var accessFlags = PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION + | PROCESS_ACCESS_RIGHTS.PROCESS_SUSPEND_RESUME + | PROCESS_ACCESS_RIGHTS.PROCESS_VM_OPERATION + | PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ + | PROCESS_ACCESS_RIGHTS.PROCESS_VM_WRITE; + + _processHandle = PInvoke.OpenProcess(accessFlags, false, (uint)_process.Id); try { CalculateOffset(); } catch (Exception e) { - CloseProcess(_processHandle); + PInvoke.CloseHandle((HANDLE)_processHandle); throw; } @@ -52,16 +60,10 @@ private void _process_Exited(object sender, EventArgs e) } protected override bool ReadFunc(UIntPtr address, byte[] buffer) - { - int numOfBytes = 0; - return ProcessReadMemory(_processHandle, address, buffer, (IntPtr)buffer.Length, ref numOfBytes); - } + => NativeMethodWrappers.ReadProcessMemory(_processHandle, address, buffer); protected override bool WriteFunc(UIntPtr address, byte[] buffer) - { - int numOfBytes = 0; - return ProcessWriteMemory(_processHandle, address, buffer, (IntPtr)buffer.Length, ref numOfBytes); - } + => NativeMethodWrappers.WriteProcessMemory(_processHandle, address, buffer); public override byte[] ReadAllMemory() { @@ -71,8 +73,8 @@ public override byte[] ReadAllMemory() for (uint address = 0; true; address++) { - bool success = ProcessReadMemory(_processHandle, (UIntPtr)address, buffer, (IntPtr)buffer.Length, ref numBytes); - if (!success) break; + if (!NativeMethodWrappers.ReadProcessMemory(_processHandle, address, buffer)) + break; output.Add(buffer[0]); } @@ -91,30 +93,29 @@ private bool CompareBytes(byte[] a, byte[] b) // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms684139%28v=vs.85%29.aspx public static bool Is64Bit(Process process) => Environment.Is64BitOperatingSystem - && IsWow64Process(process.Handle, out bool isWow64) + && PInvoke.IsWow64Process(process.SafeHandle, out var isWow64) ? !isWow64 : throw new Win32Exception(); protected virtual void CalculateOffset() { // Find CORE_RDRAM export from mupen if present - Win32SymbolInfo symbol = Win32SymbolInfo.Create(); - if (SymInitialize(_process.Handle, null, true)) + if (PInvoke.SymInitialize(_process.SafeHandle, null, true)) { try { - if (SymFromName(_process.Handle, "CORE_RDRAM", ref symbol)) + if (NativeMethodWrappers.GetSymbolAddress(_process.SafeHandle, "CORE_RDRAM", out var address)) { bool is64Bit = Is64Bit(_process); byte[]? buffer = new byte[is64Bit ? 8 : 4]; - ReadAbsolute((UIntPtr)symbol.Address, buffer, EndiannessType.Little); + ReadAbsolute((UIntPtr)address, buffer, EndiannessType.Little); _baseOffset = (UIntPtr)(is64Bit ? BitConverter.ToUInt64(buffer, 0) : (ulong)BitConverter.ToUInt32(buffer, 0)); return; } } finally { - if (!SymCleanup(_process.Handle)) + if (!PInvoke.SymCleanup(_process.SafeHandle)) throw new Win32Exception(); } } @@ -122,7 +123,7 @@ protected virtual void CalculateOffset() { // documentation doesn't say what to do when SymInitialize returns false, so just call this and don't care for its result for good (or bad) measure :shrug: // https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize - SymCleanup(_process.Handle); + PInvoke.SymCleanup(_process.SafeHandle); } // Find DLL offset if needed @@ -243,7 +244,7 @@ protected virtual void Dispose(bool disposing) } // Close old process - CloseProcess(_processHandle); + PInvoke.CloseHandle((HANDLE)_processHandle); disposedValue = true; } diff --git a/STROOP.Core/Kernal32NativeMethods.cs b/STROOP.Core/Kernal32NativeMethods.cs deleted file mode 100644 index 21d2f7da4..000000000 --- a/STROOP.Core/Kernal32NativeMethods.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace STROOP.Core; - -public static class Kernal32NativeMethods -{ - [Flags] - public enum ThreadAccess : int - { - TERMINATE = 0x0001, - SUSPEND_RESUME = 0x0002, - GET_CONTEXT = 0x0008, - SET_CONTEXT = 0x0010, - SET_INFORMATION = 0x0020, - QUERY_INFORMATION = 0x0040, - SET_THREAD_TOKEN = 0x0080, - IMPERSONATE = 0x0100, - DIRECT_IMPERSONATION = 0x0200, - } - - [Flags] - public enum ProcessAccess : int - { - VM_OPERATION = 0x0008, - VM_READ = 0x0010, - VM_WRITE = 0x0020, - PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, - SUSPEND_RESUME = 0x0800, - } - - [Flags] - public enum MemoryType : uint - { - MEM_IMAGE = 0x1000000, - MEM_MAPPED = 0x40000, - MEM_PRIVATE = 0x20000, - } - - [StructLayout(LayoutKind.Sequential)] - public struct MemoryBasicInformation - { - public UIntPtr BaseAddress; - public IntPtr AllocationBase; - public uint AllocationProtect; - public IntPtr RegionSize; - public uint State; - public uint Protect; - public MemoryType Type; - } - - [StructLayout(LayoutKind.Sequential)] - public struct PsapiWorkingSetExInformation - { - public IntPtr VirtualAddress; - public ulong VirtualAttributes; - } - - /// - /// C# representation of SYMBOL_INFO in dbghelp.h. - /// https://learn.microsoft.com/de-de/windows/win32/api/dbghelp/ns-dbghelp-symbol_info - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - public struct Win32SymbolInfo - { - private const int MaxSymbolLen = 2000; - - public static Win32SymbolInfo Create() - => new Win32SymbolInfo - { - MaxNameLen = MaxSymbolLen, - SizeOfStruct = 88, - }; - - public int SizeOfStruct; - public int TypeIndex; - private readonly ulong Reserved1, Reserved2; - public int Index; - public int Size; - public ulong ModuleBase; - public uint Flags; - public long Value; - public ulong Address; - public uint Register; - public uint Scope; - public uint Tag; - public uint NameLen; - public int MaxNameLen; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxSymbolLen)] - public string Name; - } - - #region DLL Import - - [DllImport("kernel32.dll")] - private static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); - - [DllImport("kernel32.dll")] - private static extern uint SuspendThread(IntPtr hThread); - - [DllImport("kernel32.dll")] - private static extern int ResumeThread(IntPtr hThread); - - [DllImport("kernel32.dll")] - private static extern bool CloseHandle(IntPtr hObject); - - [DllImport("kernel32.dll")] - private static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess, bool bInheritHandle, int dwProcessId); - - [DllImport("kernel32.dll")] - private static extern bool ReadProcessMemory(IntPtr hProcess, - UIntPtr lpBaseAddress, byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool WriteProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, - byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesWritten); - - [DllImport("kernel32.dll")] - private static extern IntPtr VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MemoryBasicInformation lpBuffer, IntPtr dwLength); - - [DllImport("psapi", SetLastError = true)] - private static extern bool QueryWorkingSetEx(IntPtr hProcess, out PsapiWorkingSetExInformation pv, uint cb); - - [DllImport("dbghelp", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SymInitializeW", ExactSpelling = true)] - public static extern bool SymInitialize(IntPtr hProcess, string searchPath, bool invadeProcess); - - [DllImport("dbghelp", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SymCleanup", ExactSpelling = true)] - public static extern bool SymCleanup(IntPtr hProcess); - - [DllImport("dbghelp", SetLastError = true)] - public static extern bool SymFromName(IntPtr hProcess, string name, ref Win32SymbolInfo win32Symbol); - - [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process); - - #endregion - - public static IntPtr ProcessGetHandleFromId(ProcessAccess dwDesiredAccess, bool bInheritHandle, int dwProcessId) => OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); - - public static bool CloseProcess(IntPtr processHandle) => CloseHandle(processHandle); - - public static bool ProcessReadMemory(IntPtr hProcess, - UIntPtr lpBaseAddress, byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead) => ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, dwSize, ref lpNumberOfBytesRead); - - public static bool ProcessWriteMemory(IntPtr hProcess, UIntPtr lpBaseAddress, - byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesWritten) => WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, dwSize, ref lpNumberOfBytesWritten); - - public static void ResumeProcess(Process process) - { - // Resume all threads - foreach (ProcessThread pT in process.Threads) - { - IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); - - if (pOpenThread == IntPtr.Zero) - continue; - - int suspendCount = 0; - do - { - suspendCount = ResumeThread(pOpenThread); - } while (suspendCount > 0); - - CloseHandle(pOpenThread); - } - } - - public static void SuspendProcess(Process process) - { - // Pause all threads - foreach (ProcessThread pT in process.Threads) - { - IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); - - if (pOpenThread == IntPtr.Zero) - continue; - - SuspendThread(pOpenThread); - CloseHandle(pOpenThread); - } - } - - public static IntPtr VQueryEx(IntPtr hProcess, IntPtr lpAddress, out MemoryBasicInformation lpBuffer, IntPtr dwLength) => VirtualQueryEx(hProcess, lpAddress, out lpBuffer, dwLength); - - public static bool QWorkingSetEx(IntPtr hProcess, out PsapiWorkingSetExInformation pv, uint cb) => QueryWorkingSetEx(hProcess, out pv, cb); -} diff --git a/STROOP.Core/ProcessHelper.cs b/STROOP.Core/ProcessHelper.cs new file mode 100644 index 000000000..5c64268ef --- /dev/null +++ b/STROOP.Core/ProcessHelper.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Threading; + +namespace STROOP.Core; + +public static class ProcessHelper +{ + public static void ResumeProcess(Process process) + { + // Resume all threads + foreach (ProcessThread pT in process.Threads) + { + HANDLE pOpenThread = PInvoke.OpenThread(THREAD_ACCESS_RIGHTS.THREAD_SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) + continue; + + uint suspendCount = 0; + do + { + suspendCount = PInvoke.ResumeThread(pOpenThread); + } while (suspendCount > 0); + + PInvoke.CloseHandle(pOpenThread); + } + } + + public static void SuspendProcess(Process process) + { + // Pause all threads + foreach (ProcessThread pT in process.Threads) + { + HANDLE pOpenThread = PInvoke.OpenThread(THREAD_ACCESS_RIGHTS.THREAD_SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) + continue; + + PInvoke.SuspendThread(pOpenThread); + PInvoke.CloseHandle(pOpenThread); + } + } +} diff --git a/STROOP.Core/STROOP.Core.csproj b/STROOP.Core/STROOP.Core.csproj index 229200006..86bbab216 100644 --- a/STROOP.Core/STROOP.Core.csproj +++ b/STROOP.Core/STROOP.Core.csproj @@ -6,4 +6,7 @@ disable + + + diff --git a/STROOP.Win32/NativeMethodWrappers.cs b/STROOP.Win32/NativeMethodWrappers.cs new file mode 100644 index 000000000..b33c004c0 --- /dev/null +++ b/STROOP.Win32/NativeMethodWrappers.cs @@ -0,0 +1,71 @@ +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Diagnostics.Debug; +using Windows.Win32.System.ProcessStatus; + +namespace STROOP.Win32; + +public static class NativeMethodWrappers +{ + public static unsafe bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, byte[] buffer) + { + var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + UIntPtr lpNumberOfBytesRead; + try + { + return PInvoke.ReadProcessMemory( + (HANDLE)hProcess, + (void*)lpBaseAddress, + (void*)Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0), + (uint)buffer.Length, + &lpNumberOfBytesRead + ); + } + finally + { + gcHandle.Free(); + } + } + + public static unsafe bool WriteProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, byte[] buffer) + { + UIntPtr numOfBytes = 0; + var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + try + { + return PInvoke.WriteProcessMemory( + (HANDLE)hProcess, + (void*)lpBaseAddress, + (void*)Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0), + (UIntPtr)buffer.Length, + &numOfBytes); + } + finally + { + gcHandle.Free(); + } + } + + public static unsafe bool QueryWorkingSetEx(IntPtr hProcess, UIntPtr lpBaseAddress, out PSAPI_WORKING_SET_EX_INFORMATION wsInfo) + { + PSAPI_WORKING_SET_EX_INFORMATION tmp; + tmp.VirtualAddress = (HANDLE)lpBaseAddress; + uint setInfoSize = (uint)Marshal.SizeOf(typeof(PSAPI_WORKING_SET_EX_INFORMATION)); + var result = PInvoke.QueryWorkingSetEx((HANDLE)hProcess, &tmp, setInfoSize); + wsInfo = tmp; + return result; + } + + public static unsafe bool GetSymbolAddress(SafeHandle hProcess, string name, out ulong address) + { + var symbol = new SYMBOL_INFO + { + MaxNameLen = 2000, + SizeOfStruct = 88, + }; + var result = PInvoke.SymFromName(hProcess, name, &symbol); + address = symbol.Address; + return result; + } +} diff --git a/STROOP.Win32/NativeMethods.json b/STROOP.Win32/NativeMethods.json new file mode 100644 index 000000000..d1b171f72 --- /dev/null +++ b/STROOP.Win32/NativeMethods.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "public": true +} diff --git a/STROOP.Win32/NativeMethods.txt b/STROOP.Win32/NativeMethods.txt new file mode 100644 index 000000000..7087d42e1 --- /dev/null +++ b/STROOP.Win32/NativeMethods.txt @@ -0,0 +1,26 @@ +OpenThread +SuspendThread +ResumeThread +CloseHandle +OpenProcess +ReadProcessMemory +WriteProcessMemory +QueryWorkingSetEx +SymInitialize +SymCleanup +SymFromName +IsWow64Process + +PSAPI_WORKING_SET_EX_INFORMATION +MEMORY_BASIC_INFORMATION + +CHARFORMAT2A +CFE_EFFECTS +CFM_MASK +SCF_* +EM_* +SendMessage + +GetAsyncKeyState +GetSystemMetrics +SYSTEM_METRICS_INDEX diff --git a/STROOP.Win32/STROOP.Win32.csproj b/STROOP.Win32/STROOP.Win32.csproj new file mode 100644 index 000000000..73254d5aa --- /dev/null +++ b/STROOP.Win32/STROOP.Win32.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + STROOP.Win32 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/STROOP.Win32/VirtualQueryEx.cs b/STROOP.Win32/VirtualQueryEx.cs new file mode 100644 index 000000000..82fb0eb7f --- /dev/null +++ b/STROOP.Win32/VirtualQueryEx.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; + +namespace STROOP.Win32; + +/// +/// The VirtualQueryEx method cannot be generated via CsWin32 unless the C# project itself is compiled for a specific platform.
+/// Invoking it may thus cause a runtime exception on certain platforms, but as this is only used for the Dolphin IO, I'll accept this for now. +/// See https://github.com/microsoft/CsWin32/issues/722 for details. +///
+public static class VirtualQueryEx +{ + [Flags] + public enum MemoryType : uint + { + MEM_IMAGE = 0x1000000, + MEM_MAPPED = 0x40000, + MEM_PRIVATE = 0x20000, + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemoryBasicInformation + { + public UIntPtr BaseAddress; + public IntPtr AllocationBase; + public uint AllocationProtect; + public IntPtr RegionSize; + public uint State; + public uint Protect; + public MemoryType Type; + } + + [DllImport("kernel32.dll", EntryPoint = "VirtualQueryEx")] + public static extern IntPtr Invoke(IntPtr hProcess, IntPtr lpAddress, out MemoryBasicInformation lpBuffer, IntPtr dwLength); +} diff --git a/STROOP.sln b/STROOP.sln index 514915ea7..aafba4587 100644 --- a/STROOP.sln +++ b/STROOP.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STROOP.Variables", "STROOP.Variables\STROOP.Variables.csproj", "{D5CB8378-E26B-4808-839B-200083A13E3C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STROOP.Win32", "STROOP.Win32\STROOP.Win32.csproj", "{590625A1-EC10-4656-8BD3-65BB7AAE004F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,6 +62,18 @@ Global {D5CB8378-E26B-4808-839B-200083A13E3C}.Release|x64.Build.0 = Release|Any CPU {D5CB8378-E26B-4808-839B-200083A13E3C}.Release|x86.ActiveCfg = Release|Any CPU {D5CB8378-E26B-4808-839B-200083A13E3C}.Release|x86.Build.0 = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|x64.ActiveCfg = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|x64.Build.0 = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|x86.ActiveCfg = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Debug|x86.Build.0 = Debug|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|Any CPU.Build.0 = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|x64.ActiveCfg = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|x64.Build.0 = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|x86.ActiveCfg = Release|Any CPU + {590625A1-EC10-4656-8BD3-65BB7AAE004F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/STROOP/Controls/CarretlessTextBox.cs b/STROOP/Controls/CarretlessTextBox.cs deleted file mode 100644 index 4b5c69fda..000000000 --- a/STROOP/Controls/CarretlessTextBox.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Windows.Forms; -using System.Runtime.InteropServices; - -namespace STROOP -{ - public class CarretlessTextBox : TextBox - { - [DllImport("user32.dll")] - static extern bool HideCaret(IntPtr hWnd); - - [DllImport("user32.dll")] - static extern bool ShowCaret(IntPtr hWnd); - - public CarretlessTextBox() - { - } - - public void HideTheCaret() - { - HideCaret(Handle); - } - - public void ShowTheCaret() - { - ShowCaret(Handle); - } - } -} diff --git a/STROOP/Controls/RichTextBoxEx.cs b/STROOP/Controls/RichTextBoxEx.cs index c2bbac1aa..2d371c1fd 100644 --- a/STROOP/Controls/RichTextBoxEx.cs +++ b/STROOP/Controls/RichTextBoxEx.cs @@ -3,115 +3,14 @@ using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Controls.RichEdit; namespace STROOP.Controls { public class RichTextBoxEx : RichTextBox { - #region Interop-Defines - - [StructLayout(LayoutKind.Sequential)] - private struct CHARFORMAT2_STRUCT - { - public UInt32 cbSize; - public UInt32 dwMask; - public UInt32 dwEffects; - public Int32 yHeight; - public Int32 yOffset; - public Int32 crTextColor; - public byte bCharSet; - public byte bPitchAndFamily; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public char[] szFaceName; - - public UInt16 wWeight; - public UInt16 sSpacing; - public int crBackColor; // Color.ToArgb() -> int - public int lcid; - public int dwReserved; - public Int16 sStyle; - public Int16 wKerning; - public byte bUnderlineType; - public byte bAnimation; - public byte bRevAuthor; - public byte bReserved1; - } - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); - - private const int WM_USER = 0x0400; - private const int EM_GETCHARFORMAT = WM_USER + 58; - private const int EM_SETCHARFORMAT = WM_USER + 68; - - private const int SCF_SELECTION = 0x0001; - private const int SCF_WORD = 0x0002; - private const int SCF_ALL = 0x0004; - - #region CHARFORMAT2 Flags - - private const UInt32 CFE_BOLD = 0x0001; - private const UInt32 CFE_ITALIC = 0x0002; - private const UInt32 CFE_UNDERLINE = 0x0004; - private const UInt32 CFE_STRIKEOUT = 0x0008; - private const UInt32 CFE_PROTECTED = 0x0010; - private const UInt32 CFE_LINK = 0x0020; - private const UInt32 CFE_AUTOCOLOR = 0x40000000; - private const UInt32 CFE_SUBSCRIPT = 0x00010000; /* Superscript and subscript are */ - private const UInt32 CFE_SUPERSCRIPT = 0x00020000; /* mutually exclusive */ - - private const int CFM_SMALLCAPS = 0x0040; /* (*) */ - private const int CFM_ALLCAPS = 0x0080; /* Displayed by 3.0 */ - private const int CFM_HIDDEN = 0x0100; /* Hidden by 3.0 */ - private const int CFM_OUTLINE = 0x0200; /* (*) */ - private const int CFM_SHADOW = 0x0400; /* (*) */ - private const int CFM_EMBOSS = 0x0800; /* (*) */ - private const int CFM_IMPRINT = 0x1000; /* (*) */ - private const int CFM_DISABLED = 0x2000; - private const int CFM_REVISED = 0x4000; - - private const int CFM_BACKCOLOR = 0x04000000; - private const int CFM_LCID = 0x02000000; - private const int CFM_UNDERLINETYPE = 0x00800000; /* Many displayed by 3.0 */ - private const int CFM_WEIGHT = 0x00400000; - private const int CFM_SPACING = 0x00200000; /* Displayed by 3.0 */ - private const int CFM_KERNING = 0x00100000; /* (*) */ - private const int CFM_STYLE = 0x00080000; /* (*) */ - private const int CFM_ANIMATION = 0x00040000; /* (*) */ - private const int CFM_REVAUTHOR = 0x00008000; - - - private const UInt32 CFM_BOLD = 0x00000001; - private const UInt32 CFM_ITALIC = 0x00000002; - private const UInt32 CFM_UNDERLINE = 0x00000004; - private const UInt32 CFM_STRIKEOUT = 0x00000008; - private const UInt32 CFM_PROTECTED = 0x00000010; - private const UInt32 CFM_LINK = 0x00000020; - private const UInt32 CFM_SIZE = 0x80000000; - private const UInt32 CFM_COLOR = 0x40000000; - private const UInt32 CFM_FACE = 0x20000000; - private const UInt32 CFM_OFFSET = 0x10000000; - private const UInt32 CFM_CHARSET = 0x08000000; - private const UInt32 CFM_SUBSCRIPT = CFE_SUBSCRIPT | CFE_SUPERSCRIPT; - private const UInt32 CFM_SUPERSCRIPT = CFM_SUBSCRIPT; - - private const byte CFU_UNDERLINENONE = 0x00000000; - private const byte CFU_UNDERLINE = 0x00000001; - private const byte CFU_UNDERLINEWORD = 0x00000002; /* (*) displayed as ordinary underline */ - private const byte CFU_UNDERLINEDOUBLE = 0x00000003; /* (*) displayed as ordinary underline */ - private const byte CFU_UNDERLINEDOTTED = 0x00000004; - private const byte CFU_UNDERLINEDASH = 0x00000005; - private const byte CFU_UNDERLINEDASHDOT = 0x00000006; - private const byte CFU_UNDERLINEDASHDOTDOT = 0x00000007; - private const byte CFU_UNDERLINEWAVE = 0x00000008; - private const byte CFU_UNDERLINETHICK = 0x00000009; - private const byte CFU_UNDERLINEHAIRLINE = 0x0000000A; /* (*) displayed as ordinary underline */ - - #endregion - - #endregion - public RichTextBoxEx() { // Otherwise, non-standard links get lost when user starts typing @@ -193,7 +92,7 @@ public void InsertLink(string text, string hyperlink, int position) /// true: set link style, false: clear link style public void SetSelectionLink(bool link) { - SetSelectionStyle(CFM_LINK, link ? CFE_LINK : 0); + SetSelectionStyle(CFM_MASK.CFM_LINK, link ? CFE_EFFECTS.CFE_LINK : 0); } /// @@ -202,39 +101,39 @@ public void SetSelectionLink(bool link) /// 0: link style not set, 1: link style set, -1: mixed public int GetSelectionLink() { - return GetSelectionStyle(CFM_LINK, CFE_LINK); + return GetSelectionStyle(CFM_MASK.CFM_LINK, CFE_EFFECTS.CFE_LINK); } - private void SetSelectionStyle(UInt32 mask, UInt32 effect) + private void SetSelectionStyle(CFM_MASK mask, CFE_EFFECTS effect) { - CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT(); + CHARFORMATA cf = new CHARFORMATA(); cf.cbSize = (UInt32)Marshal.SizeOf(cf); cf.dwMask = mask; cf.dwEffects = effect; - IntPtr wpar = new IntPtr(SCF_SELECTION); - IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); + WPARAM wpar = new WPARAM(PInvoke.SCF_SELECTION); + LPARAM lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); Marshal.StructureToPtr(cf, lpar, false); - IntPtr res = SendMessage(Handle, EM_SETCHARFORMAT, wpar, lpar); + IntPtr res = PInvoke.SendMessage((HWND)Handle, PInvoke.EM_SETCHARFORMAT, wpar, lpar); Marshal.FreeCoTaskMem(lpar); } - private int GetSelectionStyle(UInt32 mask, UInt32 effect) + private int GetSelectionStyle(CFM_MASK mask, CFE_EFFECTS effect) { - CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT(); + CHARFORMATA cf = new CHARFORMATA(); cf.cbSize = (UInt32)Marshal.SizeOf(cf); - cf.szFaceName = new char[32]; + cf.szFaceName = new __CHAR_32(); - IntPtr wpar = new IntPtr(SCF_SELECTION); - IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); + WPARAM wpar = new WPARAM(PInvoke.SCF_SELECTION); + LPARAM lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); Marshal.StructureToPtr(cf, lpar, false); - IntPtr res = SendMessage(Handle, EM_GETCHARFORMAT, wpar, lpar); + IntPtr res = PInvoke.SendMessage((HWND)Handle, PInvoke.EM_GETCHARFORMAT, wpar, lpar); - cf = (CHARFORMAT2_STRUCT)Marshal.PtrToStructure(lpar, typeof(CHARFORMAT2_STRUCT)); + cf = (CHARFORMATA)Marshal.PtrToStructure(lpar, typeof(CHARFORMATA)); int state; // dwMask holds the information which properties are consistent throughout the selection: diff --git a/STROOP/Controls/VariablePanel/VariablePanelValueEditBox.cs b/STROOP/Controls/VariablePanel/VariablePanelValueEditBox.cs index 77b3b3a7c..458d2defd 100644 --- a/STROOP/Controls/VariablePanel/VariablePanelValueEditBox.cs +++ b/STROOP/Controls/VariablePanel/VariablePanelValueEditBox.cs @@ -5,7 +5,7 @@ namespace STROOP.Controls.VariablePanel; -public class VariablePanelValueEditBox : CarretlessTextBox, IValueEditBox +public class VariablePanelValueEditBox : TextBox, IValueEditBox { private readonly EventHandler _handleLostFocus; private bool killed = false; diff --git a/STROOP/STROOP.csproj b/STROOP/STROOP.csproj index fe041d587..76291479a 100644 --- a/STROOP/STROOP.csproj +++ b/STROOP/STROOP.csproj @@ -53,9 +53,6 @@ Component - - Component - Component diff --git a/STROOP/Utilities/MouseUtility.cs b/STROOP/Utilities/MouseUtility.cs index 8fc05baa2..a0cb0924b 100644 --- a/STROOP/Utilities/MouseUtility.cs +++ b/STROOP/Utilities/MouseUtility.cs @@ -1,27 +1,20 @@ -using System.Runtime.InteropServices; -using System.Windows.Forms; +using System.Windows.Forms; +using Windows.Win32; +using Windows.Win32.UI.WindowsAndMessaging; namespace STROOP.Utilities { public static class MouseUtility { - const int SM_SWAPBUTTON = 23; - - [DllImport("user32.dll")] - static extern short GetAsyncKeyState(Keys vKey); - - [DllImport("user32.dll")] - static extern int GetSystemMetrics(int nIndex); - public static bool IsMouseDown(int button) { - bool buttonsSwapped = GetSystemMetrics(SM_SWAPBUTTON) != 0; + bool buttonsSwapped = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_SWAPBUTTON) != 0; if (buttonsSwapped) if (button == 0) button = 1; else if (button == 1) button = 0; Keys key = button == 2 ? Keys.MButton : (Keys)(button + 1); - return (GetAsyncKeyState(key) & 0x8000) != 0; + return (PInvoke.GetAsyncKeyState((int)key) & 0x8000) != 0; } } }