Проект описанный в данной статье являет собой мой первый опыт программирования на C# .NET, посему — прошу не судить строго.
Многие из нас пользуются Microsoft Visual Studio и, я полагаю, большинство для обмена кодом использует клевый ресурс http://pastebin.com
«Так зачем тратить время на загрузку этого сайта в браузере, копипаст кода в форму и нажатие кнопок», — подумал я и решил написать плагин для Студии, добавляющий в контекстное меню редактора кода пункт «PasteBin». Простым кликом на него вы заставите выделенную часть исходного кода переслаться на http://pastebin.com и, после этого, уникальный URL, созданный сайтом PasteBin окажется в вашем буфер обмена.
Для работы я использую Microsoft Visual Studio 2008, но в 2005 — все абсолютно аналогично.
Итак, все начинается с создания проекта.

Далее будут заданы несколько вопросов и, в итоге, перед вами появится заготовка будущего плагина. Исходный код любого плагина должен содержать некоторые стандартные функции. Сейчас я последовательно расскажу что за что отвечает и в каком порядке вызывается.
Для начала объявим глобальные переменные нашего стандартного класса
Многие из нас пользуются Microsoft Visual Studio и, я полагаю, большинство для обмена кодом использует клевый ресурс http://pastebin.com
«Так зачем тратить время на загрузку этого сайта в браузере, копипаст кода в форму и нажатие кнопок», — подумал я и решил написать плагин для Студии, добавляющий в контекстное меню редактора кода пункт «PasteBin». Простым кликом на него вы заставите выделенную часть исходного кода переслаться на http://pastebin.com и, после этого, уникальный URL, созданный сайтом PasteBin окажется в вашем буфер обмена.
Для работы я использую Microsoft Visual Studio 2008, но в 2005 — все абсолютно аналогично.
Итак, все начинается с создания проекта.

Далее будут заданы несколько вопросов и, в итоге, перед вами появится заготовка будущего плагина. Исходный код любого плагина должен содержать некоторые стандартные функции. Сейчас я последовательно расскажу что за что отвечает и в каком порядке вызывается.
Для начала объявим глобальные переменные нашего стандартного класса
Connect
. // Constants for command properties<br> private const string CommandName = "PasteBin";<br> private const string CommandCaption = "PasteBin";<br> private const string Url = "http://pastebin.com";<br> private const string RequestParameters = "parent_pid=&format={0}&code2={1}&poster=&paste=Send&expiry=f&email=";<br><br> // Constants for file formats<br> private static readonly Dictionary<string, string> FileFormat = new Dictionary<string, string><br> {<br> {"c","c"},<br> {"h","c"},<br> {"cpp","cpp"},<br> {"cs","csharp"}<br> };<br><br> // Variables for IDE and add-in instances<br> private DTE _applicationObject;<br> private AddIn _addInInstance;<br><br> // Buttons that will be created on built-in command bars of Visual Studio<br> // We must keep them at class level to remove them when the add-in is unloaded<br> private CommandBarButton _pasteBinButton;<br><br>* This source code was highlighted with Source Code Highlighter.
Первая стандартная функция OnConnection
описывает на каком этапе загрузки Студии необходимо внедрять наши изменения в пользовательский интерфейс. Для статьи она интереса не представляет, желающие смогут увидеть ее в полном исходном коде, который будет приложен в конце статьи. Гораздо больший интерес представляет функция AddUI
, описывающая непосредственно процесс добавления нового пункта в контекстное меню (кстати, AddUI
вызывается также и второй стандартной функцией — OnStartupComplete
, что гарантирует корректную загрузку нашего плагина, даже если при старте Студии возникли проблемы).
Итак, функция AddUI
. Вначале мы проверяем не создана ли уже команда с таким именем как у нас (мало ли) и если нет, то создаем ее сами.
// Try to retrieve the command, just in case it was already created, ignoring the <br> // exception that would happen if the command was not created yet.<br> try<br> {<br> myCommand = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + CommandName, -1);<br> }<br> catch (Exception e)<br> {<br> Debug.Write(e.ToString());<br> }<br><br> // Add the command if it does not exist<br> if (myCommand == null)<br> {<br> myCommand = _applicationObject.Commands.AddNamedCommand(_addInInstance,<br> CommandName, CommandCaption, null, true, 59, ref contextUIGuids,<br> (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));<br> }<br><br>* This source code was highlighted with Source Code Highlighter.
Далее получаем «указатель» на контекстное меню редактора кода.
// Constants for names of built-in command bars of Visual Studio<br> const string vsCodeWindowCommandbarName = "Code Window";<br><br> // Retrieve the collection of command bars<br> // Note:<br> // - In VS.NET 2002/2003 (which uses the Office.dll reference) <br> // DTE.CommandBars returns directly a CommandBars type, so a cast <br> // to CommandBars is redundant<br> // - In VS 2005 or higher (which uses the new Microsoft.VisualStudio.CommandBars.dll reference) <br> // DTE.CommandBars returns an Object type, so we do need a cast to CommandBars<br> var commandBars = (CommandBars)_applicationObject.CommandBars;<br><br> // Retrieve some built-in command bars<br> codeCommandBar = commandBars[vsCodeWindowCommandbarName];<br><br>* This source code was highlighted with Source Code Highlighter.
И описываем наш пункт в контекстном меню, не забывая про его внешний вид — иконку.
// Create the buttons from the commands<br> // Note:<br> // - In VS.NET 2002/2003 (which uses the Office.dll reference) <br> // Command.AddControl returns directly a CommandBarControl type, so a cast <br> // to CommandBarControl is redundant<br> // - In VS 2005 or higher (which uses the new Microsoft.VisualStudio.CommandBars.dll reference) <br> // Command.AddControl returns an Object type, so we do need a cast to CommandBarControl<br><br> string ns = GetType().Namespace;<br> Assembly currentAssembly = GetType().Assembly;<br><br> Stream<br> imgStreamPic = currentAssembly.GetManifestResourceStream(ns + "." + "klipper.png"),<br> imgStreamMask = currentAssembly.GetManifestResourceStream(ns + "." + "klipper-mask.png");<br><br> if (imgStreamPic != null && imgStreamMask != null)<br> {<br> Image<br> pasteBinImage = Image.FromStream(imgStreamPic),<br> pasteBinImageMask = Image.FromStream(imgStreamMask);<br><br> // ------------------------------------------------------------------------------------<br> // Button on the "Code Window" context menu<br> // ------------------------------------------------------------------------------------<br><br> // Add a button to the built-in "Code Window" context menu<br> _pasteBinButton = (CommandBarButton) myCommand.AddControl(codeCommandBar,<br> codeCommandBar.Controls.Count + 1);<br><br> // Change some button properties<br> _pasteBinButton.Caption = CommandCaption;<br> _pasteBinButton.BeginGroup = true; // Separator line above button<br><br> _pasteBinButton.Style = MsoButtonStyle.msoButtonIconAndCaption; // It could be also msoButtonIcon<br> _pasteBinButton.Picture = (stdole.StdPicture) ImageConverter.ImageToIpicture(pasteBinImage);<br> _pasteBinButton.Mask = (stdole.StdPicture) ImageConverter.ImageToIpicture(pasteBinImageMask);<br> }<br><br>* This source code was highlighted with Source Code Highlighter.
Для получения ресурсов с помощью GetManifestResourceStream
необходимо добавить нужные файлы в проект и изменить в свойствах параметр Build Action на Embeded Resource.

