Добрый день.
Будем делать расширение Visual Studio, которое встраивает в контекстное меню редактора поле «Create C++ Header #define», при клике на которое генерируется уникальная директива #define (директива позволяет включать header только один раз).
В конечном счете выглядеть это будет следующим образом:



На самом деле обычно это автоматизируется с помощью макросов самой студии ( и вариантов макросов в сети много), но мне приятнее использовать контекстное меню, тем более что создание самого расширения — это дело простое, всю работу сделает VSPackage Builder, нам останется лишь вписать несколько строчек кода в обработчик нажатия элемента меню.


Итак, начнем. Нам потребуется Visual Studio 2010 Pro или старше. Необходимо дополнительно установить Visual Studio 2010 SDK и специальное дополнение VSPackage Builder, максимально облегчающее создание новых расширений.

Создание расширения


Создадим новый проект в Visual Studio:
Visual C# -> Extensibility -> VSPackage Builder
Даем проекту имя — я назвала его HeaderDefineCreater.



В открывшемся проекте разместим на дизайнере расширения (файл HeaderDefineCreater.vspackage) все необходимые нам элементы. Начнем с первого элемента — добавим контекстное меню. В его свойствах напротив поля Location выберем размещение — «Context Menu | Editor»



Затем создадим группу Group, одну кнопку (назовем ее CreateHeaderDefineButton) и два коннекта между всеми элементами. На скрине с левой стороны показаны свойства для кнопки. Там все стандартное, разве что следует обратить внимание что кнопка (то есть строка в контекстном меню) будет видима только в редакторе (Visibility Constraints: TextEditor). Что касается шортката, то эти поля можно оставить пустыми, поскольку их можно настроить отдельно при использовании расширения.



Ну вот, все готово. Остало��ь только заполнить обработчик нажатия на элемент контекстного меню.
Автоматически сгенерированный обработчик нажатия выглядит следующим образом:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;


namespace HeaderDefineCreater
{
    [Guid(GuidList.guidHeaderDefineCreaterPkgString)]
    public class HeaderDefineCreaterPackage : HeaderDefineCreaterPackageBase
    {
        protected override void CreateHeaderDefineButtonExecuteHandler(object sender, EventArgs e)
        {

        }
    }
}



Заполним обработчик. Существует множество схожих по функциональности макросов для студии, написанные в VB, так что ничего не пришлось выдумывать самой, просто слегка переписала на С#.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;

// Импортируем еще 3 пространства имен:
using System.Windows.Forms;
using EnvDTE;
using System.IO;

namespace HeaderDefineCreater
{
    [Guid(GuidList.guidHeaderDefineCreaterPkgString)]
    public class HeaderDefineCreaterPackage : HeaderDefineCreaterPackageBase
    {
        protected override void CreateHeaderDefineButtonExecuteHandler(object sender, EventArgs e)
        {
            try
            {
                // Получим объект верхнего уровня в объектной модели автоматизации Visual Studio
                EnvDTE.DTE dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));

                // Получим название без расширения у активного документа в Visual Studio
                String fileName = Path.GetFileNameWithoutExtension(dte.ActiveDocument.Name);

                // Приведем название к прописным буквам
                fileName = fileName.ToUpper();

                // Сгенерируем уникальный идентификатор GUID
                String strGuid = System.Guid.NewGuid().ToString().ToUpper();
                strGuid = strGuid.Replace("-", "_");

                // Объединим название файла и идентификатор GUID 
                String strDefine = fileName + "_" + strGuid;

                // Создадим саму надпись, которая будет выведена в документе
                string txt = "";
                txt += "\n#ifndef " + strDefine;
                txt += "\n#define " + strDefine;
                txt += "\n\n\n\n\n#endif // " + strDefine;
                txt += "\n";
    
                // Выведем надпись в активный документ
                TextSelection textSelection = (TextSelection)dte.ActiveDocument.Selection;
                textSelection.Text = txt;

            }
            catch (Exception ex)
            {
                // Если что-то пошло не так, то просто покажем сообщение об ошибке и не уроним студию...
                MessageBox.Show(ex.Message);
            }
        }
    }
}


Нам осталось лишь заполнить поля в манифесте (добавить иконку, описание и тд. ) и все скомпилировать.



После компиляции мы получим готовое расширение HeaderDefineCreater.vsix.

Установка расширения и смена шорткатов


Устанавливается расширение простым двойным кликом.
Удалить его можно зайдя в Tools->Extension Manager.



Для того, чтобы установить шорткат для расширения, нужно зайти в Tools -> Options -> Keyboard, найти там по поиску имя кнопки (мы кнопку назвали CreateHeaderDefineButton, но, впрочем, для простоты можно было бы ее назвать и именем расширения). Для найденной кнопки ставим любое свободное сочетание:



Спасибо за внимание. Замечания приветствуются. Замечу, что я не пишу на C#, а изучаю С++ и расширение создавалось прежд�� всего для облегчения рутинных операций на плюсах и для внутреннего использования. Думаю, что название для поля контекстного меню можно было бы придумать и лучше… да и иконка не соответствует содержимому :) Но здесь мне просто хотелось показать как быстро создать свое расширение, надеюсь, еще кому-нибудь будет полезно.

Я не стала выкладывать сюда весь проект, поскольку расширение очень простое, все шаги описаны, да и весь код в обработчике клика тоже здесь выложен. Поэтому приложу для примера только само расширение, получившееся в итоге — HeaderDefineCreater.vsix