📜 ⬆️ ⬇️

Work with clipboard

After reading the title, you are probably very surprised.
After all, it would seem, everything is extremely simple - there is a Clipboard object, there are its static methods (like SetText / SetData and GetText / GetData), what else is needed for happiness?

However, in practice everything is simple only as long as you copy or paste only basic objects, such as text or bitmap-pictures. What happens when you need to operate with a more complex structure?

Personally, I recently faced the need to copy the "hyperlinks", which then should be easily inserted into Word / Outlook / any other program. And without relying on the recipient program itself defining the link in the inserted text and not converting it into the necessary format. Therefore, we consider the work on the example of a hyperlink (the algorithm of actions for any other format will be similar).
')
So where do you start?

To begin with, it is necessary to find out in what format certain data should be. It can be figured out in different ways, but the most obvious, perhaps, is the small utility ClipSpy . It is enough just to copy the desired object from anywhere and determine which formats are created on the clipboard and what they contain.

As the experiment shows, for the hyperlink TEXT, UNICODETEXT and HTML are usually created. The first and second contain a textual representation of the hyperlink (that will be inserted, for example, in a notebook). The HTML format is of most interest to us - it contains an html-fragment, which will be inserted into the target program as a hyperlink. It looks like this:
  Version: 1.0
 StartHTML: XXXXXXXX
 EndHTML: YYYYYYYY
 StartFragment: ZZZZZZZZ
 EndFragment: TTTTTTTT
 <html ....
 <a href="http://some.site.com/target"> Some target </a>
 ... </ html> 
where XXXXXXXX is the offset of the beginning of the html content (in fact, the length of the header),
YYYYYYYY - respectively, the offset of the end of the html content (in fact, the length of the entire content),
ZZZZZZZZ and TTTTTTTT - the beginning and end of the fragment with a hyperlink.

Well, we decided on the format. Now create the desired content - a matter of technology. For now, we will look at how to send the necessary content (in several formats) to the clipboard.

Google (advisor beloved by all) gives, as a rule, the following solution:
  void CopyLink (Uri target, string title)
 {
	 var htmlContent = MakeLink (target, title);
	 var data = new DataObject ();
	 data.SetData (DataFormats.Text, true, target.ToString ());
	 data.SetData (DataFormats.Unicode, true, html_content);
	 data.SetData (DataFormats.Html, true, formatted_buffer);
	 Clipboard.SetDataObject (data, true);
 } 

And everything would be fine if it were not for one “but” - such a solution does not allow you to control the encoding when setting the html content. Therefore, links, for example, with the Russian text in the title are inserted completely wrong, which leads to various interesting effects, up to the "crash" of the recipient program.

On this note, the idea of ​​writing a purely managed code covered with a copper basin, therefore, had to turn to WinAPI. As a result, it turned out not so beautiful, but it is very efficient:
  [DllImport ("user32.dll")]
 private static extern IntPtr SetClipboardData (uint uFormat, IntPtr hMem);
 [DllImport ("user32.dll")]
 private static extern bool OpenClipboard (IntPtr hWndNewOwner);
 [DllImport ("user32.dll")]
 private static extern bool EmptyClipboard ();
 [DllImport ("user32.dll")]
 private static extern bool CloseClipboard ();
 [DllImport ("user32.dll", SetLastError = true)]
 private static extern uint RegisterClipboardFormat (string lpszFormat);

 void CopyLink (Uri target, string title)
 {
	 var htmlContent = MakeLink (target, title, Encoding.UTF8);

	 if (! OpenClipboard (IntPtr.Zero))
		 throw new Exception ("Failed to open clipboard");
	 EmptyClipboard ();
        
	 var pText = IntPtr.Zero;
	 var pHtml = IntPtr.Zero;
	 try
	 {
		 pText = Marshal.StringToHGlobalAnsi (target.ToString ());
		 SetClipboardData (1 / * CF_TEXT * /, pText);  // For TEXT and UNICODETEXT

		 var bytes = Encoding.UTF8.GetBytes (htmlContent);
		 pHtml = Marshal.AllocHGlobal (bytes.Length);
		 Marshal.Copy (bytes, 0, pHtml, bytes.Length);
		 SetClipboardData (RegisterClipboardFormat (DataFormats.Html), pHtml);
	 }
	 finally 
	 {
		 CloseClipboard ();
		 if (pText! = IntPtr.Zero)
			 Marshal.FreeHGlobal (pText);
		 if (pHtml! = IntPtr.Zero)
			 Marshal.FreeHGlobal (pHtml);
	 }
 } 

Well, to complete the picture, the MakeLink source code:
  string MakeLink (Uri target, string title, Encoding encoding)
 {
	 const int numberLengthWithCr = 11;
	 var htmlIntro = "<html> \ n <head> \ n <meta http-equiv = \" Content-Type \ "content = \" text / html;  charset = " 
		 + encoding.WebName + "\" /> \ n </ head> \ n <body> \ n <! - StartFragment -> ";
	 var htmlOutro = "<! - EndFragment -> \ n </ body> \ n </ html>";
	 var htmlLink = string.Format ("<a href=\"{0}\" {1} </a>", target, title);

	 var startHtmlIndex = 57 + 4 * numberLengthWithCr;
	 var startFragmentIndex = startHtmlIndex + encoding.GetByteCount (htmlIntro);
	 var endFragmentIndex = startFragmentIndex + encoding.GetByteCount (htmlLink);
	 var endHtmlIndex = endFragmentIndex + encoding. GetByteCount (htmlOutro);

	 var buff = new StringBuilder ();
	 buff.AppendFormat ("Version: 1.0 \ n");
	 buff.AppendFormat ("StartHTML: {0: 0000000000} \ n", startHtmlIndex);
	 buff.AppendFormat ("EndHTML: {0: 0000000000} \ n", endHtmlIndex);
	 buff.AppendFormat ("StartFragment: {0: 0000000000} \ n", startFragmentIndex);
	 buff.AppendFormat ("EndFragment: {0: 0000000000} \ n", endFragmentIndex);
	 buff.Append (htmlIntro) .Append (htmlLink) .Append (htmlOutro);
	 return buff.ToString ();
 } 


PS By the way, the question is: is it possible to write code normally, without disabling auto-formatting and manually arranging line breaks? And then with the auto format in
  the spacing between the lines is just deadly.
 </ habracut> 

Source: https://habr.com/ru/post/24712/


All Articles