Add /xlcopylog command (#1850)

* feat: add /xlcopylog command

Add a command to copy the dalamud.log file to the user's
clipboard, behaving the same as copying the file in an
Explorer window.

* chore: fix clientstructs submodule

* fix: dispose hGlobal if it's unused

* refactor: move file copier to util class
This commit is contained in:
Anna 2024-06-21 22:36:18 +00:00 committed by GitHub
parent 5d2942786f
commit 50dc3d44c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 111 additions and 12 deletions

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using CheapLoc;
@ -143,6 +144,13 @@ internal class DalamudCommands : IServiceType
HelpMessage = "ImGui DEBUG",
ShowInHelp = false,
});
commandManager.AddHandler("/xlcopylog", new CommandInfo(this.OnCopyLogCommand)
{
HelpMessage = Loc.Localize(
"DalamudCopyLogHelp",
"Copy the dalamud.log file to your clipboard."),
});
}
private void OnUnloadCommand(string command, string arguments)
@ -406,4 +414,17 @@ internal class DalamudCommands : IServiceType
{
Service<DalamudInterface>.Get().ToggleProfilerWindow();
}
private void OnCopyLogCommand(string command, string arguments)
{
var chatGui = Service<ChatGui>.Get();
var logPath = Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"XIVLauncher",
"dalamud.log");
var message = Util.CopyFilesToClipboard([logPath])
? Loc.Localize("DalamudLogCopySuccess", "Log file copied to clipboard.")
: Loc.Localize("DalamudLogCopyFailure", "Could not copy log file to clipboard.");
chatGui.Print(message);
}
}

View file

@ -11,3 +11,13 @@ SetActiveWindow
HWND_TOPMOST
HWND_NOTOPMOST
SET_WINDOW_POS_FLAGS
OpenClipboard
SetClipboardData
CloseClipboard
DROPFILES
CLIPBOARD_FORMAT
GlobalAlloc
GlobalLock
GlobalUnlock
GLOBAL_ALLOC_FLAGS

View file

@ -25,6 +25,11 @@ using Lumina.Excel.GeneratedSheets;
using Serilog;
using TerraFX.Interop.Windows;
using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Memory;
using Windows.Win32.System.Ole;
using HWND = Windows.Win32.Foundation.HWND;
using Win32_PInvoke = Windows.Win32.PInvoke;
namespace Dalamud.Utility;
@ -736,6 +741,69 @@ public static class Util
}
}
/// <summary>
/// Copy files to the clipboard as if they were copied in Explorer.
/// </summary>
/// <param name="paths">Full paths to files to be copied.</param>
/// <returns>Returns true on success.</returns>
internal static unsafe bool CopyFilesToClipboard(IEnumerable<string> paths)
{
var pathBytes = paths
.Select(Encoding.Unicode.GetBytes)
.ToArray();
var pathBytesSize = pathBytes
.Select(bytes => bytes.Length)
.Sum();
var sizeWithTerminators = pathBytesSize + (pathBytes.Length * 2);
var dropFilesSize = sizeof(DROPFILES);
var hGlobal = Win32_PInvoke.GlobalAlloc_SafeHandle(
GLOBAL_ALLOC_FLAGS.GHND,
// struct size + size of encoded strings + null terminator for each
// string + two null terminators for end of list
(uint)(dropFilesSize + sizeWithTerminators + 4));
var dropFiles = (DROPFILES*)Win32_PInvoke.GlobalLock(hGlobal);
*dropFiles = default;
dropFiles->fWide = true;
dropFiles->pFiles = (uint)dropFilesSize;
var pathLoc = (byte*)((nint)dropFiles + dropFilesSize);
foreach (var bytes in pathBytes)
{
// copy the encoded strings
for (var i = 0; i < bytes.Length; i++)
{
pathLoc![i] = bytes[i];
}
// null terminate
pathLoc![bytes.Length] = 0;
pathLoc[bytes.Length + 1] = 0;
pathLoc += bytes.Length + 2;
}
// double null terminator for end of list
for (var i = 0; i < 4; i++)
{
pathLoc![i] = 0;
}
Win32_PInvoke.GlobalUnlock(hGlobal);
if (Win32_PInvoke.OpenClipboard(HWND.Null))
{
Win32_PInvoke.SetClipboardData(
(uint)CLIPBOARD_FORMAT.CF_HDROP,
hGlobal);
Win32_PInvoke.CloseClipboard();
return true;
}
hGlobal.Dispose();
return false;
}
private static void ShowSpanProperty(ulong addr, IList<string> path, PropertyInfo p, object obj)
{
var objType = obj.GetType();