diff --git a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs index 13ccda5eb9..d67834a070 100644 --- a/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs +++ b/src/BizHawk.Client.EmuHawk/EmuHawkUtil.cs @@ -23,16 +23,19 @@ namespace BizHawk.Client.EmuHawk using var link = new ShellLinkImports.ShellLink(); - const uint STGM_READ = 0; - link.Load(filename, STGM_READ); + unsafe + { + const uint STGM_READ = 0; + ((ShellLinkImports.IPersistFile*)link)->Load(filename, STGM_READ); #if false - // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. - link.Resolve(hwnd, 0); + // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. + ((ShellLinkImports.IShellLinkW*)link)->Resolve(hwnd, 0); #endif - link.GetPath(out var path, Win32Imports.MAX_PATH + 1, out _, 0); - return path; + ((ShellLinkImports.IShellLinkW*)link)->GetPath(out var path, Win32Imports.MAX_PATH + 1, 0); + return path; + } } } } diff --git a/src/BizHawk.Common/Win32/ShellLinkImports.cs b/src/BizHawk.Common/Win32/ShellLinkImports.cs index 1ccd6eb089..3deabfaefa 100644 --- a/src/BizHawk.Common/Win32/ShellLinkImports.cs +++ b/src/BizHawk.Common/Win32/ShellLinkImports.cs @@ -10,33 +10,6 @@ namespace BizHawk.Common { public static class ShellLinkImports { - [StructLayout(LayoutKind.Sequential)] - public struct WIN32_FIND_DATAW - { - public uint dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - public uint dwReserved0; - public uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Win32Imports.MAX_PATH)] - public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] - public string cAlternateFileName; - // Obsolete fields - public uint dwFileType; - public uint dwCreatorType; - public int wFinderFlags; - - public struct FILETIME - { - public uint dwLowDateTime; - public uint dwHighDateTime; - } - } - /// The IShellLink interface allows Shell links to be created, modified, and resolved [StructLayout(LayoutKind.Sequential)] public unsafe struct IShellLinkW @@ -72,25 +45,35 @@ namespace BizHawk.Common } public IShellLinkWVtbl* lpVtbl; - } - [StructLayout(LayoutKind.Sequential)] - public unsafe struct IPersist - { - public static readonly Guid Guid = new("0000010c-0000-0000-C000-000000000046"); - - [StructLayout(LayoutKind.Sequential)] - public struct IPersistVtbl + public void GetPath(out string pszFile, int cch, uint fFlags) { - // IUnknown functions - public delegate* unmanaged[Stdcall] QueryInterface; - public delegate* unmanaged[Stdcall] AddRef; - public delegate* unmanaged[Stdcall] Release; - // IPersist functions - public delegate* unmanaged[Stdcall] GetClassID; + fixed (IShellLinkW* _this = &this) + { + var _pszFile = Marshal.AllocCoTaskMem(cch * sizeof(char)); + try + { + var hr = lpVtbl->GetPath(_this, _pszFile, cch, IntPtr.Zero, fFlags); + Marshal.ThrowExceptionForHR(hr); + pszFile = Marshal.PtrToStringUni(_pszFile); + } + finally + { + Marshal.FreeCoTaskMem(_pszFile); + } + } } - public IPersistVtbl* lpVtbl; +#if false + public void Resolve(IntPtr hwnd, int fFlags) + { + fixed (IShellLinkW* _this = &this) + { + var hr = lpVtbl->Resolve(_this, hwnd, fFlags); + Marshal.ThrowExceptionForHR(hr); + } + } +#endif } [StructLayout(LayoutKind.Sequential)] @@ -116,12 +99,31 @@ namespace BizHawk.Common } public IPersistFileVtbl* lpVtbl; + + public void Load(string pszFileName, uint dwMode) + { + fixed (IPersistFile* _this = &this) + { + var _pszFileName = Marshal.StringToCoTaskMemUni(pszFileName); + try + { + var hr = lpVtbl->Load(_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; @@ -132,7 +134,7 @@ namespace BizHawk.Common Marshal.ThrowExceptionForHR(hr); var sli = (IShellLinkW*)psl; - hr = sli->lpVtbl->QueryInterface(sli, in IPersist.Guid, out var ppf); + hr = sli->lpVtbl->QueryInterface(sli, in IPersistFile.Guid, out var ppf); var hrEx = Marshal.GetExceptionForHR(hr); if (hrEx != null) { @@ -158,44 +160,6 @@ namespace BizHawk.Common SLI = null; } } - - public void GetPath(out string pszFile, int cch, out WIN32_FIND_DATAW pfd, uint fFlags) - { - var pszFile_ = Marshal.AllocCoTaskMem(cch * sizeof(char)); -#if false // should we do this? we don't need pfd (NULL is valid), and we could delete the WIN32_FIND_DATAW definition by doing this - var hr = SLI->lpVtbl->GetPath(SLI, pszFile_, cch, IntPtr.Zero, fFlags); -#else - var pfd_ = Marshal.AllocCoTaskMem(Marshal.SizeOf()); - var hr = SLI->lpVtbl->GetPath(SLI, pszFile_, cch, pfd_, fFlags); -#endif - try - { - Marshal.ThrowExceptionForHR(hr); - pszFile = Marshal.PtrToStringUni(pszFile_); - pfd = Marshal.PtrToStructure(pfd_); - } - finally - { - Marshal.FreeCoTaskMem(pszFile_); - Marshal.FreeCoTaskMem(pfd_); - } - } - -#if false - public void Resolve(IntPtr hwnd, int fFlags) - { - var hr = SLI->lpVtbl->Resolve(SLI, hwnd, fFlags); - Marshal.ThrowExceptionForHR(hr); - } -#endif - - public void Load(string pszFileName, uint dwMode) - { - var pszFileName_ = Marshal.StringToCoTaskMemUni(pszFileName); - var hr = PFI->lpVtbl->Load(PFI, pszFileName_, dwMode); - Marshal.FreeCoTaskMem(pszFileName_); - Marshal.ThrowExceptionForHR(hr); - } } } }