다음을 통해 공유


유니버설 Windows 프로젝트 관리

유니버설 Windows 앱은 Windows 8.1 및 Windows Phone 8.1을 대상으로 하는 앱으로, 개발자가 두 플랫폼에서 코드 및 기타 자산을 사용할 수 있도록 합니다. 공유 코드 및 리소스는 공유 프로젝트에 유지되고 플랫폼별 코드 및 리소스는 Windows 및 Windows Phone용 프로젝트에 별도로 유지됩니다. 유니버설 Windows 앱에 대한 자세한 내용은 유니버설 Windows 앱을 참조하세요. 프로젝트를 관리하는 Visual Studio 확장은 유니버설 Windows 앱 프로젝트에 단일 플랫폼 앱과 다른 구조체가 있다는 것을 알고 있어야 합니다. 이 연습에서는 공유 프로젝트를 탐색하고 공유 항목을 관리하는 방법을 보여 줍니다.

  1. TestUniversalProject라는 C# VSIX 프로젝트를 만듭니다. (파일>새로 만들기>프로젝트, 그런 다음 C#>확장성>Visual Studio 패키지). 사용자 지정 명령 프로젝트 항목 템플릿을 추가합니다(솔루션 탐색기에서 프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 추가>새 항목을 선택한 다음 확장성으로 이동). 파일 이름을 TestUniversalProject로 지정합니다.

  2. 확장 섹션에서 Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime.dllMicrosoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll에 대한 참조를 추가합니다.

  3. TestUniversalProject.cs를 열고 다음 using 지시문을 추가합니다.

    using EnvDTE;
    using EnvDTE80;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.PlatformUI;
    using Microsoft.Internal.VisualStudio.PlatformUI;
    using System.Collections.Generic;
    using System.IO;
    using System.Windows.Forms;
    
  4. TestUniversalProject 클래스에서 출력 창을 가리키는 프라이빗 필드를 추가합니다.

    public sealed class TestUniversalProject
    {
        IVsOutputWindowPane output;
    . . .
    }
    
  5. TestUniversalProject 생성자 내의 출력 창에 대한 참조를 설정합니다.

    private TestUniversalProject(Package package)
    {
        if (package == null)
        {
            throw new ArgumentNullException("package");
        }
    
        this.package = package;
    
        OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
        if (commandService != null)
        {
            CommandID menuCommandID = new CommandID(MenuGroup, CommandId);
            EventHandler eventHandler = this.ShowMessageBox;
            MenuCommand menuItem = new MenuCommand(eventHandler, menuCommandID);
            commandService.AddCommand(menuItem);
        }
    
        // get a reference to the Output window
        output = (IVsOutputWindowPane)ServiceProvider.GetService(typeof(SVsGeneralOutputWindowPane));
    }
    
  6. ShowMessageBox 메서드에서 기존 코드를 제거합니다.

    private void ShowMessageBox(object sender, EventArgs e)
    {
    }
    
  7. 이 연습에서 여러 가지 용도로 사용할 DTE 개체를 가져옵니다. 또한 메뉴 단추를 클릭할 때 솔루션이 로드되었는지 확인합니다.

    private void ShowMessageBox(object sender, EventArgs e)
    {
        var dte = (EnvDTE.DTE)this.ServiceProvider.GetService(typeof(EnvDTE.DTE));
        if (dte.Solution != null)
        {
            . . .
        }
        else
        {
            MessageBox.Show("No solution is open");
            return;
        }
    }
    
  8. 공유 프로젝트를 찾습니다. 공유 프로젝트는 순수 컨테이너입니다. 출력을 빌드하거나 생성하지 않습니다. 다음 메서드는 공유 프로젝트 기능이 있는 IVsHierarchy 개체를 찾아 솔루션에서 첫 번째 공유 프로젝트를 찾습니다.

    private IVsHierarchy FindSharedProject()
    {
        var sln = (IVsSolution)this.ServiceProvider.GetService(typeof(SVsSolution));
        Guid empty = Guid.Empty;
        IEnumHierarchies enumHiers;
    
        //get all the projects in the solution
        ErrorHandler.ThrowOnFailure(sln.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref empty, out enumHiers));
        foreach (IVsHierarchy hier in ComUtilities.EnumerableFrom(enumHiers))
        {
            if (PackageUtilities.IsCapabilityMatch(hier, "SharedAssetsProject"))
            {
                return hier;
            }
        }
        return null;
    }
    
  9. ShowMessageBox 메서드에서 공유 프로젝트의 캡션(솔루션 탐색기에 표시되는 프로젝트 이름)을 출력합니다.

    private void ShowMessageBox(object sender, EventArgs e)
    {
        var dte = (DTE)this.ServiceProvider.GetService(typeof(DTE));
    
        if (dte.Solution != null)
        {
            var sharedHier = this.FindSharedProject();
            if (sharedHier != null)
            {
                string sharedCaption = HierarchyUtilities.GetHierarchyProperty<string>(sharedHier, (uint)VSConstants.VSITEMID.Root,
                     (int)__VSHPROPID.VSHPROPID_Caption);
                output.OutputStringThreadSafe(string.Format("Found shared project: {0}\n", sharedCaption));
            }
            else
            {
                MessageBox.Show("Solution has no shared project");
                return;
            }
        }
        else
        {
            MessageBox.Show("No solution is open");
            return;
        }
    }
    
  10. 활성 플랫폼 프로젝트를 가져옵니다. 플랫폼 프로젝트는 플랫폼별 코드 및 리소스를 포함하는 프로젝트입니다. 다음 메서드는 새 필드 VSHPROPID_SharedItemContextHierarchy를 사용하여 활성 플랫폼 프로젝트를 가져옵니다.

    private IVsHierarchy GetActiveProjectContext(IVsHierarchy hierarchy)
    {
        IVsHierarchy activeProjectContext;
        if (HierarchyUtilities.TryGetHierarchyProperty(hierarchy, (uint)VSConstants.VSITEMID.Root,
             (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, out activeProjectContext))
        {
            return activeProjectContext;
        }
        else
        {
            return null;
        }
    }
    
  11. ShowMessageBox 메서드에서 활성 플랫폼 프로젝트의 캡션을 출력합니다.

    private void ShowMessageBox(object sender, EventArgs e)
    {
        var dte = (DTE)this.ServiceProvider.GetService(typeof(DTE));
    
        if (dte.Solution != null)
        {
            var sharedHier = this.FindSharedProject();
            if (sharedHier != null)
            {
                string sharedCaption = HierarchyUtilities.GetHierarchyProperty<string>(sharedHier, (uint)VSConstants.VSITEMID.Root,
                     (int)__VSHPROPID.VSHPROPID_Caption);
                output.OutputStringThreadSafe(string.Format("Shared project: {0}\n", sharedCaption));
    
                var activePlatformHier = this.GetActiveProjectContext(sharedHier);
                if (activePlatformHier != null)
                {
                    string activeCaption = HierarchyUtilities.GetHierarchyProperty<string>(activePlatformHier,
                         (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_Caption);
                    output.OutputStringThreadSafe(string.Format("Active platform project: {0}\n", activeCaption));
                }
                else
                {
                    MessageBox.Show("Shared project has no active platform project");
                }
            }
            else
            {
                MessageBox.Show("Solution has no shared project");
            }
        }
        else
        {
            MessageBox.Show("No solution is open");
        }
    }
    
  12. 플랫폼 프로젝트를 반복합니다. 다음 메서드는 공유 프로젝트에서 모든 가져오기(플랫폼) 프로젝트를 가져옵니다.

    private IEnumerable<IVsHierarchy> EnumImportingProjects(IVsHierarchy hierarchy)
    {
        IVsSharedAssetsProject sharedAssetsProject;
        if (HierarchyUtilities.TryGetHierarchyProperty(hierarchy, (uint)VSConstants.VSITEMID.Root,
            (int)__VSHPROPID7.VSHPROPID_SharedAssetsProject, out sharedAssetsProject)
            && sharedAssetsProject != null)
        {
            foreach (IVsHierarchy importingProject in sharedAssetsProject.EnumImportingProjects())
            {
                yield return importingProject;
            }
        }
    }
    

    Important

    사용자가 실험적 인스턴스에서 C++ 유니버설 Windows 앱 프로젝트를 연 경우 위의 코드는 예외를 throw합니다. 이것은 알려진 문제입니다. 예외를 방지하려면 위의 foreach 블록을 다음으로 바꿉니다.

    var importingProjects = sharedAssetsProject.EnumImportingProjects();
    for (int i = 0; i < importingProjects.Count; ++i)
    {
        yield return importingProjects[i];
    }
    
  13. ShowMessageBox 메서드에서 각 플랫폼 프로젝트의 캡션을 출력합니다. 활성 플랫폼 프로젝트의 캡션을 출력하는 줄 뒤에 다음 코드를 삽입합니다. 로드된 플랫폼 프로젝트만 이 목록에 표시됩니다.

    output.OutputStringThreadSafe("Platform projects:\n");
    
    IEnumerable<IVsHierarchy> projects = this.EnumImportingProjects(sharedHier);
    
    bool isActiveProjectSet = false;
    foreach (IVsHierarchy platformHier in projects)
    {
        string platformCaption = HierarchyUtilities.GetHierarchyProperty<string>(platformHier, (uint)VSConstants.VSITEMID.Root,
            (int)__VSHPROPID.VSHPROPID_Caption);
        output.OutputStringThreadSafe(string.Format(" * {0}\n", platformCaption));
    }
    
  14. 활성 플랫폼 프로젝트를 변경합니다. 다음 메서드는 SetProperty를 사용하여 활성 프로젝트를 설정합니다.

    private int SetActiveProjectContext(IVsHierarchy hierarchy, IVsHierarchy activeProjectContext)
    {
        return hierarchy.SetProperty((uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID7.VSHPROPID_SharedItemContextHierarchy, activeProjectContext);
    }
    
  15. ShowMessageBox 메서드에서 활성 플랫폼 프로젝트를 변경합니다. 이 코드를 foreach 블록 내에 삽입합니다.

    bool isActiveProjectSet = false;
    string platformCaption = null;
    foreach (IVsHierarchy platformHier in projects)
    {
        platformCaption = HierarchyUtilities.GetHierarchyProperty<string>(platformHier, (uint)VSConstants.VSITEMID.Root,
             (int)__VSHPROPID.VSHPROPID_Caption);
        output.OutputStringThreadSafe(string.Format(" * {0}\n", platformCaption));
    
        // if this project is neither the shared project nor the current active platform project,
        // set it to be the active project
        if (!isActiveProjectSet && platformHier != activePlatformHier)
        {
            this.SetActiveProjectContext(sharedHier, platformHier);
            activePlatformHier = platformHier;
            isActiveProjectSet = true;
        }
    }
    output.OutputStringThreadSafe("set active project: " + platformCaption +'\n');
    
  16. 이제 사용해 보세요. F5 키를 눌러 실험적 인스턴스를 시작합니다. 실험적 인스턴스에서 C# 유니버설 허브 앱 프로젝트를 만듭니다(새 프로젝트 대화 상자의 Visual C#>Windows>Windows 8>유니버설>허브 앱). 솔루션이 로드되면 도구 메뉴로 이동하여 TestUniversalProject 호출을 클릭한 다음 출력 창에서 텍스트를 확인합니다. 다음과 같은 정보가 표시됩니다.

    Found shared project: HubApp.Shared
    The active platform project: HubApp.Windows
    Platform projects:
     * HubApp.Windows
     * HubApp.WindowsPhone
    set active project: HubApp.WindowsPhone
    

플랫폼 프로젝트에서 공유 항목 관리

  1. 플랫폼 프로젝트에서 공유 항목을 찾습니다. 공유 프로젝트의 항목은 플랫폼 프로젝트에 공유 항목으로 표시됩니다. 솔루션 탐색기에서 볼 수는 없지만 프로젝트 계층 구조를 탐색하여 찾을 수 있습니다. 다음 메서드는 계층 구조를 탐색하고 모든 공유 항목을 수집합니다. 선택적으로 각 항목의 캡션을 출력합니다. 공유 항목은 새 속성 VSHPROPID_IsSharedItem으로 식별됩니다.

    private void InspectHierarchyItems(IVsHierarchy hier, uint itemid, int level, List<uint> itemIds, bool getSharedItems, bool printItems)
    {
        string caption = HierarchyUtilities.GetHierarchyProperty<string>(hier, itemid, (int)__VSHPROPID.VSHPROPID_Caption);
        if (printItems)
            output.OutputStringThreadSafe(string.Format("{0}{1}\n", new string('\t', level), caption));
    
        // if getSharedItems is true, inspect only shared items; if it's false, inspect only unshared items
        bool isSharedItem;
        if (HierarchyUtilities.TryGetHierarchyProperty(hier, itemid, (int)__VSHPROPID7.VSHPROPID_IsSharedItem, out isSharedItem)
            && (isSharedItem == getSharedItems))
        {
            itemIds.Add(itemid);
        }
    
        uint child;
        if (HierarchyUtilities.TryGetHierarchyProperty(hier, itemid, (int)__VSHPROPID.VSHPROPID_FirstChild, Unbox.AsUInt32, out child)
            && child != (uint)VSConstants.VSITEMID.Nil)
        {
            this.InspectHierarchyItems(hier, child, level + 1, itemIds, isSharedItem, printItems);
    
            while (HierarchyUtilities.TryGetHierarchyProperty(hier, child, (int)__VSHPROPID.VSHPROPID_NextSibling, Unbox.AsUInt32, out child)
                && child != (uint)VSConstants.VSITEMID.Nil)
            {
                this.InspectHierarchyItems(hier, child, level + 1, itemIds, isSharedItem, printItems);
            }
        }
    }
    
  2. ShowMessageBox 메서드에서 다음 코드를 추가하여 플랫폼 프로젝트 계층 구조 항목을 탐색합니다. 이 메서드를 foreach 블록 내에 삽입합니다.

    output.OutputStringThreadSafe("Walk the active platform project:\n");
    var sharedItemIds = new List<uint>();
    this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true);
    
  3. 공유 항목을 읽습니다. 공유 항목은 플랫폼 프로젝트에서 숨겨진 연결된 파일로 표시되며 모든 속성을 일반 연결된 파일로 읽을 수 있습니다. 다음 코드는 첫 번째 공유 항목의 전체 경로를 읽습니다.

    var sharedItemId = sharedItemIds[0];
    string fullPath;
    ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(sharedItemId, out fullPath));
    output.OutputStringThreadSafe(string.Format("Shared item full path: {0}\n", fullPath));
    
  4. 이제 사용해 보세요. F5 키를 눌러 실험적 인스턴스를 시작합니다. 실험적 인스턴스에서 C# 유니버설 허브 앱 프로젝트를 만듭니다(새 프로젝트 대화 상자의 Visual C#>Windows>Windows 8>유니버설>허브 앱). 도구 메뉴로 이동하여 TestUniversalProject 호출을 클릭한 다음 출력 창에서 텍스트를 확인합니다. 다음과 같은 정보가 표시됩니다.

    Found shared project: HubApp.Shared
    The active platform project: HubApp.Windows
    Platform projects:
     * HubApp.Windows
     * HubApp.WindowsPhone
    set active project: HubApp.WindowsPhone
    Walk the active platform project:
        HubApp.WindowsPhone
            <HubApp.Shared>
                App.xaml
                    App.xaml.cs
                Assets
                    DarkGray.png
                    LightGray.png
                    MediumGray.png
                Common
                    NavigationHelper.cs
                    ObservableDictionary.cs
                    RelayCommand.cs
                    SuspensionManager.cs
                DataModel
                    SampleData.json
                    SampleDataSource.cs
                HubApp.Shared.projitems
                Strings
                    en-US
                        Resources.resw
            Assets
                HubBackground.theme-dark.png
                HubBackground.theme-light.png
                Logo.scale-240.png
                SmallLogo.scale-240.png
                SplashScreen.scale-240.png
                Square71x71Logo.scale-240.png
                StoreLogo.scale-240.png
                WideLogo.scale-240.png
            HubPage.xaml
                HubPage.xaml.cs
            ItemPage.xaml
                ItemPage.xaml.cs
            Package.appxmanifest
            Properties
                AssemblyInfo.cs
            References
                .NET for Windows Store apps
                HubApp.Shared
                Windows Phone 8.1
            SectionPage.xaml
                SectionPage.xaml.cs
    

플랫폼 프로젝트 및 공유 프로젝트의 변경 내용 검색

  1. 플랫폼 프로젝트에서와 마찬가지로 계층 구조 및 프로젝트 이벤트를 사용하여 공유 프로젝트의 변경 내용을 검색할 수 있습니다. 그러나 공유 프로젝트의 프로젝트 항목은 표시되지 않으므로 공유 프로젝트 항목이 변경되면 특정 이벤트가 발생하지 않습니다.

    프로젝트에서 파일 이름이 바뀔 때의 이벤트 시퀀스를 생각해 보겠습니다.

    1. 디스크에서 파일 이름이 변경됩니다.

    2. 프로젝트 파일은 새 파일 이름을 포함하도록 업데이트됩니다.

      계층 구조 이벤트(예: IVsHierarchyEvents)는 일반적으로 솔루션 탐색기에서와 마찬가지로 UI에 표시되는 변경 내용을 추적합니다. 계층 구조 이벤트는 파일 삭제 후 파일 추가로 구성되도록 파일 이름 바꾸기 작업을 고려합니다. 그러나 보이지 않는 항목이 변경되면 계층 구조 이벤트 시스템은 OnItemAdded 이벤트가 아닌 OnItemDeleted 이벤트를 발생시킵니다. 따라서 플랫폼 프로젝트에서 파일 이름을 바꾸면 OnItemDeletedOnItemAdded를 둘 다 발생하지만 공유 프로젝트에서 파일 이름을 바꾸면 OnItemDeleted만 발생합니다.

      프로젝트 항목의 변경 내용을 추적하려면 DTE 프로젝트 항목 이벤트(ProjectItemsEventsClass에서 발견된 이벤트)를 처리할 수 있습니다. 그러나 많은 수의 이벤트를 처리하는 경우 IVsTrackProjectDocuments2에서 더 효율적으로 이벤트를 처리할 수 있습니다. 이 연습에서는 계층 구조 이벤트 및 DTE 이벤트만 보여 줍니다. 이 절차에서는 공유 프로젝트 및 플랫폼 프로젝트에 이벤트 수신기를 추가합니다. 그런 다음 공유 프로젝트와 플랫폼 프로젝트에서 하나씩 파일의 이름을 바꾸면 각 이름 바꾸기 작업에 대해 발생하는 이벤트를 볼 수 있습니다.

      이 절차에서는 공유 프로젝트 및 플랫폼 프로젝트에 이벤트 수신기를 추가합니다. 그런 다음 공유 프로젝트와 플랫폼 프로젝트에서 하나씩 파일의 이름을 바꾸면 각 이름 바꾸기 작업에 대해 발생하는 이벤트를 볼 수 있습니다.

  2. 이벤트 수신기를 추가합니다. 프로젝트에 새 클래스 파일을 추가하고 이름을 HierarchyEventListener.cs로 지정합니다.

  3. HierarchyEventListener.cs 파일을 열고 다음 using 지시문을 추가합니다.

    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio;
    using System.IO;
    
  4. HierarchyEventListener 클래스가 IVsHierarchyEvents를 구현하게 합니다.

    class HierarchyEventListener : IVsHierarchyEvents
    { }
    
  5. 아래 코드와 같이 IVsHierarchyEvents의 멤버를 구현합니다.

    class HierarchyEventListener : IVsHierarchyEvents
    {
        private IVsHierarchy hierarchy;
        IVsOutputWindowPane output;
    
        internal HierarchyEventListener(IVsHierarchy hierarchy, IVsOutputWindowPane outputWindow) {
             this.hierarchy = hierarchy;
             this.output = outputWindow;
        }
    
        int IVsHierarchyEvents.OnInvalidateIcon(IntPtr hIcon) {
            return VSConstants.S_OK;
        }
    
        int IVsHierarchyEvents.OnInvalidateItems(uint itemIDParent) {
            return VSConstants.S_OK;
        }
    
        int IVsHierarchyEvents.OnItemAdded(uint itemIDParent, uint itemIDSiblingPrev, uint itemIDAdded) {
            output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemAdded: " + itemIDAdded + "\n");
            return VSConstants.S_OK;
        }
    
        int IVsHierarchyEvents.OnItemDeleted(uint itemID) {
            output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemDeleted: " + itemID + "\n");
            return VSConstants.S_OK;
        }
    
        int IVsHierarchyEvents.OnItemsAppended(uint itemIDParent) {
            output.OutputStringThreadSafe("IVsHierarchyEvents.OnItemsAppended\n");
            return VSConstants.S_OK;
        }
    
        int IVsHierarchyEvents.OnPropertyChanged(uint itemID, int propID, uint flags) {
            output.OutputStringThreadSafe("IVsHierarchyEvents.OnPropertyChanged: item ID " + itemID + "\n");
            return VSConstants.S_OK;
        }
    }
    
  6. 동일한 클래스에서 프로젝트 항목의 이름이 바뀔 때마다 발생하는 DTE 이벤트 ItemRenamed에 대한 다른 이벤트 처리기를 추가합니다.

    public void OnItemRenamed(EnvDTE.ProjectItem projItem, string oldName)
    {
        output.OutputStringThreadSafe(string.Format("[Event] Renamed {0} to {1} in project {2}\n",
             oldName, Path.GetFileName(projItem.get_FileNames(1)), projItem.ContainingProject.Name));
    }
    
  7. 계층 구조 이벤트에 등록합니다. 추적하는 모든 프로젝트에 대해 별도로 등록해야 합니다. ShowMessageBox에서 다음 코드를 추가합니다. 하나는 공유 프로젝트에 대한 코드이고 다른 하나는 플랫폼 프로젝트 중 하나에 대한 코드입니다.

    // hook up the event listener for hierarchy events on the shared project
    HierarchyEventListener listener1 = new HierarchyEventListener(sharedHier, output);
    uint cookie1;
    sharedHier.AdviseHierarchyEvents(listener1, out cookie1);
    
    // hook up the event listener for hierarchy events on the
    active project
    HierarchyEventListener listener2 = new HierarchyEventListener(activePlatformHier, output);
    uint cookie2;
    activePlatformHier.AdviseHierarchyEvents(listener2, out cookie2);
    
  8. DTE 프로젝트 항목 이벤트 ItemRenamed에 등록합니다. 두 번째 수신기를 연결한 후 다음 코드를 추가합니다.

    // hook up DTE events for project items
    Events2 dteEvents = (Events2)dte.Events;
    dteEvents.ProjectItemsEvents.ItemRenamed += listener1.OnItemRenamed;
    
  9. 공유 항목을 수정합니다. 플랫폼 프로젝트에서는 공유 항목을 수정할 수 없습니다. 대신 이러한 항목의 실제 소유자인 공유 프로젝트에서 수정해야 합니다. IsDocumentInProject를 사용하여 공유 프로젝트에서 해당 항목 ID를 가져와서 공유 항목의 전체 경로를 제공할 수 있습니다. 그런 다음 공유 항목을 수정할 수 있습니다. 변경 내용은 플랫폼 프로젝트에 전파됩니다.

    Important

    프로젝트 항목을 수정하기 전에 공유 항목인지 여부를 확인해야 합니다.

    다음 메서드는 프로젝트 항목 파일의 이름을 수정합니다.

    private void ModifyFileNameInProject(IVsHierarchy project, string path)
    {
        int found;
        uint projectItemID;
        VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1];
        if (ErrorHandler.Succeeded(((IVsProject)project).IsDocumentInProject(path, out found, priority, out projectItemID))
            && found != 0)
        {
            var name = DateTime.Now.Ticks.ToString() + Path.GetExtension(path);
            project.SetProperty(projectItemID, (int)__VSHPROPID.VSHPROPID_EditLabel, name);
            output.OutputStringThreadSafe(string.Format("Renamed {0} to {1}\n", path,name));
        }
    }
    
  10. 이 메서드를 ShowMessageBox의 다른 모든 코드 뒤에 호출하여 공유 프로젝트의 항목에 있는 파일 이름을 수정합니다. 공유 프로젝트에서 항목의 전체 경로를 가져오는 코드 뒤에 다음 코드를 삽입합니다.

    // change the file name of an item in a shared project
    this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, sharedItemIds, true, true);
    ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(sharedItemId, out fullPath));
    output.OutputStringThreadSafe(string.Format("Shared project item ID = {0}, full path = {1}\n", sharedItemId, fullPath));
    this.ModifyFileNameInProject(sharedHier, fullPath);
    
  11. 프로젝트를 빌드하고 실행합니다. 실험적 인스턴스에서 C# 유니버설 허브 앱을 만들고 도구 메뉴로 이동하여 TestUniversalProject 호출을 클릭하고 일반 출력 창에서 텍스트를 확인합니다. 공유 프로젝트에서 첫 번째 항목의 이름(App.xaml 파일일 것으로 예상)이 변경되어야 하며 ItemRenamed 이벤트가 발생해야 합니다. 이 경우 App.xaml의 이름을 바꾸면 App.xaml.cs의 이름도 변경되므로 네 개의 이벤트(각 플랫폼 프로젝트에 대해 두 개)가 표시됩니다. (DTE 이벤트는 공유 프로젝트의 항목을 추적하지 않습니다.) 두 개의 OnItemDeleted 이벤트(각 플랫폼 프로젝트에 대해 하나씩)가 표시되지만 OnItemAdded 이벤트는 표시되지 않습니다.

  12. 이제 플랫폼 프로젝트에서 파일 이름을 바꾸면 발생하는 이벤트의 차이를 확인할 수 있습니다. ShowMessageBox에서 ModifyFileName에 대한 호출 뒤에 다음 코드를 추가합니다.

    // change the file name of an item in a platform project
    var unsharedItemIds = new List<uint>();
    this.InspectHierarchyItems(activePlatformHier, (uint)VSConstants.VSITEMID.Root, 1, unsharedItemIds, false, false);
    
    var unsharedItemId = unsharedItemIds[0];
    string unsharedPath;
    ErrorHandler.ThrowOnFailure(((IVsProject)activePlatformHier).GetMkDocument(unsharedItemId, out unsharedPath));
    output.OutputStringThreadSafe(string.Format("Platform project item ID = {0}, full path = {1}\n", unsharedItemId, unsharedPath));
    
    this.ModifyFileNameInProject(activePlatformHier, unsharedPath);
    
  13. 프로젝트를 빌드하고 실행합니다. 실험적 인스턴스에서 C# 유니버설 프로젝트를 만들고 도구 메뉴로 이동하여 TestUniversalProject 호출을 클릭하고 일반 출력 창에서 텍스트를 확인합니다. 플랫폼 프로젝트에서 파일 이름을 바꾼 후에는 OnItemAdded 이벤트와 OnItemDeleted 이벤트가 모두 표시됩니다. 파일 변경으로 인해 다른 파일이 변경되지 않았고 플랫폼 프로젝트의 항목에 대한 변경 내용이 전파되지 않으므로 이러한 이벤트는 각각 하나만 있습니다.