Прочитав заголовок, Вы, наверное, очень удивились.
Ведь казалось бы, все предельно просто — есть объект Clipboard, есть его статические методы (вроде SetText/SetData и GetText/GetData), чего еще для счастья нужно?
Однако, на практике все просто лишь до тех пор, пока Вы копируете или вставляете только базовые объекты, вроде текста или bitmap-картинки. Что же случается, когда нужно оперировать более сложной структурой?
Лично я недавно столкнулся с необходимостью копировать «гиперссылки», которые потом должны легко вставляться в Word/Outlook/любую другую программу. Причем, не полагаясь на то, что программа-получатель сама определит во вставленном тексте ссылку и не преобразует в нужный формат. Поэтому и рассмотрим работу на примере гиперссылки (алгоритм действий для любого другого формата будет аналогичным).
Итак, с чего же начать?
Для начала, необходимо выяснить, в каком формате должны быть те или иные данные. Выяснить это можно разными способами, но самый наглядный, пожалуй, — это небольшая утилита ClipSpy. Достаточно просто скопировать откуда угодно нужный объект и определить, какие форматы при этом создаются в буфере обмена и что они в себе содержат.
Как показывает эксперимент, для гиперссылки обычно создаются форматы TEXT, UNICODETEXT и HTML. Первый и второй содержат в себе текстовое представление гиперссылки (то, что будет вставлено, например, в блокнот). Формат HTML же представляет для нас наибольший интерес — в нем содержится html-фрагмент, который и будет вставлен в целевую программу как гиперссылка. Выглядит он примерно так:
YYYYYYYY — соответственно, смещение конца html-содержимого (фактически, длина всего контента),
ZZZZZZZZ и TTTTTTTT — начало и конец фрагмента с гиперссылкой.
Что ж, с форматом определились. Теперь сформировать нужный контент — дело техники. А мы пока что рассмотрим, как отправить нужное содержимое (в нескольких форматах) в буфер обмена.
Гугл (любимый всеми советчик) дает, как правило, следующее решение:
И все бы было хорошо, если бы не одно «но» — такое решение совершенно не позволяет управлять кодировкой при задании html-содержимого. Поэтому ссылки, например, с русским текстом в названии вставляются совершенно неправильно, что приводит к разным интересным эффектам, вплоть до «вылетания» программы-получателя.
На этой ноте идея написания чисто управляемого кода накрылась медным тазом, посему пришлось обращаться к WinAPI. В итоге вышло не так красиво, зато весьма работоспособно:
Ну, и для полноты картины исходный текст MakeLink:
P.S. Кстати, вопрос: есть ли возможность писать код нормально, без отключения автоформатирования и ручной расстановки разрывов строк? А то с автоформатированием в
Ведь казалось бы, все предельно просто — есть объект Clipboard, есть его статические методы (вроде SetText/SetData и GetText/GetData), чего еще для счастья нужно?
Однако, на практике все просто лишь до тех пор, пока Вы копируете или вставляете только базовые объекты, вроде текста или bitmap-картинки. Что же случается, когда нужно оперировать более сложной структурой?
Лично я недавно столкнулся с необходимостью копировать «гиперссылки», которые потом должны легко вставляться в Word/Outlook/любую другую программу. Причем, не полагаясь на то, что программа-получатель сама определит во вставленном тексте ссылку и не преобразует в нужный формат. Поэтому и рассмотрим работу на примере гиперссылки (алгоритм действий для любого другого формата будет аналогичным).
Итак, с чего же начать?
Для начала, необходимо выяснить, в каком формате должны быть те или иные данные. Выяснить это можно разными способами, но самый наглядный, пожалуй, — это небольшая утилита ClipSpy. Достаточно просто скопировать откуда угодно нужный объект и определить, какие форматы при этом создаются в буфере обмена и что они в себе содержат.
Как показывает эксперимент, для гиперссылки обычно создаются форматы TEXT, UNICODETEXT и HTML. Первый и второй содержат в себе текстовое представление гиперссылки (то, что будет вставлено, например, в блокнот). Формат HTML же представляет для нас наибольший интерес — в нем содержится html-фрагмент, который и будет вставлен в целевую программу как гиперссылка. Выглядит он примерно так:
где XXXXXXXX — смещение начала html-содержимого (фактически, длина заголовка),Version:1.0 StartHTML:XXXXXXXX EndHTML:YYYYYYYY StartFragment:ZZZZZZZZ EndFragment:TTTTTTTT <html .... <a href="http://some.site.com/target">Some target</a> ... </html>
YYYYYYYY — соответственно, смещение конца html-содержимого (фактически, длина всего контента),
ZZZZZZZZ и TTTTTTTT — начало и конец фрагмента с гиперссылкой.
Что ж, с форматом определились. Теперь сформировать нужный контент — дело техники. А мы пока что рассмотрим, как отправить нужное содержимое (в нескольких форматах) в буфер обмена.
Гугл (любимый всеми советчик) дает, как правило, следующее решение:
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); }
И все бы было хорошо, если бы не одно «но» — такое решение совершенно не позволяет управлять кодировкой при задании html-содержимого. Поэтому ссылки, например, с русским текстом в названии вставляются совершенно неправильно, что приводит к разным интересным эффектам, вплоть до «вылетания» программы-получателя.
На этой ноте идея написания чисто управляемого кода накрылась медным тазом, посему пришлось обращаться к WinAPI. В итоге вышло не так красиво, зато весьма работоспособно:
[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); // Для TEXT и 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); } }
Ну, и для полноты картины исходный текст MakeLink:
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(); }
P.S. Кстати, вопрос: есть ли возможность писать код нормально, без отключения автоформатирования и ручной расстановки разрывов строк? А то с автоформатированием в
интервал между строками просто убийственный. </habracut>