■ Visual Studio 확장을 개발하는 방법을 보여준다.
1. [Visual Studio Installer]를 실행한다.
2. [Visual Studio Installer]에서 아래 항목이 설치되어 있지 않다면 설치한다.
3. [Visual Studio]를 실행한다.
4. [Visual Studio 2019] 대화 상자에서 아래와 같이 [새 프로젝트 만들기] 항목을 클릭한다.
5. [새 프로젝트 만들기] 대화 상자에서 아래와 같이 [VSIX Project] 템플리트를 선택하고 [다음] 버튼을 클릭한다.
6. [새 프로젝트 구성] 대화 상자에서 아래와 같이 입력하고 [만들기] 버튼을 클릭한다.
7. [솔루션 탐색기]의 [TestProject] 프로젝트 항목에서 마우스 오른쪽 버튼을 클릭하고 컨텍스트 메뉴에서 [추가] 메뉴를 클릭한다.
8. 컨텍스트 메뉴에서 [새 항목] 메뉴를 클릭한다.
9. [Visual Studio]의 [새 항목 추가] 대화 상자에서 아래와 같이 입력하고 [추가] 버튼을 클릭한다.
10. 소스 코드는 아래와 같다.
▶ TestCommand.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.ComponentModel.Design; using System.Globalization; namespace TestProject { /// <summary> /// 테스트 명령 /// </summary> internal sealed class TestCommand { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 명령 ID /// </summary> public const int CommandID = 0x0100; /// <summary> /// 명령 세트 /// </summary> public static readonly Guid CommandSet = new Guid("EE73DD14-8767-4596-9AB9-7C271A43C6F5"); #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 패키지 /// </summary> private readonly AsyncPackage package; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 인스턴스 - Instance /// <summary> /// 인스턴스 /// </summary> public static TestCommand Instance { get; private set; } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Instance //////////////////////////////////////////////////////////////////////////////// Private #region 서비스 공급자 - ServiceProvider /// <summary> /// 서비스 공급자 /// </summary> private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider { get { return this.package; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Private #region 생성자 - TestCommand(package, commandService) /// <summary> /// 생성자 /// </summary> /// <param name="package">패키지</param> /// <param name="commandService">명령 서비스</param> private TestCommand(AsyncPackage package, OleMenuCommandService commandService) { this.package = package ?? throw new ArgumentNullException(nameof(package)); commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); CommandID commandID = new CommandID(CommandSet, CommandID); MenuCommand menuCommand = new MenuCommand(this.Execute, commandID); commandService.AddCommand(menuCommand); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 초기화하기 (비동기) - InitializeAsync(package) /// <summary> /// 초기화하기 (비동기) /// </summary> /// <param name="package">패키지</param> /// <returns>태스크</returns> public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; Instance = new TestCommand(package, commandService); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 실행하기 - Execute(sender, e) /// <summary> /// 실행하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Execute(object sender, EventArgs e) { ThreadHelper.ThrowIfNotOnUIThread(); string message = string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()", this.GetType().FullName); string title = "TestCommand"; VsShellUtilities.ShowMessageBox ( this.package, message, title, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST ); } #endregion } } |
▶ TestProjectPackage.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
using Microsoft.VisualStudio.Shell; using System; using System.Runtime.InteropServices; using System.Threading; namespace TestProject { /// <summary> /// 테스트 프로젝트 패키지 /// </summary> [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [Guid(TestProjectPackage.PackageGUIDString)] [ProvideMenuResource("Menus.ctmenu", 1)] public sealed class TestProjectPackage : AsyncPackage { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 패키지 GUID 문자열 /// </summary> public const string PackageGUIDString = "06A361EF-CA91-46BC-97A1-CCC7C20AB1E9"; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 초기화하기 (비동기) - InitializeAsync(cancellationToken, progress) /// <summary> /// 초기화하기 (비동기) /// </summary> /// <param name="cancellationToken">취소 토큰</param> /// <param name="progress">진행 인터페이스</param> /// <returns>태스크</returns> protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await TestCommand.InitializeAsync(this); } #endregion } } |
▶ TestProjectPackage.vsct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?xml version="1.0" encoding="utf-8"?> <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <Extern href="stdidcmd.h" /> <Extern href="vsshlids.h" /> <Commands package="guidTestProjectPackage"> <Groups> <Group id="TestMenuGroup" guid="guidTestProjectPackageCmdSet" priority="0x0600"> <Parent id="IDM_VS_MENU_TOOLS" guid="guidSHLMainMenu" /> </Group> </Groups> <Buttons> <Button id="TestCommandID" guid="guidTestProjectPackageCmdSet" priority="0x0100" type="Button"> <Parent id="TestMenuGroup" guid="guidTestProjectPackageCmdSet" /> <Icon id="bmpPic1" guid="guidImages" /> <Strings> <ButtonText>Invoke TestCommand</ButtonText> </Strings> </Button> </Buttons> <Bitmaps> <Bitmap guid="guidImages" href="Resources\TestCommand.png" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows, bmpPicStrikethrough" /> </Bitmaps> </Commands> <Symbols> <GuidSymbol name="guidTestProjectPackage" value="{06A361EF-CA91-46BC-97A1-CCC7C20AB1E9}" /> <GuidSymbol name="guidTestProjectPackageCmdSet" value="{EE73DD14-8767-4596-9AB9-7C271A43C6F5}"> <IDSymbol name="TestMenuGroup" value="0x1020" /> <IDSymbol name="TestCommandID" value="0x0100" /> </GuidSymbol> <GuidSymbol name="guidImages" value="{885098d7-9719-48d4-9ebd-38de4f8ddde3}" > <IDSymbol name="bmpPic1" value="1" /> <IDSymbol name="bmpPic2" value="2" /> <IDSymbol name="bmpPicSearch" value="3" /> <IDSymbol name="bmpPicX" value="4" /> <IDSymbol name="bmpPicArrows" value="5" /> <IDSymbol name="bmpPicStrikethrough" value="6" /> </GuidSymbol> </Symbols> </CommandTable> |