Далее следуют три абсолютно неинтересных функции OnDisconnection
, OnBeginShutdown
и OnAddInsUpdate
. Зато сразу же после них располагается сердце нашего проекта — функция Exec
.
Если какой-либо фрагмент кода выделен, то тогда на основе типа активного файла с исходным кодом и преобразованного с помощью UrlEncode
выделенного текста формируется запрос к сайту http://pastebin.com, а из ответа сервера вычленяется уникальная ссылка на размещенный исходный код, которая и помещается в буфер обмена.
public void Exec(string cmdName, vsCommandExecOption executeOption, ref object varIn,<br> ref object varOut, ref bool handled)<br> {<br><br> handled = false;<br><br> if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)<br> {<br> if (cmdName == string.Format("{0}.{1}", _addInInstance.ProgID, CommandName))<br> {<br> handled = true;<br> if (!((TextSelection)_applicationObject.ActiveDocument.Selection).IsEmpty)<br> {<br> // parameters: name1=value1&name2=value2 <br> var webRequest = (HttpWebRequest)WebRequest.Create(Url);<br><br> webRequest.ContentType = "application/x-www-form-urlencoded";<br> webRequest.Method = "POST";<br><br> // formatting request string from document type and selection<br> string request = string.Format(RequestParameters,<br> FileFormat[_applicationObject.ActiveDocument.Name.Split('.')[1]],<br> System.Web.HttpUtility.UrlEncode(((TextSelection)_applicationObject.ActiveDocument.Selection).Text));<br><br> Stream os = null;<br><br> try<br> { // send the POST<br> webRequest.ContentLength = request.Length; //Length of request to send<br> os = webRequest.GetRequestStream();<br> os.Write(System.Text.Encoding.GetEncoding("windows-1251").GetBytes(request), 0, request.Length); //Send it<br> }<br> catch (WebException ex)<br> {<br> MessageBox.Show(ex.Message, "HttpPost: Request error",<br> MessageBoxButtons.OK, MessageBoxIcon.Error);<br> }<br> finally<br> {<br> if (os != null)<br> {<br> os.Close();<br> }<br> }<br><br> try<br> { // get the response<br> var webResponse = (HttpWebResponse)webRequest.GetResponse();<br> if (webResponse != null)<br> { // copy to clipboard<br> Clipboard.SetText(webResponse.ResponseUri.ToString());<br> MessageBox.Show("Upload successfully. Url is copied in your clipboard.");<br> }<br> }<br> catch (WebException ex)<br> {<br> MessageBox.Show(ex.Message, "HttpPost: Response error",<br> MessageBoxButtons.OK, MessageBoxIcon.Error);<br> }<br> }<br> }<br> }<br> }<br><br>* This source code was highlighted with Source Code Highlighter.
Завершает файл также ничем не примечательная стандартная функция QueryStatus
.
Приложение А.
1. Содержание основного файла Connect.cs
2. Содержание вспомогательного файла ImageConverter.cs
На сайт http://pastebin.com вышеуказанные исходники добавлены при помощи описанного в статье плагина.
Приложение Б.
Файлы к статье
Файлы PasteBin.AddIn и PasteBin.dll необходимо поместить в каталог %USERPROFILE%\Мои Документы\Visual Studio 2008\Addins. 2008 можно заменить на 2005.
_________
Текст подготовлен в ХабраРедакторе