File: TrayApplication.Clipboard.cs
Web Access
Project: src\src\Aspire.Tray\Windows\Aspire.Tray.Windows.csproj (aspire-tray)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.InteropServices;
 
namespace Aspire.Tray;
 
internal sealed unsafe partial class TrayApplication
{
    private void CopyPath(string path)
    {
        if (smokeSeconds is not null && !_interactiveSmoke)
        {
            (CopyPathForSmoke ?? throw new InvalidOperationException("Smoke clipboard handler is missing."))(path);
            return;
        }
        // CF_UNICODETEXT needs a movable, NUL-terminated buffer. Prepare it before
        // emptying the clipboard; ownership transfers only after SetClipboardData succeeds.
        // https://learn.microsoft.com/windows/win32/dataxchg/using-the-clipboard
        var memory = NativeMethods.GlobalAlloc(0x2, checked((nuint)(path.Length + 1) * sizeof(char)));
        NativeCallException.Require(memory != 0, "GlobalAlloc(clipboard)");
        try
        {
            var buffer = NativeMethods.GlobalLock(memory);
            NativeCallException.Require(buffer != 0, "GlobalLock(clipboard)");
            path.AsSpan().CopyTo(new Span<char>((void*)buffer, path.Length));
            ((char*)buffer)[path.Length] = '\0';
            // Returning zero is also success when the last lock is released.
            Marshal.SetLastPInvokeError(0);
            var unlocked = NativeMethods.GlobalUnlock(memory);
            NativeCallException.Require(unlocked != 0 || Marshal.GetLastPInvokeError() == 0, "GlobalUnlock(clipboard)");
            NativeCallException.Require(NativeMethods.OpenClipboard(_window) != 0, "OpenClipboard");
            try
            {
                NativeCallException.Require(NativeMethods.EmptyClipboard() != 0, "EmptyClipboard");
                NativeCallException.Require(NativeMethods.SetClipboardData(13, memory) != 0, "SetClipboardData");
                memory = 0;
            }
            finally
            {
                NativeCallException.Require(NativeMethods.CloseClipboard() != 0, "CloseClipboard");
            }
        }
        finally
        {
            if (memory != 0)
            {
                Cleanup(NativeMethods.GlobalFree(memory) == 0, "GlobalFree(clipboard)");
            }
        }
    }
}