2025-12-01 09:57:41 +08:00
|
|
|
// Copyright (c) Microsoft Corporation
|
|
|
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
|
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Runtime.InteropServices;
|
2025-12-01 10:58:26 +08:00
|
|
|
using ManagedCommon;
|
2025-12-01 09:57:41 +08:00
|
|
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
|
|
|
|
|
|
|
|
|
namespace PowerToysExtension.Commands;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Opens Advanced Paste UI, reusing an existing instance when present.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal sealed partial class OpenAdvancedPasteCommand : InvokableCommand
|
|
|
|
|
{
|
|
|
|
|
public OpenAdvancedPasteCommand()
|
|
|
|
|
{
|
|
|
|
|
Name = "Open Advanced Paste";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override CommandResult Invoke()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (TryActivateExisting())
|
|
|
|
|
{
|
2025-12-01 10:58:26 +08:00
|
|
|
return CommandResult.Dismiss();
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 10:58:26 +08:00
|
|
|
return CommandResult.ShowToast("Advanced Paste is not running. Please start it first.");
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return CommandResult.ShowToast($"Failed to open Advanced Paste: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool TryActivateExisting()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-12-01 10:58:26 +08:00
|
|
|
var processes = Process.GetProcessesByName("PowerToys.AdvancedPaste");
|
|
|
|
|
Logger.LogInfo($"AdvancedPaste: found {processes.Length} processes.");
|
|
|
|
|
|
|
|
|
|
foreach (var proc in processes)
|
2025-12-01 09:57:41 +08:00
|
|
|
{
|
|
|
|
|
if (proc.HasExited)
|
|
|
|
|
{
|
2025-12-01 10:58:26 +08:00
|
|
|
Logger.LogInfo($"AdvancedPaste: pid {proc.Id} already exited.");
|
2025-12-01 09:57:41 +08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 10:58:26 +08:00
|
|
|
proc.Refresh();
|
2025-12-01 09:57:41 +08:00
|
|
|
var handle = proc.MainWindowHandle;
|
2025-12-01 10:58:26 +08:00
|
|
|
|
|
|
|
|
if (handle != IntPtr.Zero)
|
2025-12-01 09:57:41 +08:00
|
|
|
{
|
2025-12-01 10:58:26 +08:00
|
|
|
Logger.LogInfo($"AdvancedPaste: using MainWindowHandle for pid {proc.Id}.");
|
|
|
|
|
ShowWindowAsync(handle, SW_RESTORE);
|
|
|
|
|
SetForegroundWindow(handle);
|
|
|
|
|
return true;
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 10:58:26 +08:00
|
|
|
Logger.LogInfo($"AdvancedPaste: MainWindowHandle not ready for pid {proc.Id}; enumerating windows.");
|
|
|
|
|
|
|
|
|
|
if (TryBringProcessWindowToFront(proc.Id))
|
2025-12-01 09:57:41 +08:00
|
|
|
{
|
2025-12-01 10:58:26 +08:00
|
|
|
return true;
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-01 10:58:26 +08:00
|
|
|
Logger.LogInfo($"AdvancedPaste: no window found for pid {proc.Id}.");
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 10:58:26 +08:00
|
|
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
2025-12-01 09:57:41 +08:00
|
|
|
private const int SW_RESTORE = 9;
|
2025-12-01 10:58:26 +08:00
|
|
|
#pragma warning restore SA1310 // Field names should not contain underscore
|
2025-12-01 09:57:41 +08:00
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
|
2025-12-01 10:58:26 +08:00
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
|
|
|
|
|
|
|
|
private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
|
|
|
|
|
|
|
|
|
|
private static bool TryBringProcessWindowToFront(int pid)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var windowHandle = IntPtr.Zero;
|
|
|
|
|
EnumWindows(
|
|
|
|
|
(hwnd, lParam) =>
|
|
|
|
|
{
|
|
|
|
|
var threadId = GetWindowThreadProcessId(hwnd, out var windowPid);
|
|
|
|
|
if (threadId == 0)
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInfo("AdvancedPaste: GetWindowThreadProcessId returned 0.");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (windowPid == pid)
|
|
|
|
|
{
|
|
|
|
|
windowHandle = hwnd;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
IntPtr.Zero);
|
|
|
|
|
|
|
|
|
|
if (windowHandle == IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logger.LogInfo($"AdvancedPaste: enumerated window handle for pid {pid}.");
|
|
|
|
|
ShowWindowAsync(windowHandle, SW_RESTORE);
|
|
|
|
|
SetForegroundWindow(windowHandle);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.LogError($"AdvancedPaste: failed to enumerate/activate window for pid {pid}: {ex}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-01 09:57:41 +08:00
|
|
|
}
|