From e85403a7ef237e5143c40ec07e46d7f0c55875ef Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Wed, 21 May 2025 05:49:36 +1000 Subject: [PATCH 1/6] Fix some Win32 API definitions --- .../CustomControls/FolderBrowserDialogEx.cs | 3 ++- src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs | 2 +- src/BizHawk.Common/Win32/CWDHacks.cs | 2 +- src/BizHawk.Common/Win32/Win32Imports.cs | 16 ++++++++-------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs b/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs index 1f8eb45df3d..e3147c9c242 100644 --- a/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs +++ b/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs @@ -67,7 +67,8 @@ int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData) browseOptions &= ~BROWSEINFOW.FLAGS.NewDialogStyle; } - pszDisplayName = Marshal.AllocCoTaskMem(Win32Imports.MAX_PATH * sizeof(char)); + const int BUF_SIZE_BYTES = (int) Win32Imports.MAX_PATH * sizeof(char); + pszDisplayName = Marshal.AllocCoTaskMem(BUF_SIZE_BYTES); var bi = new BROWSEINFOW { hwndOwner = hWndOwner, diff --git a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs index ed731e4f71d..d0e1ec3bfb5 100644 --- a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs +++ b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs @@ -33,7 +33,7 @@ public static string ResolveShortcut(string filename) ((ShellLinkImports.IShellLinkW*)link)->Resolve(hwnd, 0); #endif - ((ShellLinkImports.IShellLinkW*)link)->GetPath(out var path, Win32Imports.MAX_PATH + 1, 0); + ((ShellLinkImports.IShellLinkW*) link)->GetPath(out var path, (int) Win32Imports.MAX_PATH + 1, 0); return path; } } diff --git a/src/BizHawk.Common/Win32/CWDHacks.cs b/src/BizHawk.Common/Win32/CWDHacks.cs index 3409395ebf4..9df35f645c8 100644 --- a/src/BizHawk.Common/Win32/CWDHacks.cs +++ b/src/BizHawk.Common/Win32/CWDHacks.cs @@ -23,7 +23,7 @@ static Exception GetExceptionForFailure() return new InvalidOperationException("GetCurrentDirectoryW returned 0!", ex); } - const int STARTING_BUF_SIZE = Win32Imports.MAX_PATH + 1; + const int STARTING_BUF_SIZE = (int) Win32Imports.MAX_PATH + 1; var startingBuffer = stackalloc char[STARTING_BUF_SIZE]; var ret = GetCurrentDirectoryW(STARTING_BUF_SIZE, startingBuffer); switch (ret) diff --git a/src/BizHawk.Common/Win32/Win32Imports.cs b/src/BizHawk.Common/Win32/Win32Imports.cs index 7bdc80a4961..66fe84d7ada 100644 --- a/src/BizHawk.Common/Win32/Win32Imports.cs +++ b/src/BizHawk.Common/Win32/Win32Imports.cs @@ -14,16 +14,16 @@ namespace BizHawk.Common /// public static class Win32Imports { - public const int MAX_PATH = 260; + public const uint MAX_PATH = 260U; [Flags] - public enum TPM + public enum TPM : uint { LEFTBUTTON = 0x0000, RIGHTBUTTON = 0x0002, LEFTALIGN = 0x0000, - CENTERALIGN = 0x000, - RIGHTALIGN = 0x000, + CENTERALIGN = 0x0004, + RIGHTALIGN = 0x0008, TOPALIGN = 0x0000, VCENTERALIGN = 0x0010, BOTTOMALIGN = 0x0020, @@ -31,13 +31,13 @@ public enum TPM VERTICAL = 0x0040, NONOTIFY = 0x0080, RETURNCMD = 0x0100, - RECURSE = 0x0001, + RECURSE = 0x0001, // value missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3461 HORPOSANIMATION = 0x0400, HORNEGANIMATION = 0x0800, VERPOSANIMATION = 0x1000, VERNEGANIMATION = 0x2000, NOANIMATION = 0x4000, - LAYOUTRTL = 0x8000, + LAYOUTRTL = 0x8000, // value also missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3456 } [DllImport("user32.dll", ExactSpelling = true)] @@ -53,8 +53,8 @@ public enum TPM [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern unsafe int FormatMessageW(int flags, IntPtr source, uint messageId, uint languageId, char* outMsg, int size, IntPtr args); - [DllImport("kernel32.dll", ExactSpelling = true)] - public static extern uint GetLastError(); + public static uint GetLastError() + => unchecked((uint) Marshal.GetLastWin32Error()); [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern bool PathRelativePathToW([Out] char[] pszPath, [In] string pszFrom, [In] FileAttributes dwAttrFrom, [In] string pszTo, [In] FileAttributes dwAttrTo); From 964a75352e7760884ff8d79d5f39237d8d2c178b Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Wed, 21 May 2025 07:03:33 +1000 Subject: [PATCH 2/6] Outsource Windows API definitions (`extern`) to CsWin32 --- Directory.Packages.props | 1 + .../KeyMouseInput/RawKeyMouseInput.cs | 169 +++++++------ .../SDL2/SDL2InputAdapter.cs | 13 +- src/BizHawk.Client.DiscoHawk/Program.cs | 15 +- src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs | 10 +- .../CustomControls/FolderBrowserDialogEx.cs | 74 +++--- .../CustomControls/InputWidget.cs | 6 +- src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs | 22 +- .../Extensions/ControlExtensions.cs | 27 +- src/BizHawk.Client.EmuHawk/Program.cs | 8 +- src/BizHawk.Client.EmuHawk/ScreenSaver.cs | 20 +- src/BizHawk.Client.EmuHawk/Throttle.cs | 2 + .../tools/SNES/SNESGraphicsDebugger.cs | 11 +- src/BizHawk.Common/BizHawk.Common.csproj | 4 +- .../Extensions/PathExtensions.cs | 2 + .../MemoryBlock/MemoryBlockWindowsPal.cs | 21 +- src/BizHawk.Common/NativeMethods.json | 7 + src/BizHawk.Common/NativeMethods.txt | 70 ++++++ src/BizHawk.Common/OSTailoredCode.cs | 45 +++- src/BizHawk.Common/TempFileManager.cs | 2 + src/BizHawk.Common/Win32/CWDHacks.cs | 33 ++- src/BizHawk.Common/Win32/CommctrlImports.cs | 59 ----- src/BizHawk.Common/Win32/HeapApiImports.cs | 21 -- src/BizHawk.Common/Win32/LoaderApiImports.cs | 25 -- src/BizHawk.Common/Win32/MemoryApiImports.cs | 62 ----- src/BizHawk.Common/Win32/MotWHack.cs | 4 +- src/BizHawk.Common/Win32/Ole32Imports.cs | 49 ---- src/BizHawk.Common/Win32/RawInputImports.cs | 164 ++---------- src/BizHawk.Common/Win32/Shell32Imports.cs | 86 ------- src/BizHawk.Common/Win32/ShellLinkImports.cs | 157 ------------ src/BizHawk.Common/Win32/Win32Imports.cs | 85 ------- .../Win32/Win32ShellContextMenu.cs | 234 ++++-------------- src/BizHawk.Common/Win32/WmImports.cs | 94 +------ ...Windows.Win32.Win32Imports.GetLastError.cs | 11 + ...Windows.Win32.Win32Imports.KERNEL32.dll.cs | 56 +++++ .../Windows.Win32.Win32Imports.SHLWAPI.dll.cs | 23 ++ ....Win32.Win32Imports.ShellLinkExtensions.cs | 19 ++ .../Windows.Win32.Win32Imports.TPMFLAGS.cs | 37 +++ .../Windows.Win32.Win32Imports.USER32.dll.cs | 59 +++++ src/MainSlnCommon.props | 3 + 40 files changed, 645 insertions(+), 1165 deletions(-) create mode 100644 src/BizHawk.Common/NativeMethods.json create mode 100644 src/BizHawk.Common/NativeMethods.txt delete mode 100644 src/BizHawk.Common/Win32/CommctrlImports.cs delete mode 100644 src/BizHawk.Common/Win32/HeapApiImports.cs delete mode 100644 src/BizHawk.Common/Win32/LoaderApiImports.cs delete mode 100644 src/BizHawk.Common/Win32/MemoryApiImports.cs delete mode 100644 src/BizHawk.Common/Win32/Ole32Imports.cs delete mode 100644 src/BizHawk.Common/Win32/Shell32Imports.cs delete mode 100644 src/BizHawk.Common/Win32/ShellLinkImports.cs delete mode 100644 src/BizHawk.Common/Win32/Win32Imports.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs create mode 100644 src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 439384498f4..2aa7d9cf5b8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,7 @@ + diff --git a/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs b/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs index 3968cbc45b4..2a173943af4 100644 --- a/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs +++ b/src/BizHawk.Bizware.Input/KeyMouseInput/RawKeyMouseInput.cs @@ -7,8 +7,15 @@ using BizHawk.Common; using BizHawk.Common.CollectionExtensions; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Input; +using Windows.Win32.UI.Input.KeyboardAndMouse; +using Windows.Win32.UI.WindowsAndMessaging; + using static BizHawk.Common.RawInputImports; -using static BizHawk.Common.WmImports; +using static BizHawk.Common.WmImports1; +using static Windows.Win32.Win32Imports; namespace BizHawk.Bizware.Input { @@ -16,12 +23,12 @@ namespace BizHawk.Bizware.Input /// Note: Only 1 window per device class (e.g. keyboards) is actually allowed to use RAWINPUT (last one to call RegisterRawInputDevices) /// So only one instance can actually be used at the same time /// - internal sealed class RawKeyMouseInput : IKeyMouseInput + internal sealed unsafe class RawKeyMouseInput : IKeyMouseInput { private const int WM_CLOSE = 0x0010; private const int WM_INPUT = 0x00FF; - private IntPtr RawInputWindow; + private HWND RawInputWindow; private bool _handleAltKbLayouts; private List _keyEvents = [ ]; private (int X, int Y) _mouseDelta; @@ -33,17 +40,19 @@ internal sealed class RawKeyMouseInput : IKeyMouseInput private int RawInputBufferSize; private readonly int RawInputBufferDataOffset; - private static readonly WNDPROC _wndProc = WndProc; - - private static readonly Lazy _rawInputWindowAtom = new(() => + private static unsafe readonly Lazy _rawInputWindowAtom = new(() => { - var wc = default(WNDCLASSW); - wc.lpfnWndProc = _wndProc; - wc.hInstance = LoaderApiImports.GetModuleHandleW(null); - wc.lpszClassName = "RawKeyMouseInputClass"; - - var atom = RegisterClassW(ref wc); - if (atom == IntPtr.Zero) + WNDCLASSW wc = default; + wc.lpfnWndProc = WndProc; + wc.hInstance = GetModuleHandleW(default(PCWSTR)); + var lpszClassNameStr = "RawKeyMouseInputClass"; + PCWSTR atom; + fixed (char* lpszClassName = lpszClassNameStr) + { + wc.lpszClassName = lpszClassName; + atom = MAKEINTATOM(RegisterClassW(in wc)); + } + if (atom.Value is null) { throw new InvalidOperationException("Failed to register RAWINPUT window class"); } @@ -51,9 +60,9 @@ internal sealed class RawKeyMouseInput : IKeyMouseInput return atom; }); - private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam) + private static LRESULT WndProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) { - var ud = GetWindowLongPtrW(hWnd, GWLP_USERDATA); + var ud = GetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA); if (ud == IntPtr.Zero) { return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -65,7 +74,7 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP { if (uMsg == WM_CLOSE) { - SetWindowLongPtrW(hWnd, GWLP_USERDATA, IntPtr.Zero); + SetWindowLongPtrW(hWnd, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, IntPtr.Zero); handle = GCHandle.FromIntPtr(ud); rawKeyMouseInput = (RawKeyMouseInput)handle.Target; Marshal.FreeCoTaskMem(rawKeyMouseInput.RawInputBuffer); @@ -75,17 +84,24 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP return DefWindowProcW(hWnd, uMsg, wParam, lParam); } - if (GetRawInputData(lParam, RID.INPUT, IntPtr.Zero, - out var size, sizeof(RAWINPUTHEADER)) == -1) + uint size = default; + if (GetRawInputData( + new(lParam), + RAW_INPUT_DATA_COMMAND_FLAGS.RID_INPUT, + pData: default, + ref size, + cbSizeHeader: unchecked((uint) sizeof(RAWINPUTHEADER))) + is uint.MaxValue/*-1*/) { return DefWindowProcW(hWnd, uMsg, wParam, lParam); } // don't think size should ever be this big, but just in case + var allocOnHeap = size > 1024; // also, make sure to align the buffer to a pointer boundary - var buffer = size > 1024 - ? new IntPtr[(size + sizeof(IntPtr) - 1) / sizeof(IntPtr)] - : stackalloc IntPtr[(size + sizeof(IntPtr) - 1) / sizeof(IntPtr)]; + var roundedSize = unchecked((int) (size / sizeof(IntPtr))); + if ((size & 0b11U) is not 0U) roundedSize++; + var buffer = allocOnHeap ? new IntPtr[roundedSize] : stackalloc IntPtr[roundedSize]; handle = GCHandle.FromIntPtr(ud); rawKeyMouseInput = (RawKeyMouseInput)handle.Target; @@ -93,18 +109,23 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP fixed (IntPtr* p = buffer) { var input = (RAWINPUT*)p; - if (GetRawInputData(lParam, RID.INPUT, input, - ref size, sizeof(RAWINPUTHEADER)) == -1) + if (GetRawInputData( + new(lParam), + RAW_INPUT_DATA_COMMAND_FLAGS.RID_INPUT, + pData: input, + ref size, + cbSizeHeader: unchecked((uint) sizeof(RAWINPUTHEADER))) + is uint.MaxValue/*-1*/) { return DefWindowProcW(hWnd, uMsg, wParam, lParam); } - if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD) + if (input->header.dwType == RIM_TYPE.KEYBOARD) { rawKeyMouseInput.AddKeyInput(&input->data.keyboard); } - if (input->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE) + if (input->header.dwType == RIM_TYPE.MOUSE) { rawKeyMouseInput.AddMouseInput(&input->data.mouse); } @@ -113,14 +134,14 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP while (true) { var rawInputBuffer = (RAWINPUT*)rawKeyMouseInput.RawInputBuffer; - size = rawKeyMouseInput.RawInputBufferSize; - var count = GetRawInputBuffer(rawInputBuffer, ref size, sizeof(RAWINPUTHEADER)); + size = unchecked((uint) rawKeyMouseInput.RawInputBufferSize); + var count = GetRawInputBuffer(rawInputBuffer, ref size, unchecked((uint) sizeof(RAWINPUTHEADER))); if (count == 0) { break; } - if (count == -1) + if (count is uint.MaxValue/*-1*/) { // From testing, it appears this never actually occurs in practice // As GetRawInputBuffer will succeed as long as the buffer has room for at least 1 packet @@ -136,15 +157,15 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP break; } - for (var i = 0u; i < (uint)count; i++) + for (var i = 0U; i < count; i++) { - if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.KEYBOARD) + if (rawInputBuffer->header.dwType == RIM_TYPE.KEYBOARD) { var keyboard = (RAWKEYBOARD*)((byte*)&rawInputBuffer->data.keyboard + rawKeyMouseInput.RawInputBufferDataOffset); rawKeyMouseInput.AddKeyInput(keyboard); } - if (rawInputBuffer->header.dwType == RAWINPUTHEADER.RIM_TYPE.MOUSE) + if (rawInputBuffer->header.dwType == RIM_TYPE.MOUSE) { var mouse = (RAWMOUSE*)((byte*)&rawInputBuffer->data.mouse + rawKeyMouseInput.RawInputBufferDataOffset); rawKeyMouseInput.AddMouseInput(mouse); @@ -157,20 +178,20 @@ private static unsafe IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntP } } - return IntPtr.Zero; + return default; } private unsafe void AddKeyInput(RAWKEYBOARD* keyboard) { - if ((keyboard->Flags & ~(RAWKEYBOARD.RI_KEY.E0 | RAWKEYBOARD.RI_KEY.BREAK)) == 0) + if ((keyboard->Flags & ~(RI_KEY.E0 | RI_KEY.BREAK)) == 0) { - var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RAWKEYBOARD.RI_KEY.E0) != 0 ? 0x80 : 0)); + var rawKey = (RawKey)(keyboard->MakeCode | ((keyboard->Flags & RI_KEY.E0) != 0 ? 0x80 : 0)); // kind of a dumb hack, the Pause key is apparently special here // keyboards would send scancode 0x1D with an E1 prefix, followed by 0x45 (which is NumLock!) // this "NumLock" press will set the VKey to 255 (invalid VKey), so we can use that to know if this is actually a Pause press // (note that DIK_PAUSE is just 0x45 with an E0 prefix, although this is likely just a conversion DirectInput does internally) - if (rawKey == RawKey.NUMLOCK && keyboard->VKey == VirtualKey.VK_NONE) + if (rawKey is RawKey.NUMLOCK && (VirtualKey) keyboard->VKey is VirtualKey.VK_NONE) { rawKey = RawKey.PAUSE; } @@ -182,7 +203,7 @@ private unsafe void AddKeyInput(RAWKEYBOARD* keyboard) if (KeyEnumMap.TryGetValue(rawKey, out var key)) { - _keyEvents.Add(new(key, (keyboard->Flags & RAWKEYBOARD.RI_KEY.BREAK) == RAWKEYBOARD.RI_KEY.MAKE)); + _keyEvents.Add(new(key, (keyboard->Flags & RI_KEY.BREAK) == RI_KEY.MAKE)); } } } @@ -190,54 +211,56 @@ private unsafe void AddKeyInput(RAWKEYBOARD* keyboard) private unsafe void AddMouseInput(RAWMOUSE* mouse) { // raw input usually doesn't report absolute inputs, only in odd cases with e.g. touchscreen and rdp screen - if ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) + if ((mouse->usFlags & MOUSE_STATE.MOUSE_MOVE_ABSOLUTE) == MOUSE_STATE.MOUSE_MOVE_ABSOLUTE) { _mouseDelta.X += mouse->lLastX - _lastMouseAbsPos.X; _mouseDelta.Y += mouse->lLastY - _lastMouseAbsPos.Y; _lastMouseAbsPos = (mouse->lLastX, mouse->lLastY); } - else // ((mouse->usFlags & RAWMOUSE.MOUSE_FLAGS.MOVE_ABSOLUTE) == RAWMOUSE.MOUSE_FLAGS.MOVE_RELATIVE) + else // ((mouse->usFlags & MOUSE_STATE.MOUSE_MOVE_ABSOLUTE) == MOUSE_STATE.MOUSE_MOVE_RELATIVE) { _mouseDelta.X += mouse->lLastX; _mouseDelta.Y += mouse->lLastY; } } - private static IntPtr CreateRawInputWindow() + private static unsafe HWND CreateRawInputWindow() { - const int WS_CHILD = 0x40000000; - var window = CreateWindowExW( + var lpWindowNameStr = "RawKeyInput"; + HWND window; + fixed (char* lpWindowName = lpWindowNameStr) + { + window = CreateWindowExW( dwExStyle: 0, lpClassName: _rawInputWindowAtom.Value, - lpWindowName: "RawKeyInput", - dwStyle: WS_CHILD, + lpWindowName: lpWindowName, + dwStyle: WINDOW_STYLE.WS_CHILD, X: 0, Y: 0, nWidth: 1, nHeight: 1, - hWndParent: HWND_MESSAGE, - hMenu: IntPtr.Zero, - hInstance: LoaderApiImports.GetModuleHandleW(null), - lpParam: IntPtr.Zero); - - if (window == IntPtr.Zero) + hWndParent: HWND.HWND_MESSAGE, + hMenu: default, + hInstance: GetModuleHandleW(default(PCWSTR)), + lpParam: default); + } + if (window.IsNull) { throw new InvalidOperationException("Failed to create RAWINPUT window"); } unsafe { - var rid = stackalloc RAWINPUTDEVICE[2]; - rid[0].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC; - rid[0].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_KEYBOARD; - rid[0].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK; + Span rid = stackalloc RAWINPUTDEVICE[2]; + rid[0].usUsagePage = HidUsagePage.GENERIC; + rid[0].usUsage = HidUsageId.GENERIC_KEYBOARD; + rid[0].dwFlags = RAWINPUTDEVICE_FLAGS.RIDEV_INPUTSINK; rid[0].hwndTarget = window; - rid[1].usUsagePage = RAWINPUTDEVICE.HidUsagePage.GENERIC; - rid[1].usUsage = RAWINPUTDEVICE.HidUsageId.GENERIC_MOUSE; - rid[1].dwFlags = RAWINPUTDEVICE.RIDEV.INPUTSINK; + rid[1].usUsagePage = HidUsagePage.GENERIC; + rid[1].usUsage = HidUsageId.GENERIC_MOUSE; + rid[1].dwFlags = RAWINPUTDEVICE_FLAGS.RIDEV_INPUTSINK; rid[1].hwndTarget = window; - - if (!RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE))) + if (!RegisterRawInputDevices(rid, unchecked((uint) sizeof(RAWINPUTDEVICE)))) { DestroyWindow(window); throw new InvalidOperationException("Failed to register RAWINPUTDEVICE"); @@ -280,11 +303,11 @@ public void Dispose() { lock (_lockObj) { - if (RawInputWindow != IntPtr.Zero) + if (!RawInputWindow.IsNull) { // Can't use DestroyWindow, that's only allowed in the thread that created the window! - PostMessageW(RawInputWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); - RawInputWindow = IntPtr.Zero; + PostMessageW(RawInputWindow, WM_CLOSE, default, default); + RawInputWindow = HWND.Null; } else { @@ -310,15 +333,20 @@ public IEnumerable UpdateKeyInputs(bool handleAltKbLayouts) { RawInputWindow = CreateRawInputWindow(); var handle = GCHandle.Alloc(this, GCHandleType.Normal); - SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle)); + SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle)); } _handleAltKbLayouts = handleAltKbLayouts; - while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PM_REMOVE)) + while (PeekMessageW( + out var msg, + RawInputWindow, + wMsgFilterMin: 0, + wMsgFilterMax: 0, + PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)) { - TranslateMessage(ref msg); - DispatchMessageW(ref msg); + TranslateMessage(in msg); + DispatchMessageW(in msg); } var ret = _keyEvents; @@ -340,13 +368,13 @@ public IEnumerable UpdateKeyInputs(bool handleAltKbLayouts) { RawInputWindow = CreateRawInputWindow(); var handle = GCHandle.Alloc(this, GCHandleType.Normal); - SetWindowLongPtrW(RawInputWindow, GWLP_USERDATA, GCHandle.ToIntPtr(handle)); + SetWindowLongPtrW(RawInputWindow, WINDOW_LONG_PTR_INDEX.GWLP_USERDATA, GCHandle.ToIntPtr(handle)); } - while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PM_REMOVE)) + while (PeekMessageW(out var msg, RawInputWindow, 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)) { - TranslateMessage(ref msg); - DispatchMessageW(ref msg); + TranslateMessage(in msg); + DispatchMessageW(in msg); } var ret = _mouseDelta; @@ -395,8 +423,7 @@ private static RawKey MapToRealKeyViaScanCode(RawKey key) scanCode |= 0xE000; } - const uint MAPVK_VSC_TO_VK_EX = 0x03; - var virtualKey = (VirtualKey)MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX); + var virtualKey = (VirtualKey) MapVirtualKeyW(scanCode, MAP_VIRTUAL_KEY_TYPE.MAPVK_VSC_TO_VK_EX); return VKeyToRawKeyMap.GetValueOrDefault(virtualKey); } diff --git a/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs b/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs index 796244cd044..14fa41d0269 100644 --- a/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs +++ b/src/BizHawk.Bizware.Input/SDL2/SDL2InputAdapter.cs @@ -9,6 +9,8 @@ using BizHawk.Common.NumberExtensions; #endif +using Windows.Win32.UI.WindowsAndMessaging; + using static SDL2.SDL; #pragma warning disable BHI1007 // target-typed Exception TODO don't @@ -49,10 +51,15 @@ private static void DoSDLEventLoop() // similar code shouldn't be needed on other platforms (which have global message queues and not thread local message queues) if (!OSTailoredCode.IsUnixHost) { - while (WmImports.PeekMessageW(out var msg, IntPtr.Zero, 0, 0, WmImports.PM_REMOVE)) + while (WmImports.PeekMessageW( + out var msg, + hWnd: default, + wMsgFilterMin: 0, + wMsgFilterMax: 0, + PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)) { - WmImports.TranslateMessage(ref msg); - WmImports.DispatchMessageW(ref msg); + WmImports.TranslateMessage(in msg); + WmImports.DispatchMessageW(in msg); } } diff --git a/src/BizHawk.Client.DiscoHawk/Program.cs b/src/BizHawk.Client.DiscoHawk/Program.cs index f85d66f0a75..045dc677168 100644 --- a/src/BizHawk.Client.DiscoHawk/Program.cs +++ b/src/BizHawk.Client.DiscoHawk/Program.cs @@ -8,6 +8,9 @@ using BizHawk.Common.StringExtensions; using BizHawk.Emulation.DiscSystem; +using Windows.Win32; +using Windows.Win32.UI.WindowsAndMessaging; + namespace BizHawk.Client.DiscoHawk { internal static class Program @@ -67,9 +70,9 @@ private static void Main(string[] args) const uint WM_DROPFILES = 0x0233; const uint WM_COPYDATA = 0x004A; const uint WM_COPYGLOBALDATA = 0x0049; - WmImports.ChangeWindowMessageFilter(WM_DROPFILES, WmImports.ChangeWindowMessageFilterFlags.Add); - WmImports.ChangeWindowMessageFilter(WM_COPYDATA, WmImports.ChangeWindowMessageFilterFlags.Add); - WmImports.ChangeWindowMessageFilter(WM_COPYGLOBALDATA, WmImports.ChangeWindowMessageFilterFlags.Add); + WmImports.ChangeWindowMessageFilter(WM_DROPFILES, CHANGE_WINDOW_MESSAGE_FILTER_FLAGS.MSGFLT_ADD); + WmImports.ChangeWindowMessageFilter(WM_COPYDATA, CHANGE_WINDOW_MESSAGE_FILTER_FLAGS.MSGFLT_ADD); + WmImports.ChangeWindowMessageFilter(WM_COPYGLOBALDATA, CHANGE_WINDOW_MESSAGE_FILTER_FLAGS.MSGFLT_ADD); // this will look in subdirectory "dll" to load pinvoked stuff var dllDir = Path.Combine(AppContext.BaseDirectory, "dll"); @@ -83,7 +86,7 @@ private static void Main(string[] args) if (dllDir.ContainsOrdinal(';')) { - var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, null, 0); + var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir); if (dllShortPathLen == 0) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); @@ -91,14 +94,14 @@ private static void Main(string[] args) } var dllShortPathBuffer = new char[dllShortPathLen]; - dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer, dllShortPathLen); + dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer); if (dllShortPathLen == 0) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); return; } - dllDir = new string(dllShortPathBuffer, 0, dllShortPathLen); + dllDir = dllShortPathBuffer.AsSpan(start: 0, length: (int) dllShortPathLen).ToString(); if (dllDir.ContainsOrdinal(';')) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); diff --git a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs index ae16339af67..73a71bbaf74 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs @@ -11,7 +11,7 @@ using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; -using static BizHawk.Common.HeapApiImports; +using Windows.Win32; // some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967 namespace BizHawk.Client.EmuHawk @@ -456,12 +456,12 @@ public static void DeallocateAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESS #if false // test: increase stability by never freeing anything, ever if (opts.lpParms != IntPtr.Zero) { - HeapFree(GetProcessHeap(), 0, opts.lpParms); + HeapFree(GetProcessHeap_SafeHandle(), 0, opts.lpParms); } if (opts.lpFormat != IntPtr.Zero) { - HeapFree(GetProcessHeap(), 0, opts.lpFormat); + HeapFree(GetProcessHeap_SafeHandle(), 0, opts.lpFormat); } #endif #if AVI_SUPPORT @@ -473,13 +473,13 @@ public void AllocateToAVICOMPRESSOPTIONS(out AVIWriterImports.AVICOMPRESSOPTIONS { if (_comprOptions.cbParms != 0) { - _comprOptions.lpParms = HeapAlloc(GetProcessHeap(), 0, _comprOptions.cbParms); + _comprOptions.lpParms = Win32Imports.HeapAlloc(_comprOptions.cbParms); Marshal.Copy(Parms, 0, _comprOptions.lpParms, _comprOptions.cbParms); } if (_comprOptions.cbFormat != 0) { - _comprOptions.lpFormat = HeapAlloc(GetProcessHeap(), 0, _comprOptions.cbFormat); + _comprOptions.lpFormat = Win32Imports.HeapAlloc(_comprOptions.cbFormat); Marshal.Copy(Format, 0, _comprOptions.lpFormat, _comprOptions.cbFormat); } diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs b/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs index e3147c9c242..deac9325f72 100644 --- a/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs +++ b/src/BizHawk.Client.EmuHawk/CustomControls/FolderBrowserDialogEx.cs @@ -3,9 +3,12 @@ using System.Threading; using System.Windows.Forms; -using BizHawk.Common; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.Shell.Common; -using static BizHawk.Common.Shell32Imports; +using static Windows.Win32.Win32Imports; namespace BizHawk.Client.EmuHawk { @@ -21,25 +24,24 @@ namespace BizHawk.Client.EmuHawk /// public sealed class FolderBrowserEx : Component { - private const BROWSEINFOW.FLAGS BrowseOptions = BROWSEINFOW.FLAGS.RestrictToFilesystem | BROWSEINFOW.FLAGS.RestrictToDomain | - BROWSEINFOW.FLAGS.NewDialogStyle | BROWSEINFOW.FLAGS.ShowTextBox; + private const uint BrowseOptions = BIF_NEWDIALOGSTYLE | BIF_EDITBOX | BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS; public string Description = "Please select a folder below:"; public string SelectedPath; /// Shows the folder browser dialog box with the specified owner window. - public DialogResult ShowDialog(IWin32Window owner = null) + public unsafe DialogResult ShowDialog(IWin32Window owner = null) { const int startLocation = 0; // = Desktop CSIDL - int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData) + int Callback(HWND hwnd, uint uMsg, LPARAM lParam, LPARAM lpData) { if (uMsg == BFFM_INITIALIZED) { var str = Marshal.StringToHGlobalUni(SelectedPath); try { - WmImports.SendMessageW(hwnd, BFFM_SETSELECTIONW, new(1), str); + WmImports.SendMessageW(hwnd, BFFM_SETSELECTIONW, new WPARAM(1), new LPARAM(str)); } finally { @@ -50,54 +52,44 @@ int Callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData) return 0; } - var hWndOwner = owner?.Handle ?? WmImports.GetActiveWindow(); - _ = SHGetSpecialFolderLocation(hWndOwner, startLocation, out var pidlRoot); - if (pidlRoot == IntPtr.Zero) - { - return DialogResult.Cancel; - } + HWND hWndOwner = new(owner?.Handle ?? WmImports.GetActiveWindow()); + ITEMIDLIST* pidlRoot = null; + _ = SHGetSpecialFolderLocation(hWndOwner, startLocation, &pidlRoot); + if (pidlRoot is null) return DialogResult.Cancel; - var pidlRet = IntPtr.Zero; - var pszDisplayName = IntPtr.Zero; + ITEMIDLIST* pidlRet = null; + PWSTR pszDisplayName = new(null); try { var browseOptions = BrowseOptions; - if (ApartmentState.MTA == Application.OleRequired()) - { - browseOptions &= ~BROWSEINFOW.FLAGS.NewDialogStyle; - } - + if (Application.OleRequired() is ApartmentState.MTA) browseOptions &= ~BIF_NEWDIALOGSTYLE; const int BUF_SIZE_BYTES = (int) Win32Imports.MAX_PATH * sizeof(char); - pszDisplayName = Marshal.AllocCoTaskMem(BUF_SIZE_BYTES); - var bi = new BROWSEINFOW + pszDisplayName = new(Marshal.AllocCoTaskMem(BUF_SIZE_BYTES)); + fixed (char* lpszTitle = Description) { - hwndOwner = hWndOwner, - pidlRoot = pidlRoot, - pszDisplayName = pszDisplayName, - lpszTitle = Description, - ulFlags = browseOptions, - lpfn = Callback, - }; - - pidlRet = SHBrowseForFolderW(ref bi); - if (pidlRet == IntPtr.Zero) - { - return DialogResult.Cancel; // user clicked Cancel + var bi = new BROWSEINFOW + { + hwndOwner = hWndOwner, + pidlRoot = pidlRoot, + pszDisplayName = pszDisplayName, + lpszTitle = lpszTitle, + ulFlags = browseOptions, + lpfn = Callback, + }; + pidlRet = SHBrowseForFolderW(in bi); } + if (pidlRet is null) return DialogResult.Cancel; // user clicked Cancel var path = new char[Win32Imports.MAX_PATH]; - if (SHGetPathFromIDListW(pidlRet, path) == 0) - { - return DialogResult.Cancel; - } + if (!SHGetPathFromIDListW(*pidlRet, path)) return DialogResult.Cancel; SelectedPath = new string(path).TrimEnd('\0'); } finally { - Marshal.FreeCoTaskMem(pidlRoot); - Marshal.FreeCoTaskMem(pidlRet); - Marshal.FreeCoTaskMem(pszDisplayName); + Marshal.FreeCoTaskMem(unchecked((IntPtr) pidlRoot)); + Marshal.FreeCoTaskMem(unchecked((IntPtr) pidlRet)); + Marshal.FreeCoTaskMem(unchecked((IntPtr) pszDisplayName.Value)); } return DialogResult.OK; diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/InputWidget.cs b/src/BizHawk.Client.EmuHawk/CustomControls/InputWidget.cs index c6941f81fe5..c1e518aa6cc 100644 --- a/src/BizHawk.Client.EmuHawk/CustomControls/InputWidget.cs +++ b/src/BizHawk.Client.EmuHawk/CustomControls/InputWidget.cs @@ -64,7 +64,7 @@ protected override void OnMouseClick(MouseEventArgs e) { if (!OSTailoredCode.IsUnixHost) { - WmImports.HideCaret(Handle); + WmImports.HideCaret(new(Handle)); } base.OnMouseClick(e); @@ -260,7 +260,7 @@ protected override void OnGotFocus(EventArgs e) { if (!OSTailoredCode.IsUnixHost) { - WmImports.HideCaret(Handle); + WmImports.HideCaret(new(Handle)); } } @@ -269,4 +269,4 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) return !(keyData.ToString() == "F4" || keyData.ToString().Contains("Alt")); } } -} \ No newline at end of file +} diff --git a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs index d0e1ec3bfb5..06ab0999fe6 100644 --- a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs +++ b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs @@ -4,6 +4,10 @@ using BizHawk.Common; using BizHawk.Common.StringExtensions; +using Windows.Win32; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; + namespace BizHawk.Client.EmuHawk { public static class EmuHawkUtil @@ -21,21 +25,13 @@ public static string ResolveShortcut(string filename) return filename; // archive internal files are never shortcuts (and choke when analyzing any further) } - using var link = new ShellLinkImports.ShellLink(); - - unsafe - { - const uint STGM_READ = 0; - ((ShellLinkImports.IPersistFile*)link)->Load(filename, STGM_READ); - + ShellLink link = new(); + ((IPersistFile) link).Load(filename, unchecked((uint) STGM.STGM_READ)); #if false - // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. - ((ShellLinkImports.IShellLinkW*)link)->Resolve(hwnd, 0); + // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. + ((IShellLinkW) link).Resolve(hwnd, 0); #endif - - ((ShellLinkImports.IShellLinkW*) link)->GetPath(out var path, (int) Win32Imports.MAX_PATH + 1, 0); - return path; - } + return ((IShellLinkW) link).GetPath(); } } } diff --git a/src/BizHawk.Client.EmuHawk/Extensions/ControlExtensions.cs b/src/BizHawk.Client.EmuHawk/Extensions/ControlExtensions.cs index b25d632af62..446d683f627 100644 --- a/src/BizHawk.Client.EmuHawk/Extensions/ControlExtensions.cs +++ b/src/BizHawk.Client.EmuHawk/Extensions/ControlExtensions.cs @@ -17,7 +17,10 @@ using BizHawk.Common.ReflectionExtensions; using BizHawk.Emulation.Common; -using static BizHawk.Common.CommctrlImports; +using Windows.Win32; +using Windows.Win32.UI.Controls; + +using static Windows.Win32.Win32Imports; namespace BizHawk.Client.EmuHawk { @@ -228,34 +231,38 @@ public static void SetSortIcon(this ListView listViewControl, int columnIndex, S return; } - var columnHeader = WmImports.SendMessageW(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); + var columnHeader = WmImports.SendMessageW( + new(listViewControl.Handle), + Win32Imports.LVM_GETHEADER, + default, + IntPtr.Zero); for (int columnNumber = 0, l = listViewControl.Columns.Count; columnNumber < l; columnNumber++) { var columnPtr = new IntPtr(columnNumber); - var item = new HDITEMW { mask = HDITEMW.Mask.Format }; - if (SendMessageW(columnHeader, HDM_GETITEMW, columnPtr, ref item) == IntPtr.Zero) + var item = new HDITEMW { mask = HDI_MASK.HDI_FORMAT }; + if (SendMessageW(new(columnHeader.Value), Win32Imports.HDM_GETITEMW, columnPtr, ref item) == IntPtr.Zero) { throw new Win32Exception(); } if (columnNumber != columnIndex || order == SortOrder.None) { - item.fmt &= ~HDITEMW.Format.SortDown & ~HDITEMW.Format.SortUp; + item.fmt &= ~(HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTDOWN | HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTUP); } // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault else switch (order) { case SortOrder.Ascending: - item.fmt &= ~HDITEMW.Format.SortDown; - item.fmt |= HDITEMW.Format.SortUp; + item.fmt &= ~HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTDOWN; + item.fmt |= HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTUP; break; case SortOrder.Descending: - item.fmt &= ~HDITEMW.Format.SortUp; - item.fmt |= HDITEMW.Format.SortDown; + item.fmt &= ~HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTUP; + item.fmt |= HEADER_CONTROL_FORMAT_FLAGS.HDF_SORTDOWN; break; } - if (SendMessageW(columnHeader, HDM_SETITEMW, columnPtr, ref item) == IntPtr.Zero) + if (SendMessageW(new(columnHeader.Value), Win32Imports.HDM_SETITEMW, columnPtr, ref item) == IntPtr.Zero) { throw new Win32Exception(); } diff --git a/src/BizHawk.Client.EmuHawk/Program.cs b/src/BizHawk.Client.EmuHawk/Program.cs index f1ea4b31e48..d6b236031d2 100644 --- a/src/BizHawk.Client.EmuHawk/Program.cs +++ b/src/BizHawk.Client.EmuHawk/Program.cs @@ -14,6 +14,8 @@ using BizHawk.Client.EmuHawk.CustomControls; using BizHawk.Emulation.Cores; +using Windows.Win32; + namespace BizHawk.Client.EmuHawk { internal static class Program @@ -132,7 +134,7 @@ private static int SubMain(string[] args) if (dllDir.ContainsOrdinal(';')) { - var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, null, 0); + var dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir); if (dllShortPathLen == 0) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); @@ -140,14 +142,14 @@ private static int SubMain(string[] args) } var dllShortPathBuffer = new char[dllShortPathLen]; - dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer, dllShortPathLen); + dllShortPathLen = Win32Imports.GetShortPathNameW(dllDir, dllShortPathBuffer); if (dllShortPathLen == 0) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); return -1; } - dllDir = new string(dllShortPathBuffer, 0, dllShortPathLen); + dllDir = dllShortPathBuffer.AsSpan(start: 0, length: (int) dllShortPathLen).ToString(); if (dllDir.ContainsOrdinal(';')) { MessageBox.Show(SEMICOLON_IN_DIR_MSG); diff --git a/src/BizHawk.Client.EmuHawk/ScreenSaver.cs b/src/BizHawk.Client.EmuHawk/ScreenSaver.cs index 72163977e5d..ffee8da8f69 100644 --- a/src/BizHawk.Client.EmuHawk/ScreenSaver.cs +++ b/src/BizHawk.Client.EmuHawk/ScreenSaver.cs @@ -1,5 +1,8 @@ using BizHawk.Common; +using Windows.Win32; +using Windows.Win32.UI.WindowsAndMessaging; + namespace BizHawk.Client.EmuHawk { /// Derived from http://www.codeproject.com/KB/cs/ScreenSaverControl.aspx @@ -19,17 +22,18 @@ public int Duration { get { - const int SPI_GETSCREENSAVERTIMEOUT = 14; - int value = default; - Win32Imports.SystemParametersInfoW(SPI_GETSCREENSAVERTIMEOUT, 0, ref value, 0); - return value; + _ = Win32Imports.SystemParametersInfoW( + SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETSCREENSAVETIMEOUT, + uiParam: 0, + out var value); + return unchecked((int) value); } set { - const int SPI_SETSCREENSAVERTIMEOUT = 15; - const int SPIF_SENDWININICHANGE = 2; - int nullVar = default; - Win32Imports.SystemParametersInfoW(SPI_SETSCREENSAVERTIMEOUT, value, ref nullVar, SPIF_SENDWININICHANGE); + _ = Win32Imports.SystemParametersInfoW( + SYSTEM_PARAMETERS_INFO_ACTION.SPI_SETSCREENSAVETIMEOUT, + unchecked((uint) value), + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS.SPIF_SENDWININICHANGE); } } } diff --git a/src/BizHawk.Client.EmuHawk/Throttle.cs b/src/BizHawk.Client.EmuHawk/Throttle.cs index 45557b1ee0a..b1904ed76fd 100644 --- a/src/BizHawk.Client.EmuHawk/Throttle.cs +++ b/src/BizHawk.Client.EmuHawk/Throttle.cs @@ -4,6 +4,8 @@ using BizHawk.Client.Common; using BizHawk.Common; +using Windows.Win32; + //this throttle is nitsuja's fine-tuned techniques from desmume namespace BizHawk.Client.EmuHawk diff --git a/src/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs b/src/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs index 707e23d2b1f..a27fa76712e 100644 --- a/src/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs +++ b/src/BizHawk.Client.EmuHawk/tools/SNES/SNESGraphicsDebugger.cs @@ -34,6 +34,9 @@ using BizHawk.Emulation.Common; using BizHawk.Common; +using Windows.Win32; +using Windows.Win32.Foundation; + namespace BizHawk.Client.EmuHawk { public unsafe partial class SNESGraphicsDebugger : ToolFormBase, IToolFormAutoConfig @@ -872,8 +875,8 @@ private void Freeze() if (!OSTailoredCode.IsUnixHost) { - // WM_SETREDRAW false - WmImports.SendMessageW(groupFreeze.Handle, 11, (IntPtr)0, IntPtr.Zero); + WPARAM falseVal = new(0); + WmImports.SendMessageW(new(groupFreeze.Handle), Win32Imports.WM_SETREDRAW, falseVal, default); } var tp = tabctrlDetails.SelectedTab; @@ -894,8 +897,8 @@ private void Freeze() if (!OSTailoredCode.IsUnixHost) { - // WM_SETREDRAW true - WmImports.SendMessageW(groupFreeze.Handle, 11, (IntPtr)1, IntPtr.Zero); + WPARAM trueVal = new(1); + WmImports.SendMessageW(new(groupFreeze.Handle), Win32Imports.WM_SETREDRAW, trueVal, default); } groupFreeze.Refresh(); diff --git a/src/BizHawk.Common/BizHawk.Common.csproj b/src/BizHawk.Common/BizHawk.Common.csproj index a110093bce4..cde4fc8a2cd 100644 --- a/src/BizHawk.Common/BizHawk.Common.csproj +++ b/src/BizHawk.Common/BizHawk.Common.csproj @@ -5,18 +5,18 @@ true + System.Diagnostics.CodeAnalysis.UnscopedRefAttribute true + - - diff --git a/src/BizHawk.Common/Extensions/PathExtensions.cs b/src/BizHawk.Common/Extensions/PathExtensions.cs index 5a032bc1773..cc79630da91 100644 --- a/src/BizHawk.Common/Extensions/PathExtensions.cs +++ b/src/BizHawk.Common/Extensions/PathExtensions.cs @@ -2,6 +2,8 @@ using BizHawk.Common.StringExtensions; +using Windows.Win32; + namespace BizHawk.Common.PathExtensions { public static class PathExtensions diff --git a/src/BizHawk.Common/MemoryBlock/MemoryBlockWindowsPal.cs b/src/BizHawk.Common/MemoryBlock/MemoryBlockWindowsPal.cs index 4ba9a1c4cc1..3786f051a48 100644 --- a/src/BizHawk.Common/MemoryBlock/MemoryBlockWindowsPal.cs +++ b/src/BizHawk.Common/MemoryBlock/MemoryBlockWindowsPal.cs @@ -1,5 +1,7 @@ -using static BizHawk.Common.MemoryApiImports; +using Windows.Win32.System.Memory; + using static BizHawk.Common.MemoryBlock; +using static Windows.Win32.Win32Imports; namespace BizHawk.Common { @@ -11,7 +13,10 @@ internal sealed class MemoryBlockWindowsPal : IMemoryBlockPal public MemoryBlockWindowsPal(ulong size) { var ptr = (ulong)VirtualAlloc( - UIntPtr.Zero, Z.UU(size), AllocationType.MEM_RESERVE | AllocationType.MEM_COMMIT, MemoryProtection.NOACCESS); + UIntPtr.Zero, + Z.UU(size), + VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE | VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT, + PAGE_PROTECTION_FLAGS.PAGE_NOACCESS); if (ptr == 0) { @@ -29,12 +34,12 @@ public void Protect(ulong start, ulong size, Protection prot) } } - private static MemoryProtection GetKernelMemoryProtectionValue(Protection prot) => prot switch + private static PAGE_PROTECTION_FLAGS GetKernelMemoryProtectionValue(Protection prot) => prot switch { - Protection.None => MemoryProtection.NOACCESS, - Protection.R => MemoryProtection.READONLY, - Protection.RW => MemoryProtection.READWRITE, - Protection.RX => MemoryProtection.EXECUTE_READ, + Protection.None => PAGE_PROTECTION_FLAGS.PAGE_NOACCESS, + Protection.R => PAGE_PROTECTION_FLAGS.PAGE_READONLY, + Protection.RW => PAGE_PROTECTION_FLAGS.PAGE_READWRITE, + Protection.RX => PAGE_PROTECTION_FLAGS.PAGE_EXECUTE_READ, _ => throw new InvalidOperationException(nameof(prot)), }; @@ -45,7 +50,7 @@ public void Dispose() return; } - VirtualFree(Z.UU(Start), UIntPtr.Zero, FreeType.Release); + VirtualFree(Z.UU(Start), UIntPtr.Zero, VIRTUAL_FREE_TYPE.MEM_RELEASE); _disposed = true; } } diff --git a/src/BizHawk.Common/NativeMethods.json b/src/BizHawk.Common/NativeMethods.json new file mode 100644 index 00000000000..f5055e25230 --- /dev/null +++ b/src/BizHawk.Common/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "className": "Win32Imports", + "multiTargetingFriendlyAPIs": true, + "public": true, + "wideCharOnly": false, + "$schema": "https://aka.ms/CsWin32.schema.json" +} diff --git a/src/BizHawk.Common/NativeMethods.txt b/src/BizHawk.Common/NativeMethods.txt new file mode 100644 index 00000000000..cb3fa5abe44 --- /dev/null +++ b/src/BizHawk.Common/NativeMethods.txt @@ -0,0 +1,70 @@ +BFFM_INITIALIZED +BFFM_SETSELECTIONW +BIF_DONTGOBELOWDOMAIN +BIF_EDITBOX +BIF_NEWDIALOGSTYLE +BIF_RETURNONLYFSDIRS +BROWSEINFOW +ChangeWindowMessageFilter +CMF_EXPLORE +CreatePopupMenu +CreateWindowExW +DefWindowProcW +DeleteFileW +DestroyMenu +DestroyWindow +DispatchMessageW +FormatMessageW +FreeLibrary +GetActiveWindow +GetCurrentDirectoryW +GetCurrentProcess +GetModuleHandleW +GetProcAddress +GetProcessHeap +GetRawInputBuffer +GetRawInputData +GetShortPathNameW +HDITEMW +HDM_GETITEMW +HDM_SETITEMW +HeapAlloc +HeapFree +HideCaret +HWND_MESSAGE +IContextMenu +IContextMenu2 +ILFindLastID +IPersistFile +IShellFolder +IShellItem +IShellLinkW +IsWow64Process +LoadLibraryW +LVM_GETHEADER +MAX_PATH +MapVirtualKeyW +PathRelativePathToW +PeekMessageW +PostMessageW +RegisterClassW +RegisterRawInputDevices +SendMessageW +SetCurrentDirectoryW +SetDllDirectoryW +SHBrowseForFolderW +SHCreateItemFromParsingName +ShellLink +SHGetIDListFromObject +SHGetPathFromIDListW +SHGetSpecialFolderLocation +STGM +SystemParametersInfoW +timeBeginPeriod +TrackPopupMenuEx +TranslateMessage +VirtualAlloc +VirtualFree +VirtualProtect +WINDOW_LONG_PTR_INDEX +WM_SETREDRAW diff --git a/src/BizHawk.Common/OSTailoredCode.cs b/src/BizHawk.Common/OSTailoredCode.cs index f5c4f2e2ea6..abda14b9055 100644 --- a/src/BizHawk.Common/OSTailoredCode.cs +++ b/src/BizHawk.Common/OSTailoredCode.cs @@ -3,7 +3,12 @@ using BizHawk.Common.StringExtensions; -using static BizHawk.Common.LoaderApiImports; +using Microsoft.Win32.SafeHandles; + +using Windows.Win32; +using Windows.Win32.System.Diagnostics.Debug; + +using static Windows.Win32.Win32Imports; namespace BizHawk.Common { @@ -210,9 +215,24 @@ public string GetErrorMessage() private class WindowsLLManager : ILinkedLibManager { - public int FreeByPtr(IntPtr hModule) => FreeLibrary(hModule) ? 0 : 1; + private sealed class DummySafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public DummySafeHandle(IntPtr hModule) + : base(ownsHandle: true) + => SetHandle(hModule); + + protected override bool ReleaseHandle() + => true; + } - public IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) => GetProcAddress(hModule, procName); + public int FreeByPtr(IntPtr hModule) + => FreeLibrary(new(hModule)) ? 0 : 1; + + public unsafe IntPtr GetProcAddrOrZero(IntPtr hModule, string procName) + { + DummySafeHandle wrapper = new(hModule); + return GetProcAddress(wrapper, procName); + } public IntPtr GetProcAddrOrThrow(IntPtr hModule, string procName) { @@ -220,7 +240,10 @@ public IntPtr GetProcAddrOrThrow(IntPtr hModule, string procName) return ret != IntPtr.Zero ? ret : throw new InvalidOperationException($"got null pointer from {nameof(GetProcAddress)} trying to find symbol {procName}, {GetErrorMessage()}"); } - public IntPtr LoadOrZero(string dllToLoad) => LoadLibraryW(dllToLoad); + public unsafe IntPtr LoadOrZero(string dllToLoad) + { + fixed (char* ptr = dllToLoad) return LoadLibraryW(ptr); + } public IntPtr LoadOrThrow(string dllToLoad) { @@ -231,10 +254,16 @@ public IntPtr LoadOrThrow(string dllToLoad) public unsafe string GetErrorMessage() { var errCode = Win32Imports.GetLastError(); - var buffer = stackalloc char[1024]; - const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000; - var sz = Win32Imports.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errCode, 0, buffer, 1024, IntPtr.Zero); - return $"error code: 0x{errCode:X8}, error message: {new string(buffer, 0, sz)}"; + Span buffer = stackalloc char[1024]; + var sz = Win32Imports.FormatMessageW( + FORMAT_MESSAGE_OPTIONS.FORMAT_MESSAGE_FROM_SYSTEM, + lpSource: default, + dwMessageId: errCode, + dwLanguageId: 0, + buffer, + nSize: 1024, + Arguments: default); + return $"error code: 0x{errCode:X8}, error message: {buffer.Slice(start: 0, length: (int) sz).ToString()}"; } } diff --git a/src/BizHawk.Common/TempFileManager.cs b/src/BizHawk.Common/TempFileManager.cs index 3338545282c..395804a954a 100644 --- a/src/BizHawk.Common/TempFileManager.cs +++ b/src/BizHawk.Common/TempFileManager.cs @@ -5,6 +5,8 @@ using BizHawk.Common.PathExtensions; +using Windows.Win32; + namespace BizHawk.Common { /// diff --git a/src/BizHawk.Common/Win32/CWDHacks.cs b/src/BizHawk.Common/Win32/CWDHacks.cs index 9df35f645c8..e30707b8d43 100644 --- a/src/BizHawk.Common/Win32/CWDHacks.cs +++ b/src/BizHawk.Common/Win32/CWDHacks.cs @@ -1,16 +1,14 @@ using System.Runtime.InteropServices; +using Windows.Win32; + +using static Windows.Win32.Win32Imports; + namespace BizHawk.Common { /// Gets/Sets the current working directory while bypassing the security checks triggered by the public API (). public static class CWDHacks { - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - private static extern unsafe int GetCurrentDirectoryW(int nBufferLength, char* lpBuffer); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - private static extern bool SetCurrentDirectoryW(string lpPathName); - public static bool Set(string newCWD) => SetCurrentDirectoryW(newCWD); @@ -24,14 +22,14 @@ static Exception GetExceptionForFailure() } const int STARTING_BUF_SIZE = (int) Win32Imports.MAX_PATH + 1; - var startingBuffer = stackalloc char[STARTING_BUF_SIZE]; - var ret = GetCurrentDirectoryW(STARTING_BUF_SIZE, startingBuffer); + Span startingBuffer = stackalloc char[STARTING_BUF_SIZE]; + var ret = GetCurrentDirectoryW(startingBuffer); switch (ret) { case 0: throw GetExceptionForFailure(); case < STARTING_BUF_SIZE: // ret should be smaller than the buffer, as ret doesn't include null terminator - return new(startingBuffer, 0, ret); + return startingBuffer.Slice(start: 0, length: unchecked((int) ret)).ToString(); } // since current directory could suddenly grow (due to it being global / modifiable by other threads), a while true loop is used here @@ -40,18 +38,15 @@ static Exception GetExceptionForFailure() { var bufSize = ret; var buffer = new char[bufSize]; - fixed (char* p = buffer) + ret = GetCurrentDirectoryW(buffer); + if (ret == 0) { - ret = GetCurrentDirectoryW(bufSize, p); - if (ret == 0) - { - throw GetExceptionForFailure(); - } + throw GetExceptionForFailure(); + } - if (ret < bufSize) - { - return new(p, 0, ret); - } + if (ret < bufSize) + { + return new(buffer, startIndex: 0, length: unchecked((int) ret)); } } } diff --git a/src/BizHawk.Common/Win32/CommctrlImports.cs b/src/BizHawk.Common/Win32/CommctrlImports.cs deleted file mode 100644 index 9eaaaa5bf46..00000000000 --- a/src/BizHawk.Common/Win32/CommctrlImports.cs +++ /dev/null @@ -1,59 +0,0 @@ -#nullable disable - -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class CommctrlImports - { - public const int LVM_FIRST = 0x1000; - public const int LVM_GETHEADER = LVM_FIRST + 31; - - public const int HDM_FIRST = 0x1200; - public const int HDM_GETITEMW = HDM_FIRST + 11; - public const int HDM_SETITEMW = HDM_FIRST + 12; - - [StructLayout(LayoutKind.Sequential)] - public struct HDITEMW - { - public Mask mask; - public int cxy; - [MarshalAs(UnmanagedType.LPWStr)] - public string pszText; - public IntPtr hbm; - public int cchTextMax; - public Format fmt; - public IntPtr lParam; - - // _WIN32_IE >= 0x0300 - public int iImage; - public int iOrder; - - // _WIN32_IE >= 0x0500 - public uint type; - public IntPtr pvFilter; - - // _WIN32_WINNT >= 0x0600 - public uint state; - - [Flags] - public enum Mask : uint - { - Format = 0x4, - } - - [Flags] - public enum Format : int - { - SortDown = 0x200, - SortUp = 0x400, - } - } - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr SendMessageW(IntPtr hWnd, uint msg, IntPtr wParam, ref HDITEMW lParam); - } -} \ No newline at end of file diff --git a/src/BizHawk.Common/Win32/HeapApiImports.cs b/src/BizHawk.Common/Win32/HeapApiImports.cs deleted file mode 100644 index 8f9d884c25b..00000000000 --- a/src/BizHawk.Common/Win32/HeapApiImports.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class HeapApiImports - { - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcessHeap(); - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] - public static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, int dwBytes); - - /// used in #if false code in AviWriter.CodecToken.DeallocateAVICOMPRESSOPTIONS, don't delete it - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem); - } -} diff --git a/src/BizHawk.Common/Win32/LoaderApiImports.cs b/src/BizHawk.Common/Win32/LoaderApiImports.cs deleted file mode 100644 index f5913f17282..00000000000 --- a/src/BizHawk.Common/Win32/LoaderApiImports.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Runtime.InteropServices; - -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class LoaderApiImports - { - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetModuleHandleW(string? lpModuleName); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr LoadLibraryW(string lpLibFileName); - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool FreeLibrary(IntPtr hLibModule); - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, IntPtr lpProcName); - } -} diff --git a/src/BizHawk.Common/Win32/MemoryApiImports.cs b/src/BizHawk.Common/Win32/MemoryApiImports.cs deleted file mode 100644 index 0731a4d93f4..00000000000 --- a/src/BizHawk.Common/Win32/MemoryApiImports.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class MemoryApiImports - { - [Flags] - public enum AllocationType : uint - { - MEM_COMMIT = 0x00001000, - MEM_RESERVE = 0x00002000, - MEM_RESET = 0x00080000, - MEM_RESET_UNDO = 0x1000000, - MEM_LARGE_PAGES = 0x20000000, - MEM_PHYSICAL = 0x00400000, - MEM_TOP_DOWN = 0x00100000, - MEM_WRITE_WATCH = 0x00200000, - } - - [Flags] - public enum MemoryProtection : uint - { - EXECUTE = 0x10, - EXECUTE_READ = 0x20, - EXECUTE_READWRITE = 0x40, - EXECUTE_WRITECOPY = 0x80, - NOACCESS = 0x01, - READONLY = 0x02, - READWRITE = 0x04, - WRITECOPY = 0x08, - GUARD_Modifierflag = 0x100, - NOCACHE_Modifierflag = 0x200, - WRITECOMBINE_Modifierflag = 0x400, - } - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - public static extern UIntPtr VirtualAlloc(UIntPtr lpAddress, UIntPtr dwSize, - AllocationType flAllocationType, MemoryProtection flProtect); - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool VirtualProtect( - UIntPtr lpAddress, - UIntPtr dwSize, - MemoryProtection flNewProtect, - out MemoryProtection lpflOldProtect); - - [Flags] - public enum FreeType : uint - { - Decommit = 0x4000, - Release = 0x8000, - } - - [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool VirtualFree(UIntPtr lpAddress, UIntPtr dwSize, FreeType dwFreeType); - } -} diff --git a/src/BizHawk.Common/Win32/MotWHack.cs b/src/BizHawk.Common/Win32/MotWHack.cs index 52e2a645b4f..3933f305d65 100644 --- a/src/BizHawk.Common/Win32/MotWHack.cs +++ b/src/BizHawk.Common/Win32/MotWHack.cs @@ -1,6 +1,8 @@ +using Windows.Win32; + namespace BizHawk.Common { - /// This code (and an import for ) is duplicated in each executable project because it needs to be used before loading assemblies. + /// This code (and an import for DeleteFileW) is duplicated in each executable project because it needs to be used before loading assemblies. public static class MotWHack { public static void RemoveMOTW(string path) diff --git a/src/BizHawk.Common/Win32/Ole32Imports.cs b/src/BizHawk.Common/Win32/Ole32Imports.cs deleted file mode 100644 index 1571a1a03ce..00000000000 --- a/src/BizHawk.Common/Win32/Ole32Imports.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Runtime.InteropServices; - -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class Ole32Imports - { - public enum CLSCTX : uint - { - INPROC_SERVER = 0x1, - INPROC_HANDLER = 0x2, - LOCAL_SERVER = 0x4, - INPROC_SERVER16 = 0x8, - REMOTE_SERVER = 0x10, - INPROC_HANDLER16 = 0x20, - RESERVED1 = 0x40, - RESERVED2 = 0x80, - RESERVED3 = 0x100, - RESERVED4 = 0x200, - NO_CODE_DOWNLOAD = 0x400, - RESERVED5 = 0x800, - NO_CUSTOM_MARSHAL = 0x1000, - ENABLE_CODE_DOWNLOAD = 0x2000, - NO_FAILURE_LOG = 0x4000, - DISABLE_AAA = 0x8000, - ENABLE_AAA = 0x10000, - FROM_DEFAULT_CONTEXT = 0x20000, - ACTIVATE_X86_SERVER = 0x40000, - ACTIVATE_32_BIT_SERVER, - ACTIVATE_64_BIT_SERVER = 0x80000, - ENABLE_CLOAKING = 0x100000, - APPCONTAINER = 0x400000, - ACTIVATE_AAA_AS_IU = 0x800000, - RESERVED6 = 0x1000000, - ACTIVATE_ARM32_SERVER = 0x2000000, - ALLOW_LOWER_TRUST_REGISTRATION = 0x4000000, // missing from official docs lmao, but confirmed by https://github.com/microsoft/windows-rs/blob/3fd93b0a8064e4c9c2fb6f43da798030124ee9c5/crates/libs/sys/src/Windows/Win32/System/Com/mod.rs#L315 - PS_DLL = 0x80000000, - } - - [DllImport("ole32.dll", ExactSpelling = true)] - public static extern int CoCreateInstance( - [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, - IntPtr pUnkOuter, - CLSCTX dwClsContext, - [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out IntPtr ppv); - } -} diff --git a/src/BizHawk.Common/Win32/RawInputImports.cs b/src/BizHawk.Common/Win32/RawInputImports.cs index 52175dc7d3c..d6fb1d8274d 100644 --- a/src/BizHawk.Common/Win32/RawInputImports.cs +++ b/src/BizHawk.Common/Win32/RawInputImports.cs @@ -1,5 +1,3 @@ -using System.Runtime.InteropServices; - // ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable UnusedMember.Global @@ -359,156 +357,38 @@ public enum VirtualKey : ushort VK_NONE = 0xFF, } - [StructLayout(LayoutKind.Sequential)] - public struct RAWINPUTDEVICE + public static class HidUsagePage { - public HidUsagePage usUsagePage; - public HidUsageId usUsage; - public RIDEV dwFlags; - public IntPtr hwndTarget; - - public enum HidUsagePage : ushort - { - GENERIC = 1, - GAME = 5, - LED = 8, - BUTTON = 9, - } - - public enum HidUsageId : ushort - { - GENERIC_POINTER = 1, - GENERIC_MOUSE = 2, - GENERIC_JOYSTICK = 4, - GENERIC_GAMEPAD = 5, - GENERIC_KEYBOARD = 6, - GENERIC_KEYPAD = 7, - GENERIC_MULTI_AXIS_CONTROLLER = 8, - } - - [Flags] - public enum RIDEV : int - { - REMOVE = 0x00000001, - EXCLUDE = 0x00000010, - PAGEONLY = 0x00000020, - NOLEGACY = PAGEONLY | EXCLUDE, - INPUTSINK = 0x00000100, - CAPTUREMOUSE = 0x00000200, - NOHOTKEYS = CAPTUREMOUSE, - APPKEYS = 0x00000400, - EXINPUTSINK = 0x00001000, - DEVNOTIFY = 0x00002000, - } + public const ushort GENERIC = 1; + public const ushort GAME = 5; + public const ushort LED = 8; + public const ushort BUTTON = 9; } - public enum RID : uint + public static class HidUsageId { - HEADER = 0x10000005, - INPUT = 0x10000003, + public const ushort GENERIC_POINTER = 1; + public const ushort GENERIC_MOUSE = 2; + public const ushort GENERIC_JOYSTICK = 4; + public const ushort GENERIC_GAMEPAD = 5; + public const ushort GENERIC_KEYBOARD = 6; + public const ushort GENERIC_KEYPAD = 7; + public const ushort GENERIC_MULTI_AXIS_CONTROLLER = 8; } - [StructLayout(LayoutKind.Sequential)] - public struct RAWINPUTHEADER + public static class RIM_TYPE { - public RIM_TYPE dwType; - public uint dwSize; - public IntPtr hDevice; - public IntPtr wParam; - - public enum RIM_TYPE : uint - { - MOUSE = 0, - KEYBOARD = 1, - HID = 2, - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct RAWMOUSE - { - public MOUSE_FLAGS usFlags; - public uint ulButtons; - public uint ulRawButtons; - public int lLastX; - public int lLastY; - public uint ulExtraInformation; - - [Flags] - public enum MOUSE_FLAGS : ushort - { - MOVE_RELATIVE = 0, - MOVE_ABSOLUTE = 1, - VIRTUAL_DESKTOP = 2, - ATTRIBUTES_CHANGED = 4, - MOVE_NOCOALESCE = 8, - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct RAWKEYBOARD - { - public ushort MakeCode; - public RI_KEY Flags; - public ushort Reserved; - public VirtualKey VKey; - public uint Message; - public uint ExtraInformation; - - [Flags] - public enum RI_KEY : ushort - { - MAKE = 0, - BREAK = 1, - E0 = 2, - E1 = 4, - } + public const uint MOUSE = 0; + public const uint KEYBOARD = 1; + public const uint HID = 2; } - [StructLayout(LayoutKind.Sequential)] - public struct RAWHID + public static class RI_KEY { - public uint dwSizeHid; - public uint dwCount; - public byte bRawData; + public const ushort MAKE = 0; + public const ushort BREAK = 1; + public const ushort E0 = 2; + public const ushort E1 = 4; } - - [StructLayout(LayoutKind.Explicit)] - public struct RAWINPUTDATA - { - [FieldOffset(0)] - public RAWMOUSE mouse; - [FieldOffset(0)] - public RAWKEYBOARD keyboard; - [FieldOffset(0)] - public RAWHID hid; - } - - [StructLayout(LayoutKind.Sequential)] - public struct RAWINPUT - { - public RAWINPUTHEADER header; - public RAWINPUTDATA data; - } - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern int GetRawInputData(IntPtr hRawInput, RID uiCommand, IntPtr pData, out int bSize, int cbSizeHeader); - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern unsafe int GetRawInputData(IntPtr hRawInput, RID uiCommand, RAWINPUT* pData, ref int bSize, int cbSizeHeader); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - public static extern unsafe int GetRawInputBuffer(RAWINPUT* pData, ref int bSize, int cbSizeHeader); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern unsafe bool RegisterRawInputDevices(RAWINPUTDEVICE* pRawInputDevice, uint uiNumDevices, int cbSize); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, uint uiNumDevices, int cbSize); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern uint MapVirtualKeyW(uint uCode, uint uMapType); } } diff --git a/src/BizHawk.Common/Win32/Shell32Imports.cs b/src/BizHawk.Common/Win32/Shell32Imports.cs deleted file mode 100644 index 676a08d00b6..00000000000 --- a/src/BizHawk.Common/Win32/Shell32Imports.cs +++ /dev/null @@ -1,86 +0,0 @@ -#nullable disable - -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class Shell32Imports - { - public const int BFFM_INITIALIZED = 1; - public const int BFFM_SETSELECTIONW = 0x400 + 103; - - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct BROWSEINFOW - { - public IntPtr hwndOwner; - public IntPtr pidlRoot; - public IntPtr pszDisplayName; - [MarshalAs(UnmanagedType.LPWStr)] - public string lpszTitle; - public FLAGS ulFlags; - [MarshalAs(UnmanagedType.FunctionPtr)] - public BFFCALLBACK lpfn; - public IntPtr lParam; - public int iImage; - - [Flags] - public enum FLAGS - { - /// BIF_RETURNONLYFSDIRS - RestrictToFilesystem = 0x0001, - - /// BIF_DONTGOBELOWDOMAIN - RestrictToDomain = 0x0002, - - /// BIF_RETURNFSANCESTORS - RestrictToSubfolders = 0x0008, - - /// BIF_EDITBOX - ShowTextBox = 0x0010, - - /// BIF_VALIDATE - ValidateSelection = 0x0020, - - /// BIF_NEWDIALOGSTYLE - NewDialogStyle = 0x0040, - - /// BIF_BROWSEFORCOMPUTER - BrowseForComputer = 0x1000, - - /// BIF_BROWSEFORPRINTER - BrowseForPrinter = 0x2000, - - /// BIF_BROWSEINCLUDEFILES - BrowseForEverything = 0x4000, - } - } - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern IntPtr SHBrowseForFolderW(ref BROWSEINFOW bi); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern int SHGetPathFromIDListW(IntPtr pidl, char[] pszPath); - - [DllImport("shell32.dll", ExactSpelling = true)] - public static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, int nFolder, out IntPtr ppidl); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern int SHCreateItemFromParsingName( - [In] string pszPath, - [In] IntPtr pbc, - [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out IntPtr ppv); - - [DllImport("shell32.dll", ExactSpelling = true)] - public static extern int SHGetIDListFromObject(IntPtr punk, out IntPtr ppidl); - - [DllImport("shell32.dll", EntryPoint = "#16")] - public static extern IntPtr ILFindLastID(IntPtr pidl); - } -} diff --git a/src/BizHawk.Common/Win32/ShellLinkImports.cs b/src/BizHawk.Common/Win32/ShellLinkImports.cs deleted file mode 100644 index 797f899b88d..00000000000 --- a/src/BizHawk.Common/Win32/ShellLinkImports.cs +++ /dev/null @@ -1,157 +0,0 @@ -#nullable disable -#pragma warning disable BHI1300 // explicit conversion operators w/ no checked variant - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global -// ReSharper disable UnusedMember.Global - -namespace BizHawk.Common -{ - public static class ShellLinkImports - { - /// The IShellLink interface allows Shell links to be created, modified, and resolved - [StructLayout(LayoutKind.Sequential)] - public unsafe struct IShellLinkW - { - public static readonly Guid Guid = new("000214F9-0000-0000-C000-000000000046"); - - [StructLayout(LayoutKind.Sequential)] - public struct IShellLinkWVtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IShellLinkW functions - public delegate* unmanaged[Stdcall] GetPath; - public delegate* unmanaged[Stdcall] GetIDList; - public delegate* unmanaged[Stdcall] SetIDList; - public delegate* unmanaged[Stdcall] GetDescription; - public delegate* unmanaged[Stdcall] SetDescription; - public delegate* unmanaged[Stdcall] GetWorkingDirectory; - public delegate* unmanaged[Stdcall] SetWorkingDirectory; - public delegate* unmanaged[Stdcall] GetArguments; - public delegate* unmanaged[Stdcall] SetArguments; - public delegate* unmanaged[Stdcall] GetHotkey; - public delegate* unmanaged[Stdcall] SetHotkey; - public delegate* unmanaged[Stdcall] GetShowCmd; - public delegate* unmanaged[Stdcall] SetShowCmd; - public delegate* unmanaged[Stdcall] GetIconLocation; - public delegate* unmanaged[Stdcall] SetIconLocation; - public delegate* unmanaged[Stdcall] SetRelativePath; - public delegate* unmanaged[Stdcall] Resolve; - public delegate* unmanaged[Stdcall] SetPath; - } - - public IShellLinkWVtbl* lpVtbl; - - public void GetPath(out string pszFile, int cch, uint fFlags) - { - var _pszFile = Marshal.AllocCoTaskMem(cch * sizeof(char)); - try - { - var hr = lpVtbl->GetPath((IShellLinkW*)Unsafe.AsPointer(ref this), _pszFile, cch, IntPtr.Zero, fFlags); - Marshal.ThrowExceptionForHR(hr); - pszFile = Marshal.PtrToStringUni(_pszFile); - } - finally - { - Marshal.FreeCoTaskMem(_pszFile); - } - } - -#if false - public void Resolve(IntPtr hwnd, int fFlags) - { - var hr = lpVtbl->Resolve((IShellLinkW*)Unsafe.AsPointer(ref this), hwnd, fFlags); - Marshal.ThrowExceptionForHR(hr); - } -#endif - } - - [StructLayout(LayoutKind.Sequential)] - public unsafe struct IPersistFile - { - public static readonly Guid Guid = new("0000010b-0000-0000-C000-000000000046"); - - [StructLayout(LayoutKind.Sequential)] - public struct IPersistFileVtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IPersist functions - public delegate* unmanaged[Stdcall] GetClassID; - // IPersistFile functions - public delegate* unmanaged[Stdcall] IsDirty; - public delegate* unmanaged[Stdcall] Load; - public delegate* unmanaged[Stdcall] Save; - public delegate* unmanaged[Stdcall] SaveCompleted; - public delegate* unmanaged[Stdcall] GetCurFile; - } - - public IPersistFileVtbl* lpVtbl; - - public void Load(string pszFileName, uint dwMode) - { - var _pszFileName = Marshal.StringToCoTaskMemUni(pszFileName); - try - { - var hr = lpVtbl->Load((IPersistFile*)Unsafe.AsPointer(ref this), _pszFileName, dwMode); - Marshal.ThrowExceptionForHR(hr); - } - finally - { - Marshal.FreeCoTaskMem(_pszFileName); - } - } - } - - /// CLSID_ShellLink from ShlGuid.h - public unsafe class ShellLink : IDisposable - { - public static readonly Guid Guid = new("00021401-0000-0000-C000-000000000046"); - public static explicit operator IShellLinkW*(ShellLink link) => link.SLI; - public static explicit operator IPersistFile*(ShellLink link) => link.PFI; - - private IShellLinkW* SLI; - private IPersistFile* PFI; - - public ShellLink() - { - var hr = Ole32Imports.CoCreateInstance(Guid, IntPtr.Zero, Ole32Imports.CLSCTX.INPROC_SERVER, IShellLinkW.Guid, out var psl); - Marshal.ThrowExceptionForHR(hr); - - var sli = (IShellLinkW*)psl; - hr = sli->lpVtbl->QueryInterface(sli, in IPersistFile.Guid, out var ppf); - var hrEx = Marshal.GetExceptionForHR(hr); - if (hrEx != null) - { - sli->lpVtbl->Release(sli); - throw hrEx; - } - - SLI = sli; - PFI = (IPersistFile*)ppf; - } - - public void Dispose() - { - if (PFI != null) - { - PFI->lpVtbl->Release(PFI); - PFI = null; - } - - if (SLI != null) - { - SLI->lpVtbl->Release(SLI); - SLI = null; - } - } - } - } -} diff --git a/src/BizHawk.Common/Win32/Win32Imports.cs b/src/BizHawk.Common/Win32/Win32Imports.cs deleted file mode 100644 index 66fe84d7ada..00000000000 --- a/src/BizHawk.Common/Win32/Win32Imports.cs +++ /dev/null @@ -1,85 +0,0 @@ -#nullable disable - -using System.IO; -using System.Runtime.InteropServices; - -// ReSharper disable UnusedMember.Global - -#pragma warning disable CA1069 // This warning is just dumb - -namespace BizHawk.Common -{ - /// - /// This is more just an assorted bunch of Win32 functions - /// - public static class Win32Imports - { - public const uint MAX_PATH = 260U; - - [Flags] - public enum TPM : uint - { - LEFTBUTTON = 0x0000, - RIGHTBUTTON = 0x0002, - LEFTALIGN = 0x0000, - CENTERALIGN = 0x0004, - RIGHTALIGN = 0x0008, - TOPALIGN = 0x0000, - VCENTERALIGN = 0x0010, - BOTTOMALIGN = 0x0020, - HORIZONTAL = 0x0000, - VERTICAL = 0x0040, - NONOTIFY = 0x0080, - RETURNCMD = 0x0100, - RECURSE = 0x0001, // value missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3461 - HORPOSANIMATION = 0x0400, - HORNEGANIMATION = 0x0800, - VERPOSANIMATION = 0x1000, - VERNEGANIMATION = 0x2000, - NOANIMATION = 0x4000, - LAYOUTRTL = 0x8000, // value also missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3456 - } - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern IntPtr CreatePopupMenu(); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern bool DeleteFileW(string lpFileName); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DestroyMenu(IntPtr hMenu); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern unsafe int FormatMessageW(int flags, IntPtr source, uint messageId, uint languageId, char* outMsg, int size, IntPtr args); - - public static uint GetLastError() - => unchecked((uint) Marshal.GetLastWin32Error()); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern bool PathRelativePathToW([Out] char[] pszPath, [In] string pszFrom, [In] FileAttributes dwAttrFrom, [In] string pszTo, [In] FileAttributes dwAttrTo); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern bool SystemParametersInfoW(int uAction, int uParam, ref int lpvParam, int flags); - - [DllImport("winmm.dll", ExactSpelling = true)] - public static extern uint timeBeginPeriod(uint uMilliseconds); - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern int TrackPopupMenuEx(IntPtr hmenu, TPM fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm); - - [DllImport("kernel32.dll", ExactSpelling = true)] - public static extern IntPtr GetCurrentProcess(); - - [DllImport("kernel32.dll", ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool Wow64Process); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SetDllDirectoryW(string lpPathName); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern int GetShortPathNameW(string lpszLongPath, char[] lpszShortPath, int cchBuffer); - } -} \ No newline at end of file diff --git a/src/BizHawk.Common/Win32/Win32ShellContextMenu.cs b/src/BizHawk.Common/Win32/Win32ShellContextMenu.cs index 18c0183a918..1f0c625b909 100644 --- a/src/BizHawk.Common/Win32/Win32ShellContextMenu.cs +++ b/src/BizHawk.Common/Win32/Win32ShellContextMenu.cs @@ -1,168 +1,25 @@ -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// ReSharper disable UnusedMember.Local +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.Shell.Common; +using Windows.Win32.UI.WindowsAndMessaging; namespace BizHawk.Common { public unsafe class Win32ShellContextMenu : IDisposable { - [StructLayout(LayoutKind.Sequential)] - private struct IShellItem - { - public static readonly Guid Guid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - [StructLayout(LayoutKind.Sequential)] - public struct IShellItemVtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IShellItem functions - public delegate* unmanaged[Stdcall] BindToHandler; - public delegate* unmanaged[Stdcall] GetParent; - public delegate* unmanaged[Stdcall] GetDisplayName; - public delegate* unmanaged[Stdcall] GetAttributes; - public delegate* unmanaged[Stdcall] Compare; - } - - public IShellItemVtbl* lpVtbl; - - public void BindToHandler(IntPtr pbc, Guid bhid, Guid riid, out IntPtr ppv) - { - var hr = lpVtbl->BindToHandler((IShellItem*)Unsafe.AsPointer(ref this), pbc, in bhid, in riid, out ppv); - Marshal.ThrowExceptionForHR(hr); - } - - public void GetParent(out IShellItem* ppsi) - { - var hr = lpVtbl->GetParent((IShellItem*)Unsafe.AsPointer(ref this), out ppsi); - Marshal.ThrowExceptionForHR(hr); - } - } + private IContextMenu? CMI; + private IContextMenu2? CM2I; - [StructLayout(LayoutKind.Sequential)] - private struct IShellFolder - { - public static readonly Guid Guid = new("000214E6-0000-0000-C000-000000000046"); + private static readonly Guid GUID_ICONTEXTMENU = new("000214E4-0000-0000-C000-000000000046"); - [StructLayout(LayoutKind.Sequential)] - public struct IShellFolderVtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IShellFolder functions - public delegate* unmanaged[Stdcall] ParseDisplayName; - public delegate* unmanaged[Stdcall] EnumObjects; - public delegate* unmanaged[Stdcall] BindToObject; - public delegate* unmanaged[Stdcall] BindToStorage; - public delegate* unmanaged[Stdcall] CompareIDs; - public delegate* unmanaged[Stdcall] CreateViewObject; - public delegate* unmanaged[Stdcall] GetAttributesOf; - public delegate* unmanaged[Stdcall] GetUIObjectOf; - public delegate* unmanaged[Stdcall] GetDisplayNameOf; - public delegate* unmanaged[Stdcall] SetNameOf; - } + private static readonly Guid GUID_ICONTEXTMENU2 = new("000214F4-0000-0000-C000-000000000046"); - public IShellFolderVtbl* lpVtbl; + private static readonly Guid GUID_ISHELLFOLDER = new("000214E6-0000-0000-C000-000000000046"); - public void GetUIObjectOf(IntPtr hwndOwner, uint cidl, IntPtr* apidl, Guid riid, out IntPtr ppv) - { - var hr = lpVtbl->GetUIObjectOf((IShellFolder*)Unsafe.AsPointer(ref this), hwndOwner, cidl, apidl, in riid, null, out ppv); - Marshal.ThrowExceptionForHR(hr); - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct IContextMenu - { - public static readonly Guid Guid = new("000214e4-0000-0000-c000-000000000046"); - - [Flags] - public enum CMF : uint - { - NORMAL = 0x00000000, - DEFAULTONLY = 0x00000001, - VERBSONLY = 0x00000002, - EXPLORE = 0x00000004, - NOVERBS = 0x00000008, - CANRENAME = 0x00000010, - NODEFAULT = 0x00000020, - INCLUDESTATIC = 0x00000040, - EXTENDEDVERBS = 0x00000100, - RESERVED = 0xffff0000, - } - - [StructLayout(LayoutKind.Sequential)] - public struct CMINVOKECOMMANDINFO - { - public uint cbSize; - public uint fMask; - public IntPtr hwnd; - public IntPtr lpVerb; - public IntPtr lpParameters; - public IntPtr lpDirectory; - public int nShow; - public uint dwHotKey; - public IntPtr hIcon; - } - - [StructLayout(LayoutKind.Sequential)] - public struct IContextMenuVtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IContextMenu functions - public delegate* unmanaged[Stdcall] QueryContextMenu; - public delegate* unmanaged[Stdcall] InvokeCommand; - public delegate* unmanaged[Stdcall] GetCommandString; - } - - public IContextMenuVtbl* lpVtbl; - - public void QueryContextMenu(IntPtr hmenu, uint indexMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags) - { - var hr = lpVtbl->QueryContextMenu((IContextMenu*)Unsafe.AsPointer(ref this), hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); - Marshal.ThrowExceptionForHR(hr); - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct IContextMenu2 - { - public static readonly Guid Guid = new("000214f4-0000-0000-c000-000000000046"); - - [StructLayout(LayoutKind.Sequential)] - public struct IContextMenu2Vtbl - { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IContextMenu functions - public delegate* unmanaged[Stdcall] QueryContextMenu; - public delegate* unmanaged[Stdcall] InvokeCommand; - public delegate* unmanaged[Stdcall] GetCommandString; - // IContextMenu2 functions - public delegate* unmanaged[Stdcall] HandleMenuMsg; - } - - public IContextMenu2Vtbl* lpVtbl; - - public void InvokeCommand(IContextMenu.CMINVOKECOMMANDINFO* pici) - { - var hr = lpVtbl->InvokeCommand((IContextMenu2*)Unsafe.AsPointer(ref this), pici); - Marshal.ThrowExceptionForHR(hr); - } - } - - private IContextMenu* CMI; - private IContextMenu2* CM2I; + private static readonly Guid GUID_ISHELLITEM = new("43826D1E-E718-42EE-BC55-A1E261C37BFE"); private static readonly Guid SFObject = new("3981e224-f559-11d3-8e3a-00c04f6837d5"); @@ -176,56 +33,57 @@ private Win32ShellContextMenu(string path) throw new NotSupportedException("Non-file Uri schemes are unsupported"); } - var hr = Shell32Imports.SHCreateItemFromParsingName(uri.LocalPath, IntPtr.Zero, IShellItem.Guid, out var psi); + var hr = Shell32Imports.SHCreateItemFromParsingName(uri.LocalPath, default, in GUID_ISHELLITEM, out var psi); Marshal.ThrowExceptionForHR(hr); - var sii = (IShellItem*)psi; + var sii = (IShellItem) psi; - IntPtr pidls; - IShellItem* psii; + ITEMIDLIST* pidls; + IShellItem psii; try { hr = Shell32Imports.SHGetIDListFromObject(psi, out var ppidl); Marshal.ThrowExceptionForHR(hr); pidls = Shell32Imports.ILFindLastID(ppidl); - sii->GetParent(out psii); + sii.GetParent(out psii); } finally { - sii->lpVtbl->Release(sii); +// sii.Release(sii); } - IShellFolder* sfi; + IShellFolder sfi; try { - psii->BindToHandler(IntPtr.Zero, SFObject, IShellFolder.Guid, out var psf); - sfi = (IShellFolder*)psf; + psii.BindToHandler(default, in SFObject, in GUID_ISHELLFOLDER, out var psf); + sfi = (IShellFolder) psf; } finally { - psii->lpVtbl->Release(psii); +// psii.Release(psii); } - IContextMenu* cmi; + IContextMenu cmi; try { - sfi->GetUIObjectOf(IntPtr.Zero, 1, &pidls, IContextMenu.Guid, out var pcm); - cmi = (IContextMenu*)pcm; + sfi.GetUIObjectOf(HWND.Null, cidl: 1, &pidls, in GUID_ICONTEXTMENU, out var pcm); + cmi = (IContextMenu) pcm; } finally { - sfi->lpVtbl->Release(sfi); +// sfi.Release(sfi); } - IContextMenu2* cm2i; + IContextMenu2 cm2i; try { - cmi->lpVtbl->QueryInterface(cmi, in IContextMenu2.Guid, out var pcm2); - cm2i = (IContextMenu2*)pcm2; +// cmi.QueryInterface(cmi, in GUID_ICONTEXTMENU2, out var pcm2); + var pcm2 = cmi; + cm2i = (IContextMenu2) pcm2; } catch { - cmi->lpVtbl->Release(cmi); +// cmi.Release(cmi); throw; } @@ -237,25 +95,25 @@ public void Dispose() { if (CM2I != null) { - CM2I->lpVtbl->Release(CM2I); +// CM2I.Release(CM2I); CM2I = null; } if (CMI != null) { - CMI->lpVtbl->Release(CMI); +// CMI.Release(CMI); CMI = null; } } private ref struct TempMenu { - public IntPtr Handle { get; private set; } + public HMENU Handle { get; private set; } public TempMenu() { Handle = Win32Imports.CreatePopupMenu(); - if (Handle == IntPtr.Zero) + if (Handle.IsNull) { throw new InvalidOperationException($"{nameof(Win32Imports.CreatePopupMenu)} returned NULL!"); } @@ -263,10 +121,10 @@ public TempMenu() public void Dispose() { - if (Handle != IntPtr.Zero) + if (!Handle.IsNull) { _ = Win32Imports.DestroyMenu(Handle); - Handle = IntPtr.Zero; + Handle = HMENU.Null; } } } @@ -277,17 +135,23 @@ public static void ShowContextMenu(string path, IntPtr parentWindow, int x, int using var menu = new TempMenu(); const int CmdFirst = 0x8000; - ctxMenu.CMI->QueryContextMenu(menu.Handle, 0, CmdFirst, uint.MaxValue, IContextMenu.CMF.EXPLORE); - - var command = Win32Imports.TrackPopupMenuEx(menu.Handle, Win32Imports.TPM.RETURNCMD, x, y, parentWindow, IntPtr.Zero); + ctxMenu.CMI!.QueryContextMenu(menu.Handle, 0, CmdFirst, uint.MaxValue, Win32Imports.CMF_EXPLORE); + + var command = Win32Imports.TrackPopupMenuEx( + menu.Handle, + TPMFLAGS.RETURNCMD, + x: x, + y: y, + new(parentWindow), + lptpm: default).Value; if (command > 0) { const int SW_SHOWNORMAL = 1; - IContextMenu.CMINVOKECOMMANDINFO invoke = default; - invoke.cbSize = (uint)Marshal.SizeOf(); - invoke.lpVerb = new(command - CmdFirst); + CMINVOKECOMMANDINFO invoke = default; + invoke.cbSize = (uint)Marshal.SizeOf(); + invoke.lpVerb = new(unchecked((byte*) (IntPtr) (command - CmdFirst))); invoke.nShow = SW_SHOWNORMAL; - ctxMenu.CM2I->InvokeCommand(&invoke); + ctxMenu.CM2I!.InvokeCommand(&invoke); } } } diff --git a/src/BizHawk.Common/Win32/WmImports.cs b/src/BizHawk.Common/Win32/WmImports.cs index 9ddb1f45898..41245704b39 100644 --- a/src/BizHawk.Common/Win32/WmImports.cs +++ b/src/BizHawk.Common/Win32/WmImports.cs @@ -1,99 +1,15 @@ -#nullable disable +using System.Runtime.InteropServices; -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global +using Windows.Win32.UI.WindowsAndMessaging; namespace BizHawk.Common { - public static class WmImports + public static class WmImports1 { - public const uint PM_REMOVE = 0x0001U; - public static readonly IntPtr HWND_MESSAGE = new(-3); - public const int GWLP_USERDATA = -21; - - public enum ChangeWindowMessageFilterFlags : uint - { - Add = 1, - Remove = 2, - } - - [StructLayout(LayoutKind.Sequential)] - public struct MSG - { - public IntPtr hwnd; - public uint message; - public IntPtr wParam; - public IntPtr lParam; - public uint time; - public int x; - public int y; - public uint lPrivate; - } - - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - public delegate IntPtr WNDPROC(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WNDCLASSW - { - public uint style; - public WNDPROC lpfnWndProc; - public int cbClsExtra; - public int cbWndExtra; - public IntPtr hInstance; - public IntPtr hIcon; - public IntPtr hCursor; - public IntPtr hbrBackground; - public string lpszMenuName; - public string lpszClassName; - } - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr CreateWindowExW(int dwExStyle, IntPtr lpClassName, string lpWindowName, - int dwStyle, int X, int Y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr DefWindowProcW(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DestroyWindow(IntPtr hWnd); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr DispatchMessageW([In] ref MSG lpMsg); - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern IntPtr GetActiveWindow(); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetWindowLongPtrW(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll", ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool HideCaret(IntPtr hWnd); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool PeekMessageW(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr PostMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr RegisterClassW([In] ref WNDCLASSW lpWndClass); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr SendMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); - [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr SetWindowLongPtrW(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + public static extern IntPtr GetWindowLongPtrW(IntPtr hWnd, WINDOW_LONG_PTR_INDEX nIndex); [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool TranslateMessage([In] ref MSG lpMsg); + public static extern IntPtr SetWindowLongPtrW(IntPtr hWnd, WINDOW_LONG_PTR_INDEX nIndex, IntPtr dwNewLong); } } diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs new file mode 100644 index 00000000000..89f797fd128 --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Windows.Win32 +{ + public static partial class Win32Imports + { + /// alias for , per PInvoke003/ + public static uint GetLastError() + => unchecked((uint) Marshal.GetLastWin32Error()); + } +} diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs new file mode 100644 index 00000000000..9b8d8eb573e --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs @@ -0,0 +1,56 @@ +using System.Runtime.InteropServices; + +using Windows.Win32.Foundation; +using Windows.Win32.System.Memory; + +namespace Windows.Win32 +{ + public static partial class Win32Imports + { + /// + public static unsafe uint GetShortPathNameW(string lpszLongPath) + { + fixed (char* lpszLongPathLocal = lpszLongPath) + { + return Win32Imports.GetShortPathNameW(lpszLongPathLocal, default, 0U); + } + } + + /// + public static unsafe IntPtr HeapAlloc(int dwBytes, HEAP_FLAGS dwFlags = HEAP_FLAGS.HEAP_NONE) + => unchecked((IntPtr) HeapAlloc(GetProcessHeap_SafeHandle(), dwFlags, dwBytes: (UIntPtr) dwBytes)); + + /// + public static unsafe BOOL IsWow64Process(HANDLE hProcess, out BOOL Wow64Process) + { + fixed (BOOL* ptr = &Wow64Process) return IsWow64Process(hProcess, ptr); + } + + /// + public static unsafe UIntPtr VirtualAlloc( + UIntPtr lpAddress, + UIntPtr dwSize, + VIRTUAL_ALLOCATION_TYPE flAllocationType, + PAGE_PROTECTION_FLAGS flProtect) + => unchecked((UIntPtr) VirtualAlloc( + lpAddress: (void*) lpAddress, + dwSize: dwSize, + flAllocationType, + flProtect)); + + /// + public static unsafe BOOL VirtualFree( + UIntPtr lpAddress, + UIntPtr dwSize, + VIRTUAL_FREE_TYPE dwFreeType) + => unchecked(VirtualFree((void*) lpAddress, (nuint) dwSize, dwFreeType)); + + /// + public static unsafe BOOL VirtualProtect( + UIntPtr lpAddress, + UIntPtr dwSize, + PAGE_PROTECTION_FLAGS flNewProtect, + out PAGE_PROTECTION_FLAGS lpflOldProtect) + => unchecked(VirtualProtect((void*) lpAddress, (nuint) dwSize, flNewProtect, out lpflOldProtect)); + } +} diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs new file mode 100644 index 00000000000..cfe28f2b39a --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs @@ -0,0 +1,23 @@ +using System.IO; + +using Windows.Win32.Foundation; + +namespace Windows.Win32 +{ + public static partial class Win32Imports + { + /// + public static BOOL PathRelativePathToW( + Span pszPath, + string pszFrom, + FileAttributes dwAttrFrom, + string pszTo, + FileAttributes dwAttrTo) + => PathRelativePathToW( + pszPath: pszPath, + pszFrom: pszFrom, + dwAttrFrom: unchecked((uint) dwAttrFrom), + pszTo: pszTo, + dwAttrTo: unchecked((uint) dwAttrTo)); + } +} diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs new file mode 100644 index 00000000000..0da007d5a68 --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs @@ -0,0 +1,19 @@ +using Windows.Win32.Storage.FileSystem; +using Windows.Win32.UI.Shell; + +namespace Windows.Win32 +{ + public static class ShellLinkExtensions + { + /// + public static string GetPath(this IShellLinkW shellLink) + { + Span buf = stackalloc char[(int) Win32Imports.MAX_PATH + 1]; + WIN32_FIND_DATAW pfd = default; + shellLink.GetPath(buf, ref pfd, fFlags: default); + var i = buf.IndexOf((char) 0); + if (i >= 0) buf = buf.Slice(start: 0, length: i); + return buf.ToString(); + } + } +} diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs new file mode 100644 index 00000000000..6dc1258c16b --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs @@ -0,0 +1,37 @@ +using Windows.Win32.Foundation; + +namespace Windows.Win32.UI.WindowsAndMessaging +{ + /// + /// + [Flags] + public enum TPMFLAGS : uint + { + CENTERALIGN = 0x0004U, + LEFTALIGN = 0x0000U, + RIGHTALIGN = 0x0008U, + + BOTTOMALIGN = 0x0020U, + TOPALIGN = 0x0000U, + VCENTERALIGN = 0x0010U, + + NONOTIFY = 0x0080U, + RETURNCMD = 0x0100U, + + LEFTBUTTON = 0x0000U, + RIGHTBUTTON = 0x0002U, + + HORNEGANIMATION = 0x0800U, + HORPOSANIMATION = 0x0400U, + NOANIMATION = 0x4000U, + VERNEGANIMATION = 0x2000U, + VERPOSANIMATION = 0x1000U, + + RECURSE = 0x0001U, // value missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3461 + + HORIZONTAL = 0x0000U, + VERTICAL = 0x0040U, + + LAYOUTRTL = 0x8000U, // value also missing from official docs, but confirmed by https://github.com/microsoft/windows-rs/blob/bb15076311bf185400ecd244d47596b8415450fa/crates/libs/sys/src/Windows/Win32/UI/WindowsAndMessaging/mod.rs#L3456 + } +} diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs b/src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs new file mode 100644 index 00000000000..1aeb156b789 --- /dev/null +++ b/src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs @@ -0,0 +1,59 @@ +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Windows.Win32 +{ + public static partial class Win32Imports + { + /// + /// + public static unsafe PCWSTR MAKEINTATOM(uint i) + => new(unchecked((char*) i)); + + /// + public static unsafe LRESULT SendMessageW(HWND hWnd, uint Msg, IntPtr wParam, ref T lParam) + where T : unmanaged + { + fixed (void* ptr = &lParam) + { + return SendMessageW( + hWnd, + Msg, + new(unchecked((UIntPtr) wParam.ToPointer())), + new(unchecked((IntPtr) ptr))); + } + } + + /// getter + /// + public static unsafe BOOL SystemParametersInfoW( + SYSTEM_PARAMETERS_INFO_ACTION uiAction, + uint uiParam, + out nint pvParam) + { + const SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS NONE = 0; + fixed (void* ptr = &pvParam) return SystemParametersInfoW(uiAction, uiParam, ptr, NONE); + } + + /// setter + /// + public static unsafe BOOL SystemParametersInfoW( + SYSTEM_PARAMETERS_INFO_ACTION uiAction, + uint uiParam, + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS fWinIni) + { + nint pvParam = default; + return SystemParametersInfoW(uiAction, uiParam, &pvParam, fWinIni); + } + + /// + public static unsafe BOOL TrackPopupMenuEx( + HMENU hMenu, + TPMFLAGS uFlags, + int x, + int y, + HWND hwnd, + TPMPARAMS* lptpm) + => TrackPopupMenuEx(hMenu, uFlags: unchecked((uint) uFlags), x: x, y: y, hwnd, lptpm); + } +} diff --git a/src/MainSlnCommon.props b/src/MainSlnCommon.props index 1ea37b15590..73dc582f8e2 100644 --- a/src/MainSlnCommon.props +++ b/src/MainSlnCommon.props @@ -23,6 +23,9 @@ + + + From efd1f48a3118f8df89717b34d84eabc2b27a31d0 Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Wed, 21 May 2025 22:36:57 +1000 Subject: [PATCH 3/6] Split off Bizware.Externs project from BizHawk.Common --- .../BizHawk.Bizware.Externs.csproj | 11 +++++++++++ .../GetLastError.cs} | 0 .../KERNEL32.dll.cs} | 0 .../NativeMethods.json | 0 .../NativeMethods.txt | 0 .../SHLWAPI.dll.cs} | 0 .../TPMFLAGS.cs} | 0 .../USER32.dll.cs} | 0 src/BizHawk.Common/BizHawk.Common.csproj | 5 +---- ....ShellLinkExtensions.cs => ShellLinkExtensions.cs} | 0 10 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 src/BizHawk.Bizware.Externs/BizHawk.Bizware.Externs.csproj rename src/{BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs => BizHawk.Bizware.Externs/GetLastError.cs} (100%) rename src/{BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs => BizHawk.Bizware.Externs/KERNEL32.dll.cs} (100%) rename src/{BizHawk.Common => BizHawk.Bizware.Externs}/NativeMethods.json (100%) rename src/{BizHawk.Common => BizHawk.Bizware.Externs}/NativeMethods.txt (100%) rename src/{BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs => BizHawk.Bizware.Externs/SHLWAPI.dll.cs} (100%) rename src/{BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs => BizHawk.Bizware.Externs/TPMFLAGS.cs} (100%) rename src/{BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs => BizHawk.Bizware.Externs/USER32.dll.cs} (100%) rename src/BizHawk.Common/{Windows.Win32.Win32Imports.ShellLinkExtensions.cs => ShellLinkExtensions.cs} (100%) diff --git a/src/BizHawk.Bizware.Externs/BizHawk.Bizware.Externs.csproj b/src/BizHawk.Bizware.Externs/BizHawk.Bizware.Externs.csproj new file mode 100644 index 00000000000..b01d74fa477 --- /dev/null +++ b/src/BizHawk.Bizware.Externs/BizHawk.Bizware.Externs.csproj @@ -0,0 +1,11 @@ + + + netstandard2.0 + + + + + + + + diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs b/src/BizHawk.Bizware.Externs/GetLastError.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.GetLastError.cs rename to src/BizHawk.Bizware.Externs/GetLastError.cs diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs b/src/BizHawk.Bizware.Externs/KERNEL32.dll.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.KERNEL32.dll.cs rename to src/BizHawk.Bizware.Externs/KERNEL32.dll.cs diff --git a/src/BizHawk.Common/NativeMethods.json b/src/BizHawk.Bizware.Externs/NativeMethods.json similarity index 100% rename from src/BizHawk.Common/NativeMethods.json rename to src/BizHawk.Bizware.Externs/NativeMethods.json diff --git a/src/BizHawk.Common/NativeMethods.txt b/src/BizHawk.Bizware.Externs/NativeMethods.txt similarity index 100% rename from src/BizHawk.Common/NativeMethods.txt rename to src/BizHawk.Bizware.Externs/NativeMethods.txt diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs b/src/BizHawk.Bizware.Externs/SHLWAPI.dll.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.SHLWAPI.dll.cs rename to src/BizHawk.Bizware.Externs/SHLWAPI.dll.cs diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs b/src/BizHawk.Bizware.Externs/TPMFLAGS.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.TPMFLAGS.cs rename to src/BizHawk.Bizware.Externs/TPMFLAGS.cs diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs b/src/BizHawk.Bizware.Externs/USER32.dll.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.USER32.dll.cs rename to src/BizHawk.Bizware.Externs/USER32.dll.cs diff --git a/src/BizHawk.Common/BizHawk.Common.csproj b/src/BizHawk.Common/BizHawk.Common.csproj index cde4fc8a2cd..f2980ce426a 100644 --- a/src/BizHawk.Common/BizHawk.Common.csproj +++ b/src/BizHawk.Common/BizHawk.Common.csproj @@ -5,18 +5,15 @@ true - System.Diagnostics.CodeAnalysis.UnscopedRefAttribute true - - - + diff --git a/src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs b/src/BizHawk.Common/ShellLinkExtensions.cs similarity index 100% rename from src/BizHawk.Common/Windows.Win32.Win32Imports.ShellLinkExtensions.cs rename to src/BizHawk.Common/ShellLinkExtensions.cs From cc7ff7bb1f4d89a421ccd53193e847df10f5a738 Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Fri, 23 May 2025 09:36:17 +1000 Subject: [PATCH 4/6] WIP AVIWriterImports --- Common.props | 2 +- src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs | 17 ++ src/BizHawk.Bizware.Externs/NativeMethods.txt | 13 ++ src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs | 162 +++++++++++------- src/BizHawk.Common/Win32/AVIWriterImports.cs | 38 ---- 5 files changed, 131 insertions(+), 101 deletions(-) create mode 100644 src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs diff --git a/Common.props b/Common.props index 1ea6589d6cd..1c7e41a7422 100644 --- a/Common.props +++ b/Common.props @@ -6,7 +6,7 @@ Recommended Recommended Recommended - false + true true true true diff --git a/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs b/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs new file mode 100644 index 00000000000..a0fb2b06fea --- /dev/null +++ b/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs @@ -0,0 +1,17 @@ +using Windows.Win32.Foundation; +using Windows.Win32.Media.Multimedia; + +namespace Windows.Win32 +{ + public static partial class Win32Imports + { + /// + public static unsafe nint AVISaveOptions(IAVIStream[] ppavi, ref AVICOMPRESSOPTIONS opts, HWND owner) + { + fixed (AVICOMPRESSOPTIONS* popts = &opts) + { + return AVISaveOptions(owner, uiFlags: 0, nStreams: 1, ppavi, &popts); + } + } + } +} diff --git a/src/BizHawk.Bizware.Externs/NativeMethods.txt b/src/BizHawk.Bizware.Externs/NativeMethods.txt index cb3fa5abe44..9bfdf562fec 100644 --- a/src/BizHawk.Bizware.Externs/NativeMethods.txt +++ b/src/BizHawk.Bizware.Externs/NativeMethods.txt @@ -1,9 +1,21 @@ +AVIFileCreateStreamW +AVIFileInit +AVIFileOpenW +AVIFileRelease +AVIIF_KEYFRAME +AVIMakeCompressedStream +AVISaveOptions +AVIStreamRelease +AVIStreamSetFormat +AVIStreamSetFormat +AVIStreamWrite BFFM_INITIALIZED BFFM_SETSELECTIONW BIF_DONTGOBELOWDOMAIN BIF_EDITBOX BIF_NEWDIALOGSTYLE BIF_RETURNONLYFSDIRS +BITMAPINFOHEADER BROWSEINFOW ChangeWindowMessageFilter CMF_EXPLORE @@ -66,5 +78,6 @@ TranslateMessage VirtualAlloc VirtualFree VirtualProtect +WAVEFORMATEX WINDOW_LONG_PTR_INDEX WM_SETREDRAW diff --git a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs index 73a71bbaf74..fb69bbc3080 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs @@ -12,13 +12,18 @@ using BizHawk.Emulation.Common; using Windows.Win32; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.Media.Audio; +using Windows.Win32.Media.Multimedia; + +using AVIWriterImports = Windows.Win32.Win32Imports; // some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967 namespace BizHawk.Client.EmuHawk { [VideoWriter("vfwavi", "AVI writer", "Uses the Microsoft AVIFIL32 system to write .avi files. Audio is uncompressed; Video can be compressed with any installed VCM codec. Splits on 2G and resolution change.")] - internal class AviWriter : IVideoWriter + internal unsafe class AviWriter : IVideoWriter { private CodecToken _currVideoCodecToken; private AviWriterSegment _currSegment; @@ -292,9 +297,9 @@ private class Parameters public int width, height; public int pitch; //in bytes public int pitch_add; - public void PopulateBITMAPINFOHEADER24(ref AVIWriterImports.BITMAPINFOHEADER bmih) + public void PopulateBITMAPINFOHEADER24(ref BITMAPINFOHEADER bmih) { - bmih.Init(); + bmih.biSize = unchecked((uint) Marshal.SizeOf(bmih)); bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biHeight = height; @@ -307,9 +312,9 @@ public void PopulateBITMAPINFOHEADER24(ref AVIWriterImports.BITMAPINFOHEADER bmi bmih.biSizeImage = (uint)(pitch * height); } - public void PopulateBITMAPINFOHEADER32(ref AVIWriterImports.BITMAPINFOHEADER bmih) + public void PopulateBITMAPINFOHEADER32(ref BITMAPINFOHEADER bmih) { - bmih.Init(); + bmih.biSize = unchecked((uint) Marshal.SizeOf(bmih)); bmih.biPlanes = 1; bmih.biBitCount = 32; pitch = width * 4; @@ -322,7 +327,7 @@ public void PopulateBITMAPINFOHEADER32(ref AVIWriterImports.BITMAPINFOHEADER bmi public int a_samplerate, a_channels, a_bits; /// is not 8 or 16, or is not in range 1..2 - public void PopulateWAVEFORMATEX(ref AVIWriterImports.WAVEFORMATEX wfex) + public void PopulateWAVEFORMATEX(ref WAVEFORMATEX wfex) { const int WAVE_FORMAT_PCM = 1; var bytes = a_bits switch @@ -337,7 +342,7 @@ public void PopulateWAVEFORMATEX(ref AVIWriterImports.WAVEFORMATEX wfex) throw new InvalidOperationException($"only 1/2 channels audio are supported by {nameof(AviWriter)} and you chose: {a_channels}"); } - wfex.Init(); + wfex.cbSize = unchecked((ushort) Marshal.SizeOf(wfex)); wfex.nBlockAlign = (ushort)(bytes * a_channels); wfex.nChannels = (ushort)a_channels; wfex.wBitsPerSample = (ushort)a_bits; @@ -408,7 +413,7 @@ private CodecToken() { } - private AVIWriterImports.AVICOMPRESSOPTIONS _comprOptions; + private AVICOMPRESSOPTIONS _comprOptions; public string codec; public byte[] Format = Array.Empty(); public byte[] Parms = Array.Empty(); @@ -427,22 +432,22 @@ private static unsafe string Decode_mmioFOURCC(int code) return new(chs, 0, 4); } - public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts) + public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVICOMPRESSOPTIONS opts) { var ret = new CodecToken { _comprOptions = opts, - codec = Decode_mmioFOURCC(opts.fccHandler), + codec = unchecked((int) Decode_mmioFOURCC(opts.fccHandler)), Format = new byte[opts.cbFormat], Parms = new byte[opts.cbParms], }; - if (opts.lpFormat != IntPtr.Zero) + if (opts.lpFormat is not null) { Marshal.Copy(opts.lpFormat, ret.Format, 0, opts.cbFormat); } - if (opts.lpParms != IntPtr.Zero) + if (opts.lpParms is not null) { Marshal.Copy(opts.lpParms, ret.Parms, 0, opts.cbParms); } @@ -450,26 +455,26 @@ public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICO return ret; } - public static void DeallocateAVICOMPRESSOPTIONS(ref AVIWriterImports.AVICOMPRESSOPTIONS opts) + public static void DeallocateAVICOMPRESSOPTIONS(ref AVICOMPRESSOPTIONS opts) { #endif #if false // test: increase stability by never freeing anything, ever - if (opts.lpParms != IntPtr.Zero) + if (opts.lpParms is not null) { HeapFree(GetProcessHeap_SafeHandle(), 0, opts.lpParms); } - if (opts.lpFormat != IntPtr.Zero) + if (opts.lpFormat is not null) { HeapFree(GetProcessHeap_SafeHandle(), 0, opts.lpFormat); } #endif #if AVI_SUPPORT - opts.lpParms = IntPtr.Zero; - opts.lpFormat = IntPtr.Zero; + opts.lpParms = null; + opts.lpFormat = null; } - public void AllocateToAVICOMPRESSOPTIONS(out AVIWriterImports.AVICOMPRESSOPTIONS opts) + public void AllocateToAVICOMPRESSOPTIONS(out AVICOMPRESSOPTIONS opts) { if (_comprOptions.cbParms != 0) { @@ -513,7 +518,7 @@ private static CodecToken DeSerializeFromByteArray(byte[] data) var m = new MemoryStream(data, false); var b = new BinaryReader(m); - var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS); + AVICOMPRESSOPTIONS comprOptions = default; byte[] format; byte[] parms; @@ -521,17 +526,17 @@ private static CodecToken DeSerializeFromByteArray(byte[] data) try { - comprOptions.fccType = b.ReadInt32(); - comprOptions.fccHandler = b.ReadInt32(); - comprOptions.dwKeyFrameEvery = b.ReadInt32(); - comprOptions.dwQuality = b.ReadInt32(); - comprOptions.dwBytesPerSecond = b.ReadInt32(); - comprOptions.dwFlags = b.ReadInt32(); - //comprOptions.lpFormat = b.ReadInt32(); - comprOptions.cbFormat = b.ReadInt32(); - //comprOptions.lpParms = b.ReadInt32(); - comprOptions.cbParms = b.ReadInt32(); - comprOptions.dwInterleaveEvery = b.ReadInt32(); + comprOptions.fccType = b.ReadUInt32(); + comprOptions.fccHandler = b.ReadUInt32(); + comprOptions.dwKeyFrameEvery = b.ReadUInt32(); + comprOptions.dwQuality = b.ReadUInt32(); + comprOptions.dwBytesPerSecond = b.ReadUInt32(); + comprOptions.dwFlags = b.ReadUInt32(); + //comprOptions.lpFormat = b.ReadUInt32(); + comprOptions.cbFormat = b.ReadUInt32(); + //comprOptions.lpParms = b.ReadUInt32(); + comprOptions.cbParms = b.ReadUInt32(); + comprOptions.dwInterleaveEvery = b.ReadUInt32(); format = b.ReadBytes(comprOptions.cbFormat); parms = b.ReadBytes(comprOptions.cbParms); @@ -624,16 +629,8 @@ private class OutputStatus public long GetLengthApproximation() => _outStatus.video_bytes + _outStatus.audio_bytes; - private static unsafe int AVISaveOptions(IntPtr stream, ref AVIWriterImports.AVICOMPRESSOPTIONS opts, IntPtr owner) - { - fixed (AVIWriterImports.AVICOMPRESSOPTIONS* _popts = &opts) - { - var pStream = &stream; - var popts = _popts; - var ppopts = &popts; - return AVIWriterImports.AVISaveOptions(owner, 0, 1, pStream, ppopts); - } - } + public static unsafe int AVISaveOptions(IntPtr stream, ref AVICOMPRESSOPTIONS opts, IntPtr owner) + => unchecked((int) AVIWriterImports.AVISaveOptions((IAVIStream[]) (void*) stream, ref opts, new(owner))); private Parameters _parameters; @@ -658,8 +655,11 @@ static int mmioFOURCC(string str) => ( File.Delete(destPath); } - var hr = AVIWriterImports.AVIFileOpenW(ref _pAviFile, destPath, - AVIWriterImports.OpenFileStyle.OF_CREATE | AVIWriterImports.OpenFileStyle.OF_WRITE, 0); + var hr = AVIWriterImports.AVIFileOpenW( + ref _pAviFile, + destPath, + AVIWriterImports.OpenFileStyle.OF_CREATE | AVIWriterImports.OpenFileStyle.OF_WRITE, + 0); var hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -667,15 +667,18 @@ static int mmioFOURCC(string str) => ( } // initialize the video stream - var vidstream_header = default(AVIWriterImports.AVISTREAMINFOW); - var bmih = default(AVIWriterImports.BITMAPINFOHEADER); + AVISTREAMINFOW vidstream_header = default; + BITMAPINFOHEADER bmih = default; parameters.PopulateBITMAPINFOHEADER24(ref bmih); vidstream_header.fccType = mmioFOURCC("vids"); vidstream_header.dwRate = parameters.fps; vidstream_header.dwScale = parameters.fps_scale; vidstream_header.dwSuggestedBufferSize = (int)bmih.biSizeImage; - hr = AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawVideoStream, ref vidstream_header); + hr = AVIWriterImports.AVIFileCreateStreamW( + _pAviFile, + out _pAviRawVideoStream, + ref vidstream_header); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -684,8 +687,8 @@ static int mmioFOURCC(string str) => ( } // initialize audio stream - var audstream_header = default(AVIWriterImports.AVISTREAMINFOW); - var wfex = default(AVIWriterImports.WAVEFORMATEX); + AVISTREAMINFOW audstream_header = default; + WAVEFORMATEX wfex = default; parameters.PopulateWAVEFORMATEX(ref wfex); audstream_header.fccType = mmioFOURCC("auds"); audstream_header.dwQuality = -1; @@ -694,7 +697,10 @@ static int mmioFOURCC(string str) => ( audstream_header.dwSampleSize = wfex.nBlockAlign; audstream_header.dwInitialFrames = 1; // ??? optimal value? - hr = AVIWriterImports.AVIFileCreateStreamW(_pAviFile, out _pAviRawAudioStream, ref audstream_header); + hr = AVIWriterImports.AVIFileCreateStreamW( + _pAviFile, + out _pAviRawAudioStream, + ref audstream_header); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -722,7 +728,7 @@ public IDisposable AcquireVideoCodecToken(IntPtr hwnd, CodecToken lastCodecToken } // encoder params - var comprOptions = default(AVIWriterImports.AVICOMPRESSOPTIONS); + var comprOptions = default(AVICOMPRESSOPTIONS); _currVideoCodecToken?.AllocateToAVICOMPRESSOPTIONS(out comprOptions); var result = AVISaveOptions(_pAviRawVideoStream, ref comprOptions, hwnd) != 0; @@ -752,7 +758,11 @@ public void OpenStreams() // open compressed video stream _currVideoCodecToken.AllocateToAVICOMPRESSOPTIONS(out var opts); - var hr = AVIWriterImports.AVIMakeCompressedStream(out _pAviCompressedVideoStream, _pAviRawVideoStream, ref opts, IntPtr.Zero); + var hr = AVIWriterImports.AVIMakeCompressedStream( + out _pAviCompressedVideoStream, + _pAviRawVideoStream, + ref opts, + IntPtr.Zero); var hrEx = Marshal.GetExceptionForHR(hr); CodecToken.DeallocateAVICOMPRESSOPTIONS(ref opts); if (hrEx != null) @@ -762,7 +772,7 @@ public void OpenStreams() } // set the compressed video stream input format - var bmih = default(AVIWriterImports.BITMAPINFOHEADER); + var bmih = default(BITMAPINFOHEADER); if (_bit32) { _parameters.PopulateBITMAPINFOHEADER32(ref bmih); @@ -772,7 +782,11 @@ public void OpenStreams() _parameters.PopulateBITMAPINFOHEADER24(ref bmih); } - hr = AVIWriterImports.AVIStreamSetFormat(_pAviCompressedVideoStream, 0, ref bmih, Marshal.SizeOf(bmih)); + hr = AVIWriterImports.AVIStreamSetFormat( + _pAviCompressedVideoStream, + 0, + ref bmih, + Marshal.SizeOf(bmih)); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -782,10 +796,14 @@ public void OpenStreams() } // set audio stream input format - var wfex = default(AVIWriterImports.WAVEFORMATEX); + WAVEFORMATEX wfex = default; _parameters.PopulateWAVEFORMATEX(ref wfex); - hr = AVIWriterImports.AVIStreamSetFormat(_pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex)); + hr = AVIWriterImports.AVIStreamSetFormat( + _pAviRawAudioStream, + 0, + ref wfex, + Marshal.SizeOf(wfex)); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -880,8 +898,15 @@ private unsafe void FlushBufferedAudio() } // (TODO - inefficient- build directly in a buffer) - _ = AVIWriterImports.AVIStreamWrite(_pAviRawAudioStream, _outStatus.audio_samples, - todo_realsamples, buf, todo_realsamples * 4, 0, IntPtr.Zero, out var bytes_written); + _ = AVIWriterImports.AVIStreamWrite( + _pAviRawAudioStream, + _outStatus.audio_samples, + todo_realsamples, + buf, + todo_realsamples * 4, + 0, + IntPtr.Zero, + out var bytes_written); _outStatus.audio_samples += todo_realsamples; _outStatus.audio_bytes += bytes_written; _outStatus.audio_buffered_shorts = 0; @@ -890,8 +915,6 @@ private unsafe void FlushBufferedAudio() /// attempted frame resize during encoding public unsafe void AddFrame(IVideoProvider source) { - const int AVIIF_KEYFRAME = 0x00000010; - if (_parameters.width != source.BufferWidth || _parameters.height != source.BufferHeight) throw new InvalidOperationException("video buffer changed between start and now"); @@ -930,8 +953,16 @@ public unsafe void AddFrame(IVideoProvider source) bp += pitch_add; } - _ = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames, - 1, new(bytes_ptr), todo, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written); + int bytes_written = default; + _ = AVIWriterImports.AVIStreamWrite( + _pAviCompressedVideoStream, + lStart: _outStatus.video_frames, + samples: 1, + lpBuffer: bytes_ptr, + cbBuffer: todo, + dwFlags: AVIWriterImports.AVIIF_KEYFRAME, + plSampWritten: default, + plBytesWritten: &bytes_written); _outStatus.video_bytes += bytes_written; _outStatus.video_frames++; } @@ -963,8 +994,15 @@ public unsafe void AddFrame(IVideoProvider source) idx -= w * 2; } - _ = AVIWriterImports.AVIStreamWrite(_pAviCompressedVideoStream, _outStatus.video_frames, - 1, new(bytes_ptr), todo * 3, AVIIF_KEYFRAME, IntPtr.Zero, out var bytes_written); + _ = AVIWriterImports.AVIStreamWrite( + _pAviCompressedVideoStream, + lStart: _outStatus.video_frames, + samples: 1, + lpBuffer: bytes_ptr, + cbBuffer: todo * 3, + dwFlags: AVIWriterImports.AVIIF_KEYFRAME, + plSampWritten: default, + plBytesWritten: &bytes_written); _outStatus.video_bytes += bytes_written; _outStatus.video_frames++; } diff --git a/src/BizHawk.Common/Win32/AVIWriterImports.cs b/src/BizHawk.Common/Win32/AVIWriterImports.cs index 377e548fc2e..7a18946c27c 100644 --- a/src/BizHawk.Common/Win32/AVIWriterImports.cs +++ b/src/BizHawk.Common/Win32/AVIWriterImports.cs @@ -98,44 +98,6 @@ public struct WAVEFORMATEX public void Init() => cbSize = (ushort)Marshal.SizeOf(this); } - - /// Create a new stream in an existing file and creates an interface to the new stream - [DllImport("avifil32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] - public static extern int AVIFileCreateStreamW(IntPtr pfile, out IntPtr ppavi, ref AVISTREAMINFOW psi); - - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern void AVIFileInit(); - - [DllImport("avifil32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern int AVIFileOpenW(ref IntPtr pAviFile, [MarshalAs(UnmanagedType.LPWStr)] string szFile, OpenFileStyle uMode, int lpHandler); - - /// Release an open AVI stream - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIFileRelease(IntPtr pfile); - - /// Create a compressed stream from an uncompressed stream and a compression filter, and returns the address of a pointer to the compressed stream - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIMakeCompressedStream(out IntPtr ppsCompressed, IntPtr psSource, ref AVICOMPRESSOPTIONS lpOptions, IntPtr pclsidHandler); - - /// Retrieve the save options for a file and returns them in a buffer - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern unsafe int AVISaveOptions(IntPtr hwnd, int flags, int streams, void* ppAvi, void* plpOptions); - - /// - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIStreamRelease(IntPtr pavi); - - /// Set the format of a stream at the specified position - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIStreamSetFormat(IntPtr pavi, int lPos, ref BITMAPINFOHEADER lpFormat, int cbFormat); - - /// - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIStreamSetFormat(IntPtr pavi, int lPos, ref WAVEFORMATEX lpFormat, int cbFormat); - - /// Write data to a stream - [DllImport("avifil32.dll", ExactSpelling = true)] - public static extern int AVIStreamWrite(IntPtr pavi, int lStart, int lSamples, IntPtr lpBuffer, int cbBuffer, int dwFlags, IntPtr plSampWritten, out int plBytesWritten); } } #endif From b5ef422ab64c49b7d398e30e872ae4ed35991085 Mon Sep 17 00:00:00 2001 From: Morilli <35152647+Morilli@users.noreply.github.com> Date: Fri, 23 May 2025 22:33:10 +0200 Subject: [PATCH 5/6] add BizHawk.Bizware.Externs to solution --- BizHawk.sln | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BizHawk.sln b/BizHawk.sln index 2067752ffe5..550da2e24fd 100644 --- a/BizHawk.sln +++ b/BizHawk.sln @@ -37,6 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{74391239 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BizHawk.Tests", "src\BizHawk.Tests\BizHawk.Tests.csproj", "{284E19E2-661D-4A7D-864A-AC2FC91E7C25}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Bizware.Externs", "src\BizHawk.Bizware.Externs\BizHawk.Bizware.Externs.csproj", "{DCB48DE4-8D71-4F8B-ABE4-55E9B62FA511}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,6 +101,10 @@ Global {284E19E2-661D-4A7D-864A-AC2FC91E7C25}.Debug|Any CPU.Build.0 = Debug|Any CPU {284E19E2-661D-4A7D-864A-AC2FC91E7C25}.Release|Any CPU.ActiveCfg = Release|Any CPU {284E19E2-661D-4A7D-864A-AC2FC91E7C25}.Release|Any CPU.Build.0 = Release|Any CPU + {DCB48DE4-8D71-4F8B-ABE4-55E9B62FA511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DCB48DE4-8D71-4F8B-ABE4-55E9B62FA511}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DCB48DE4-8D71-4F8B-ABE4-55E9B62FA511}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DCB48DE4-8D71-4F8B-ABE4-55E9B62FA511}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 93562b86e780a7d12c2761082ea4032b3c477f20 Mon Sep 17 00:00:00 2001 From: Morilli <35152647+Morilli@users.noreply.github.com> Date: Fri, 23 May 2025 22:34:01 +0200 Subject: [PATCH 6/6] fix AviWriter --- src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs | 4 +- src/BizHawk.Bizware.Externs/KERNEL32.dll.cs | 4 +- src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs | 114 +++++++++--------- src/BizHawk.Common/Win32/AVIWriterImports.cs | 86 ------------- 4 files changed, 63 insertions(+), 145 deletions(-) diff --git a/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs b/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs index a0fb2b06fea..d0a0379b6a9 100644 --- a/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs +++ b/src/BizHawk.Bizware.Externs/AVIFIL32.dll.cs @@ -6,11 +6,11 @@ namespace Windows.Win32 public static partial class Win32Imports { /// - public static unsafe nint AVISaveOptions(IAVIStream[] ppavi, ref AVICOMPRESSOPTIONS opts, HWND owner) + public static unsafe nint AVISaveOptions(IAVIStream ppavi, ref AVICOMPRESSOPTIONS opts, HWND owner) { fixed (AVICOMPRESSOPTIONS* popts = &opts) { - return AVISaveOptions(owner, uiFlags: 0, nStreams: 1, ppavi, &popts); + return AVISaveOptions(owner, uiFlags: 0, nStreams: 1, [ ppavi ], &popts); } } } diff --git a/src/BizHawk.Bizware.Externs/KERNEL32.dll.cs b/src/BizHawk.Bizware.Externs/KERNEL32.dll.cs index 9b8d8eb573e..b5439a4228a 100644 --- a/src/BizHawk.Bizware.Externs/KERNEL32.dll.cs +++ b/src/BizHawk.Bizware.Externs/KERNEL32.dll.cs @@ -17,8 +17,8 @@ public static unsafe uint GetShortPathNameW(string lpszLongPath) } /// - public static unsafe IntPtr HeapAlloc(int dwBytes, HEAP_FLAGS dwFlags = HEAP_FLAGS.HEAP_NONE) - => unchecked((IntPtr) HeapAlloc(GetProcessHeap_SafeHandle(), dwFlags, dwBytes: (UIntPtr) dwBytes)); + public static unsafe void* HeapAlloc(int dwBytes, HEAP_FLAGS dwFlags = HEAP_FLAGS.HEAP_NONE) + => HeapAlloc(GetProcessHeap_SafeHandle(), dwFlags, dwBytes: (UIntPtr) dwBytes); /// public static unsafe BOOL IsWow64Process(HANDLE hProcess, out BOOL Wow64Process) diff --git a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs index fb69bbc3080..3d97806a79b 100644 --- a/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs +++ b/src/BizHawk.Client.EmuHawk/AVOut/AviWriter.cs @@ -7,11 +7,9 @@ using System.Threading; using BizHawk.Client.Common; -using BizHawk.Common; using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; -using Windows.Win32; using Windows.Win32.Graphics.Gdi; using Windows.Win32.Media.Audio; using Windows.Win32.Media.Multimedia; @@ -351,7 +349,8 @@ public void PopulateWAVEFORMATEX(ref WAVEFORMATEX wfex) wfex.nAvgBytesPerSec = (uint)(wfex.nBlockAlign * a_samplerate); } - public int fps, fps_scale; + public uint fps; + public uint fps_scale; } private readonly Parameters _parameters = new Parameters(); @@ -365,10 +364,10 @@ public void SetMovieParameters(int fpsNum, int fpsDen) bool change = false; change |= fpsNum != _parameters.fps; - _parameters.fps = fpsNum; + _parameters.fps = (uint)fpsNum; change |= _parameters.fps_scale != fpsDen; - _parameters.fps_scale = fpsDen; + _parameters.fps_scale = (uint)fpsDen; if (change) { @@ -418,7 +417,7 @@ private CodecToken() public byte[] Format = Array.Empty(); public byte[] Parms = Array.Empty(); - private static unsafe string Decode_mmioFOURCC(int code) + private static string Decode_mmioFOURCC(uint code) { var chs = stackalloc char[4]; @@ -437,19 +436,19 @@ public static CodecToken CreateFromAVICOMPRESSOPTIONS(ref AVICOMPRESSOPTIONS opt var ret = new CodecToken { _comprOptions = opts, - codec = unchecked((int) Decode_mmioFOURCC(opts.fccHandler)), + codec = Decode_mmioFOURCC(opts.fccHandler), Format = new byte[opts.cbFormat], Parms = new byte[opts.cbParms], }; if (opts.lpFormat is not null) { - Marshal.Copy(opts.lpFormat, ret.Format, 0, opts.cbFormat); + Marshal.Copy((IntPtr)opts.lpFormat, ret.Format, 0, unchecked((int)opts.cbFormat)); } if (opts.lpParms is not null) { - Marshal.Copy(opts.lpParms, ret.Parms, 0, opts.cbParms); + Marshal.Copy((IntPtr)opts.lpParms, ret.Parms, 0, unchecked((int)opts.cbParms)); } return ret; @@ -478,14 +477,14 @@ public void AllocateToAVICOMPRESSOPTIONS(out AVICOMPRESSOPTIONS opts) { if (_comprOptions.cbParms != 0) { - _comprOptions.lpParms = Win32Imports.HeapAlloc(_comprOptions.cbParms); - Marshal.Copy(Parms, 0, _comprOptions.lpParms, _comprOptions.cbParms); + _comprOptions.lpParms = AVIWriterImports.HeapAlloc(unchecked((int)_comprOptions.cbParms)); + Marshal.Copy(Parms, 0, (IntPtr)_comprOptions.lpParms, unchecked((int)_comprOptions.cbParms)); } if (_comprOptions.cbFormat != 0) { - _comprOptions.lpFormat = Win32Imports.HeapAlloc(_comprOptions.cbFormat); - Marshal.Copy(Format, 0, _comprOptions.lpFormat, _comprOptions.cbFormat); + _comprOptions.lpFormat = AVIWriterImports.HeapAlloc(unchecked((int)_comprOptions.cbFormat)); + Marshal.Copy(Format, 0, (IntPtr)_comprOptions.lpFormat, unchecked((int)_comprOptions.cbFormat)); } opts = _comprOptions; @@ -538,8 +537,8 @@ private static CodecToken DeSerializeFromByteArray(byte[] data) comprOptions.cbParms = b.ReadUInt32(); comprOptions.dwInterleaveEvery = b.ReadUInt32(); - format = b.ReadBytes(comprOptions.cbFormat); - parms = b.ReadBytes(comprOptions.cbParms); + format = b.ReadBytes(unchecked((int)comprOptions.cbFormat)); + parms = b.ReadBytes(unchecked((int)comprOptions.cbParms)); } catch (IOException) { @@ -579,7 +578,7 @@ private class AviWriterSegment : IDisposable { static AviWriterSegment() { - AVIWriterImports.AVIFileInit(); + MemoryApiImports.AVIFileInit(); } public void Dispose() @@ -587,7 +586,10 @@ public void Dispose() private CodecToken _currVideoCodecToken; private bool _isOpen; - private IntPtr _pAviFile, _pAviRawVideoStream, _pAviRawAudioStream, _pAviCompressedVideoStream; + private IAVIFile _pAviFile; + private IAVIStream _pAviRawVideoStream; + private IAVIStream _pAviRawAudioStream; + private IAVIStream _pAviCompressedVideoStream; private IntPtr _pGlobalBuf; private int _pGlobalBuffSize; @@ -629,15 +631,15 @@ private class OutputStatus public long GetLengthApproximation() => _outStatus.video_bytes + _outStatus.audio_bytes; - public static unsafe int AVISaveOptions(IntPtr stream, ref AVICOMPRESSOPTIONS opts, IntPtr owner) - => unchecked((int) AVIWriterImports.AVISaveOptions((IAVIStream[]) (void*) stream, ref opts, new(owner))); + public static int AVISaveOptions(IAVIStream stream, ref AVICOMPRESSOPTIONS opts, IntPtr owner) + => unchecked((int) AVIWriterImports.AVISaveOptions(stream, ref opts, new(owner))); private Parameters _parameters; /// unmanaged call failed public void OpenFile(string destPath, Parameters parameters, CodecToken videoCodecToken) { - static int mmioFOURCC(string str) => ( + static uint mmioFOURCC(string str) => (uint)( (byte)str[0] | ((byte)str[1] << 8) | ((byte)str[2] << 16) | @@ -656,10 +658,10 @@ static int mmioFOURCC(string str) => ( } var hr = AVIWriterImports.AVIFileOpenW( - ref _pAviFile, + out _pAviFile, destPath, - AVIWriterImports.OpenFileStyle.OF_CREATE | AVIWriterImports.OpenFileStyle.OF_WRITE, - 0); + (uint)(BizHawk.Common.AVIWriterImports.OpenFileStyle.OF_CREATE | BizHawk.Common.AVIWriterImports.OpenFileStyle.OF_WRITE), + null); var hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -673,12 +675,12 @@ static int mmioFOURCC(string str) => ( vidstream_header.fccType = mmioFOURCC("vids"); vidstream_header.dwRate = parameters.fps; vidstream_header.dwScale = parameters.fps_scale; - vidstream_header.dwSuggestedBufferSize = (int)bmih.biSizeImage; + vidstream_header.dwSuggestedBufferSize = bmih.biSizeImage; hr = AVIWriterImports.AVIFileCreateStreamW( _pAviFile, out _pAviRawVideoStream, - ref vidstream_header); + in vidstream_header); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -691,9 +693,9 @@ static int mmioFOURCC(string str) => ( WAVEFORMATEX wfex = default; parameters.PopulateWAVEFORMATEX(ref wfex); audstream_header.fccType = mmioFOURCC("auds"); - audstream_header.dwQuality = -1; + audstream_header.dwQuality = unchecked((uint)-1); audstream_header.dwScale = wfex.nBlockAlign; - audstream_header.dwRate = (int)wfex.nAvgBytesPerSec; + audstream_header.dwRate = wfex.nAvgBytesPerSec; audstream_header.dwSampleSize = wfex.nBlockAlign; audstream_header.dwInitialFrames = 1; // ??? optimal value? @@ -761,8 +763,8 @@ public void OpenStreams() var hr = AVIWriterImports.AVIMakeCompressedStream( out _pAviCompressedVideoStream, _pAviRawVideoStream, - ref opts, - IntPtr.Zero); + in opts, + null); var hrEx = Marshal.GetExceptionForHR(hr); CodecToken.DeallocateAVICOMPRESSOPTIONS(ref opts); if (hrEx != null) @@ -785,7 +787,7 @@ public void OpenStreams() hr = AVIWriterImports.AVIStreamSetFormat( _pAviCompressedVideoStream, 0, - ref bmih, + &bmih, Marshal.SizeOf(bmih)); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) @@ -802,7 +804,7 @@ public void OpenStreams() hr = AVIWriterImports.AVIStreamSetFormat( _pAviRawAudioStream, 0, - ref wfex, + &wfex, Marshal.SizeOf(wfex)); hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) @@ -818,22 +820,22 @@ public void OpenStreams() public void CloseFile() { CloseStreams(); - if (_pAviRawAudioStream != IntPtr.Zero) + if (_pAviRawAudioStream is not null) { _ = AVIWriterImports.AVIStreamRelease(_pAviRawAudioStream); - _pAviRawAudioStream = IntPtr.Zero; + _pAviRawAudioStream = null; } - if (_pAviRawVideoStream != IntPtr.Zero) + if (_pAviRawVideoStream is not null) { _ = AVIWriterImports.AVIStreamRelease(_pAviRawVideoStream); - _pAviRawVideoStream = IntPtr.Zero; + _pAviRawVideoStream = null; } - if (_pAviFile != IntPtr.Zero) + if (_pAviFile is not null) { _ = AVIWriterImports.AVIFileRelease(_pAviFile); - _pAviFile = IntPtr.Zero; + _pAviFile = null; } if (_pGlobalBuf != IntPtr.Zero) @@ -849,15 +851,15 @@ public void CloseFile() /// private void CloseStreams() { - if (_pAviRawAudioStream != IntPtr.Zero) + if (_pAviRawAudioStream is not null) { FlushBufferedAudio(); } - if (_pAviCompressedVideoStream != IntPtr.Zero) + if (_pAviCompressedVideoStream is not null) { _ = AVIWriterImports.AVIStreamRelease(_pAviCompressedVideoStream); - _pAviCompressedVideoStream = IntPtr.Zero; + _pAviCompressedVideoStream = null; } } @@ -885,7 +887,7 @@ public void AddSamples(IReadOnlyList samples) } } - private unsafe void FlushBufferedAudio() + private void FlushBufferedAudio() { var todo = _outStatus.audio_buffered_shorts; var todo_realsamples = todo / 2; @@ -898,22 +900,23 @@ private unsafe void FlushBufferedAudio() } // (TODO - inefficient- build directly in a buffer) + int bytesWritten; _ = AVIWriterImports.AVIStreamWrite( _pAviRawAudioStream, _outStatus.audio_samples, todo_realsamples, - buf, + buf.ToPointer(), todo_realsamples * 4, 0, - IntPtr.Zero, - out var bytes_written); + null, + &bytesWritten); _outStatus.audio_samples += todo_realsamples; - _outStatus.audio_bytes += bytes_written; + _outStatus.audio_bytes += bytesWritten; _outStatus.audio_buffered_shorts = 0; } /// attempted frame resize during encoding - public unsafe void AddFrame(IVideoProvider source) + public void AddFrame(IVideoProvider source) { if (_parameters.width != source.BufferWidth || _parameters.height != source.BufferHeight) @@ -953,17 +956,17 @@ public unsafe void AddFrame(IVideoProvider source) bp += pitch_add; } - int bytes_written = default; + int bytesWritten; _ = AVIWriterImports.AVIStreamWrite( _pAviCompressedVideoStream, lStart: _outStatus.video_frames, - samples: 1, + lSamples: 1, lpBuffer: bytes_ptr, cbBuffer: todo, dwFlags: AVIWriterImports.AVIIF_KEYFRAME, - plSampWritten: default, - plBytesWritten: &bytes_written); - _outStatus.video_bytes += bytes_written; + plSampWritten: null, + plBytesWritten: &bytesWritten); + _outStatus.video_bytes += bytesWritten; _outStatus.video_frames++; } } @@ -994,16 +997,17 @@ public unsafe void AddFrame(IVideoProvider source) idx -= w * 2; } + int bytesWritten; _ = AVIWriterImports.AVIStreamWrite( _pAviCompressedVideoStream, lStart: _outStatus.video_frames, - samples: 1, + lSamples: 1, lpBuffer: bytes_ptr, cbBuffer: todo * 3, dwFlags: AVIWriterImports.AVIIF_KEYFRAME, - plSampWritten: default, - plBytesWritten: &bytes_written); - _outStatus.video_bytes += bytes_written; + plSampWritten: null, + plBytesWritten: &bytesWritten); + _outStatus.video_bytes += bytesWritten; _outStatus.video_frames++; } } diff --git a/src/BizHawk.Common/Win32/AVIWriterImports.cs b/src/BizHawk.Common/Win32/AVIWriterImports.cs index 7a18946c27c..7ebf683c785 100644 --- a/src/BizHawk.Common/Win32/AVIWriterImports.cs +++ b/src/BizHawk.Common/Win32/AVIWriterImports.cs @@ -1,9 +1,6 @@ #nullable disable #if AVI_SUPPORT -using System.Runtime.InteropServices; - -// ReSharper disable FieldCanBeMadeReadOnly.Global namespace BizHawk.Common { @@ -15,89 +12,6 @@ public enum OpenFileStyle : uint OF_WRITE = 0x00000001, OF_CREATE = 0x00001000, } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct AVISTREAMINFOW - { - public int fccType; - public int fccHandler; - public int dwFlags; - public int dwCaps; - public short wPriority; - public short wLanguage; - public int dwScale; - public int dwRate; - public int dwStart; - public int dwLength; - public int dwInitialFrames; - public int dwSuggestedBufferSize; - public int dwQuality; - public int dwSampleSize; - public RECT rcFrame; - public int dwEditCount; - public int dwFormatChangeCount; - [MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] - public string szName; - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct RECT - { - public int Left; - public int Top; - public int Right; - public int Bottom; - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct BITMAPINFOHEADER - { - public uint biSize; - public int biWidth; - public int biHeight; - public ushort biPlanes; - public ushort biBitCount; - public uint biCompression; - public uint biSizeImage; - public int biXPelsPerMeter; - public int biYPelsPerMeter; - public uint biClrUsed; - public uint biClrImportant; - - public void Init() - => biSize = (uint)Marshal.SizeOf(this); - } - - [StructLayout(LayoutKind.Sequential)] - public struct AVICOMPRESSOPTIONS - { - public int fccType; - public int fccHandler; - public int dwKeyFrameEvery; - public int dwQuality; - public int dwBytesPerSecond; - public int dwFlags; - public IntPtr lpFormat; - public int cbFormat; - public IntPtr lpParms; - public int cbParms; - public int dwInterleaveEvery; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct WAVEFORMATEX - { - public ushort wFormatTag; - public ushort nChannels; - public uint nSamplesPerSec; - public uint nAvgBytesPerSec; - public ushort nBlockAlign; - public ushort wBitsPerSample; - public ushort cbSize; - - public void Init() - => cbSize = (ushort)Marshal.SizeOf(this); - } } } #endif