using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace QuickLook.MediaInfo.Core; /// /// Low-level P/Invoke wrapper around the native MediaInfo library. /// All public methods return empty strings when the library fails to load. /// [SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time")] [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression")] public sealed partial class MediaInfoHandle : IDisposable { #region Native imports [DllImport(Libs.DllName, CharSet = CharSet.Unicode)] private static extern nint MediaInfo_New(); [DllImport(Libs.DllName)] private static extern void MediaInfo_Delete(nint handle); [DllImport(Libs.DllName, CharSet = CharSet.Unicode)] private static extern nint MediaInfo_Open(nint handle, string fileName); [DllImport(Libs.DllName)] private static extern nint MediaInfoA_Open(nint handle, nint fileName); [DllImport(Libs.DllName)] private static extern nint MediaInfo_Open_Buffer_Init(nint handle, long fileSize, long fileOffset); [DllImport(Libs.DllName)] private static extern nint MediaInfo_Open_Buffer_Continue(nint handle, nint buffer, nint bufferSize); [DllImport(Libs.DllName)] private static extern long MediaInfo_Open_Buffer_Continue_GoTo_Get(nint handle); [DllImport(Libs.DllName)] private static extern nint MediaInfo_Open_Buffer_Finalize(nint handle); [DllImport(Libs.DllName)] private static extern void MediaInfo_Close(nint handle); [DllImport(Libs.DllName)] private static extern nint MediaInfo_Inform(nint handle, nint reserved); [DllImport(Libs.DllName)] private static extern nint MediaInfoA_Inform(nint handle, nint reserved); [DllImport(Libs.DllName, CharSet = CharSet.Unicode)] private static extern nint MediaInfo_Get(nint handle, nint streamKind, nint streamNumber, string parameter, nint kindOfInfo, nint kindOfSearch); [DllImport(Libs.DllName)] private static extern nint MediaInfoA_Get(nint handle, nint streamKind, nint streamNumber, nint parameter, nint kindOfInfo, nint kindOfSearch); [DllImport(Libs.DllName)] private static extern nint MediaInfo_GetI(nint handle, nint streamKind, nint streamNumber, nint parameter, nint kindOfInfo); [DllImport(Libs.DllName)] private static extern nint MediaInfoA_GetI(nint handle, nint streamKind, nint streamNumber, nint parameter, nint kindOfInfo); [DllImport(Libs.DllName, CharSet = CharSet.Unicode)] private static extern nint MediaInfo_Option(nint handle, string option, string value); [DllImport(Libs.DllName)] private static extern nint MediaInfoA_Option(nint handle, nint option, nint value); [DllImport(Libs.DllName)] private static extern nint MediaInfo_State_Get(nint handle); [DllImport(Libs.DllName)] private static extern nint MediaInfo_Count_Get(nint handle, nint streamKind, nint streamNumber); #endregion Native imports private bool _disposed; private readonly bool _useAnsi; /// Gets the native handle. when the library failed to load. public nint Handle { get; } public MediaInfoHandle() { try { Handle = MediaInfo_New(); } catch { Handle = IntPtr.Zero; } #if NETFRAMEWORK _useAnsi = !true; #elif NET8_0_OR_GREATER _useAnsi = !OperatingSystem.IsWindows(); #else _useAnsi = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); #endif } ~MediaInfoHandle() => Dispose(false); public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } [SuppressMessage("Style", "IDE0060:Remove unused parameter")] private void Dispose(bool disposing) { if (_disposed) return; if (Handle != IntPtr.Zero) { MediaInfo_Close(Handle); MediaInfo_Delete(Handle); } _disposed = true; } // -- Open / Close ---------------------------------------------------------- public nint Open(string fileName) { if (Handle == IntPtr.Zero) return IntPtr.Zero; if (_useAnsi) { nint ptr = Marshal.StringToHGlobalAnsi(fileName); try { return MediaInfoA_Open(Handle, ptr); } finally { Marshal.FreeHGlobal(ptr); } } return MediaInfo_Open(Handle, fileName); } public nint OpenBufferInit(long fileSize, long fileOffset) { if (Handle == IntPtr.Zero) return IntPtr.Zero; return MediaInfo_Open_Buffer_Init(Handle, fileSize, fileOffset); } public nint OpenBufferContinue(nint buffer, nint bufferSize) { if (Handle == IntPtr.Zero) return IntPtr.Zero; return MediaInfo_Open_Buffer_Continue(Handle, buffer, bufferSize); } public unsafe nint OpenBufferContinue(byte* buffer, int bufferSize) { if (Handle == IntPtr.Zero) return IntPtr.Zero; return MediaInfo_Open_Buffer_Continue(Handle, (nint)buffer, bufferSize); } public long OpenBufferContinueGoToGet() { if (Handle == IntPtr.Zero) return -1; return MediaInfo_Open_Buffer_Continue_GoTo_Get(Handle); } public nint OpenBufferFinalize() { if (Handle == IntPtr.Zero) return IntPtr.Zero; return MediaInfo_Open_Buffer_Finalize(Handle); } public void Close() { if (Handle == IntPtr.Zero) return; MediaInfo_Close(Handle); } // -- Inform ---------------------------------------------------------------- public string Inform() { if (Handle == IntPtr.Zero) return "Unable to load MediaInfo library"; return _useAnsi ? Marshal.PtrToStringAnsi(MediaInfoA_Inform(Handle, 0)) ?? string.Empty : Marshal.PtrToStringUni(MediaInfo_Inform(Handle, 0)) ?? string.Empty; } // -- Get ------------------------------------------------------------------- public string Get(StreamKind streamKind, int streamNumber, string parameter, InfoKind kindOfInfo = InfoKind.Text, InfoKind kindOfSearch = InfoKind.Name) { if (Handle == IntPtr.Zero) return "Unable to load MediaInfo library"; if (_useAnsi) { nint paramPtr = Marshal.StringToHGlobalAnsi(parameter); try { return Marshal.PtrToStringAnsi( MediaInfoA_Get(Handle, (nint)streamKind, streamNumber, paramPtr, (nint)kindOfInfo, (nint)kindOfSearch)) ?? string.Empty; } finally { Marshal.FreeHGlobal(paramPtr); } } return Marshal.PtrToStringUni( MediaInfo_Get(Handle, (nint)streamKind, streamNumber, parameter, (nint)kindOfInfo, (nint)kindOfSearch)) ?? string.Empty; } public string Get(StreamKind streamKind, int streamNumber, int parameter, InfoKind kindOfInfo = InfoKind.Text) { if (Handle == IntPtr.Zero) return "Unable to load MediaInfo library"; return _useAnsi ? Marshal.PtrToStringAnsi(MediaInfoA_GetI(Handle, (nint)streamKind, streamNumber, parameter, (nint)kindOfInfo)) ?? string.Empty : Marshal.PtrToStringUni(MediaInfo_GetI(Handle, (nint)streamKind, streamNumber, parameter, (nint)kindOfInfo)) ?? string.Empty; } // -- Option ---------------------------------------------------------------- public string Option(string option, string value = "") { if (Handle == IntPtr.Zero) return "Unable to load MediaInfo library"; if (_useAnsi) { nint optPtr = Marshal.StringToHGlobalAnsi(option); nint valPtr = Marshal.StringToHGlobalAnsi(value); try { return Marshal.PtrToStringAnsi(MediaInfoA_Option(Handle, optPtr, valPtr)) ?? string.Empty; } finally { Marshal.FreeHGlobal(optPtr); Marshal.FreeHGlobal(valPtr); } } return Marshal.PtrToStringUni(MediaInfo_Option(Handle, option, value)) ?? string.Empty; } // -- State / Count --------------------------------------------------------- public int StateGet() { if (Handle == IntPtr.Zero) return 0; return (int)MediaInfo_State_Get(Handle); } public int CountGet(StreamKind streamKind, int streamNumber = -1) { if (Handle == IntPtr.Zero) return 0; return (int)MediaInfo_Count_Get(Handle, (nint)streamKind, streamNumber); } }