diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs index e7677801..63b07293 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/FileOperation.cs @@ -1,7 +1,13 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Win32.Foundation; +using Snap.Hutao.Win32.System.Com; +using Snap.Hutao.Win32.UI.Shell; using System.IO; +using static Snap.Hutao.Win32.Macros; +using static Snap.Hutao.Win32.Ole32; +using static Snap.Hutao.Win32.Shell32; namespace Snap.Hutao.Core.IO; @@ -39,4 +45,62 @@ internal static class FileOperation File.Move(sourceFileName, destFileName, false); return true; } + + public static unsafe bool UnsafeMove(string sourceFileName, string destFileName) + { + bool result = false; + + HRESULT hr = CoCreateInstance(in Win32.UI.Shell.FileOperation.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IFileOperation.IID, out IFileOperation* pFileOperation); + if (SUCCEEDED(hr)) + { + hr = SHCreateItemFromParsingName(sourceFileName.AsSpan(), default, in IShellItem.IID, out IShellItem* pSourceShellItem); + if (SUCCEEDED(hr)) + { + hr = SHCreateItemFromParsingName(destFileName.AsSpan(), default, in IShellItem.IID, out IShellItem* pDestShellItem); + if (SUCCEEDED(hr)) + { + pFileOperation->MoveItem(pSourceShellItem, pDestShellItem, default, default); + + if (SUCCEEDED(pFileOperation->PerformOperations())) + { + result = true; + } + + pDestShellItem->Release(); + } + + pSourceShellItem->Release(); + } + + pFileOperation->Release(); + } + + return result; + } + + public static unsafe bool UnsafeDelete(string path) + { + bool result = false; + + HRESULT hr = CoCreateInstance(in Win32.UI.Shell.FileOperation.CLSID, default, CLSCTX.CLSCTX_INPROC_SERVER, in IFileOperation.IID, out IFileOperation* pFileOperation); + if (SUCCEEDED(hr)) + { + hr = SHCreateItemFromParsingName(path.AsSpan(), default, in IShellItem.IID, out IShellItem* pShellItem); + if (SUCCEEDED(hr)) + { + pFileOperation->DeleteItem(pShellItem, default); + + if (SUCCEEDED(pFileOperation->PerformOperations())) + { + result = true; + } + + pShellItem->Release(); + } + + pFileOperation->Release(); + } + + return result; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/FileOperation.cs b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/FileOperation.cs new file mode 100644 index 00000000..e3efc500 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/FileOperation.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Win32.UI.Shell; + +[Guid("3ad05575-8857-4850-9277-11b85bdb8e09")] +internal readonly struct FileOperation +{ + internal static unsafe ref readonly Guid CLSID + { + get + { + ReadOnlySpan data = [0x75, 0x55, 0xD0, 0x3A, 0x57, 0x88, 0x50, 0x48, 0x92, 0x77, 0x11, 0xB8, 0x5B, 0xDB, 0x8E, 0x09]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/IFileOperation.cs b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/IFileOperation.cs index db14bbe7..e55948ea 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/IFileOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/UI/Shell/IFileOperation.cs @@ -47,6 +47,21 @@ internal unsafe struct IFileOperation return ThisPtr->IUnknownVftbl.Release((IUnknown*)Unsafe.AsPointer(ref this)); } + public unsafe HRESULT DeleteItem(IShellItem* psiItem, IFileOperationProgressSink* pfopsItem) + { + return ThisPtr->DeleteItem((IFileOperation*)Unsafe.AsPointer(ref this), psiItem, pfopsItem); + } + + public unsafe HRESULT MoveItem(IShellItem* psiItem, IShellItem* psiDestinationFolder, [AllowNull] PCWSTR pszNewName, IFileOperationProgressSink* pfopsItem) + { + return ThisPtr->MoveItem((IFileOperation*)Unsafe.AsPointer(ref this), psiItem, psiDestinationFolder, pszNewName, pfopsItem); + } + + public unsafe HRESULT PerformOperations() + { + return ThisPtr->PerformOperations((IFileOperation*)Unsafe.AsPointer(ref this)); + } + internal readonly struct Vftbl { internal readonly IUnknown.Vftbl IUnknownVftbl;