ClipboardImpl.cs 4.4 KB

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