ClipboardImpl.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #nullable enable
  2. using System.ComponentModel;
  3. using System.Runtime.InteropServices;
  4. namespace Terminal.Gui;
  5. internal class WindowsClipboard : ClipboardBase
  6. {
  7. private const uint CF_UNICODE_TEXT = 13;
  8. public override bool IsSupported { get; } = CheckClipboardIsAvailable ();
  9. private static bool CheckClipboardIsAvailable ()
  10. {
  11. // Attempt to open the clipboard
  12. if (OpenClipboard (nint.Zero))
  13. {
  14. // Clipboard is available
  15. // Close the clipboard after use
  16. CloseClipboard ();
  17. return true;
  18. }
  19. // Clipboard is not available
  20. return false;
  21. }
  22. protected override string GetClipboardDataImpl ()
  23. {
  24. try
  25. {
  26. if (!OpenClipboard (nint.Zero))
  27. {
  28. return string.Empty;
  29. }
  30. nint handle = GetClipboardData (CF_UNICODE_TEXT);
  31. if (handle == nint.Zero)
  32. {
  33. return string.Empty;
  34. }
  35. nint pointer = nint.Zero;
  36. try
  37. {
  38. pointer = GlobalLock (handle);
  39. if (pointer == nint.Zero)
  40. {
  41. return string.Empty;
  42. }
  43. int size = GlobalSize (handle);
  44. var buff = new byte [size];
  45. Marshal.Copy (pointer, buff, 0, size);
  46. return Encoding.Unicode.GetString (buff).TrimEnd ('\0');
  47. }
  48. finally
  49. {
  50. if (pointer != nint.Zero)
  51. {
  52. GlobalUnlock (handle);
  53. }
  54. }
  55. }
  56. finally
  57. {
  58. CloseClipboard ();
  59. }
  60. }
  61. protected override void SetClipboardDataImpl (string text)
  62. {
  63. OpenClipboard ();
  64. EmptyClipboard ();
  65. nint hGlobal = default;
  66. try
  67. {
  68. int bytes = (text.Length + 1) * 2;
  69. hGlobal = Marshal.AllocHGlobal (bytes);
  70. if (hGlobal == default (nint))
  71. {
  72. ThrowWin32 ();
  73. }
  74. nint target = GlobalLock (hGlobal);
  75. if (target == default (nint))
  76. {
  77. ThrowWin32 ();
  78. }
  79. try
  80. {
  81. Marshal.Copy (text.ToCharArray (), 0, target, text.Length);
  82. }
  83. finally
  84. {
  85. GlobalUnlock (target);
  86. }
  87. if (SetClipboardData (CF_UNICODE_TEXT, hGlobal) == default (nint))
  88. {
  89. ThrowWin32 ();
  90. }
  91. hGlobal = default (nint);
  92. }
  93. finally
  94. {
  95. if (hGlobal != default (nint))
  96. {
  97. Marshal.FreeHGlobal (hGlobal);
  98. }
  99. CloseClipboard ();
  100. }
  101. }
  102. [DllImport ("user32.dll", SetLastError = true)]
  103. [return: MarshalAs (UnmanagedType.Bool)]
  104. private static extern bool CloseClipboard ();
  105. [DllImport ("user32.dll")]
  106. private static extern bool EmptyClipboard ();
  107. [DllImport ("user32.dll", SetLastError = true)]
  108. private static extern nint GetClipboardData (uint uFormat);
  109. [DllImport ("kernel32.dll", SetLastError = true)]
  110. private static extern nint GlobalLock (nint hMem);
  111. [DllImport ("kernel32.dll", SetLastError = true)]
  112. private static extern int GlobalSize (nint handle);
  113. [DllImport ("kernel32.dll", SetLastError = true)]
  114. [return: MarshalAs (UnmanagedType.Bool)]
  115. private static extern bool GlobalUnlock (nint hMem);
  116. [DllImport ("User32.dll", SetLastError = true)]
  117. [return: MarshalAs (UnmanagedType.Bool)]
  118. private static extern bool IsClipboardFormatAvailable (uint format);
  119. private void OpenClipboard ()
  120. {
  121. var num = 10;
  122. while (true)
  123. {
  124. if (OpenClipboard (default (nint)))
  125. {
  126. break;
  127. }
  128. if (--num == 0)
  129. {
  130. ThrowWin32 ();
  131. }
  132. Thread.Sleep (100);
  133. }
  134. }
  135. [DllImport ("user32.dll", SetLastError = true)]
  136. [return: MarshalAs (UnmanagedType.Bool)]
  137. private static extern bool OpenClipboard (nint hWndNewOwner);
  138. [DllImport ("user32.dll", SetLastError = true)]
  139. private static extern nint SetClipboardData (uint uFormat, nint data);
  140. private void ThrowWin32 () { throw new Win32Exception (Marshal.GetLastWin32Error ()); }
  141. }