私は予想以上に簡単だったパッケージ、プロジェクトのためのサブタイプを作成することになりました。大きな課題は、異なるSDKバージョンを参照するため、VS2013パッケージとVS2010パッケージの間でアプリケーションコードを共有する方法を見つけ出すことでした。私は、2つの別々のプロジェクトファイルを作成し、各プロジェクトのリンク参照として共有コードを組み込みました。
IPropertyPage
の実装をPropPageBaseとPropPageUserControlBaseでモデル化しました。マイクロソフトが提供するコードはより複雑であるため、そのコードの一部を参考にしました。
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Diagnostics.CodeAnalysis
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports Microsoft.VisualStudio
Imports Microsoft.VisualStudio.OLE.Interop
Imports Microsoft.VisualStudio.Shell.Interop
Imports ControlPosition = System.Drawing.Point
Imports ControlSize = System.Drawing.Size
<ComVisible(True)>
Public MustInherit Class PropertyPageProviderBase
Implements IPropertyPage, IDisposable
Private ReadOnly _dirtyProperties As New Dictionary(Of String, String)()
Private _control As Control
Private _defaultSize As System.Drawing.Size?
Private _hostedInNative As Boolean
Private _objects As Object()
Private _pageSite As IPropertyPageSite
<SuppressMessage(_
"Microsoft.Reliability", _
"CA2006:UseSafeHandleToEncapsulateNativeResources", _
Justification:="Handle is not owned by us, we are just tracking a reference")>
Private _previousParent As IntPtr
Protected Sub New()
End Sub
' ...
Protected Property [Property](propertyName As String) As String
Get
If String.IsNullOrEmpty(propertyName) Then
If propertyName Is Nothing Then
Throw New ArgumentNullException("propertyName")
End If
Throw New ArgumentException(_
"Empty property name is invalid", _
"propertyName")
End If
Dim dirtyValue As String = Nothing
If _dirtyProperties.TryGetValue(propertyName, dirtyValue) Then
Return dirtyValue
End If
Return ReadProperty(propertyName)
End Get
Set(value As String)
If String.IsNullOrEmpty(propertyName) Then
If propertyName Is Nothing Then
Throw New ArgumentNullException("propertyName")
End If
Throw New ArgumentException(_
"Empty property name is invalid", _
"propertyName")
End If
If _objects IsNot Nothing Then
_dirtyProperties.Item(propertyName) = value
If _pageSite IsNot Nothing Then
_pageSite.OnStatusChange(PROPPAGESTATUS.DIRTY)
End If
Else
Debug.Fail("Accessing property while not bound to project")
End If
End Set
End Property
' ...
Protected Overridable Sub Apply()
If _objects Is Nothing Then
If _dirtyProperties.Count <> 0 Then
Debug.Fail("Cannot save changes. Not bound to project")
End If
Exit Sub
End If
For Each dirtyProperty As KeyValuePair(Of String, String) In _dirtyProperties
WriteProperty(dirtyProperty.Key, dirtyProperty.Value)
Next
_dirtyProperties.Clear()
If _pageSite IsNot Nothing Then
_pageSite.OnStatusChange(PROPPAGESTATUS.CLEAN)
End If
End Sub
' ...
Private Shared Function ContainsMultipleProjects(vsObjects As Object()) As Boolean
Debug.Assert(vsObjects IsNot Nothing)
If vsObjects IsNot Nothing AndAlso vsObjects.Length > 1 Then
Dim first As IVsHierarchy = GetProjectHierarchy(vsObjects(0))
For i As Integer = 1 To vsObjects.Length - 1
Dim current As IVsHierarchy = GetProjectHierarchy(vsObjects(i))
If current IsNot first Then
Return True
End If
Next
End If
Return False
End Function
' ...
Private Shared Function GetProjectHierarchy(vsObject As Object) As IVsHierarchy
Dim hierarchy As IVsHierarchy = Nothing
Dim itemId As UInteger
Dim vsCfgBrowsable As IVsCfgBrowseObject = TryCast(vsObject, IVsCfgBrowseObject)
If vsCfgBrowsable IsNot Nothing Then
ErrorHandler.ThrowOnFailure(vsCfgBrowsable.GetProjectItem(hierarchy, itemId))
Return hierarchy
End If
Dim vsBrowsable As IVsBrowseObject = TryCast(vsObject, IVsBrowseObject)
If vsBrowsable IsNot Nothing Then
ErrorHandler.ThrowOnFailure(vsBrowsable.GetProjectItem(hierarchy, itemId))
Return hierarchy
End If
Throw New NotSupportedException("Unsupported VS object type")
End Function
' ...
Private Shared Sub WriteProperty(vsObject As Object, propertyName As String, propertyValue As String)
Dim hierarchy As IVsHierarchy = GetProjectHierarchy(vsObject)
Dim buildStorage As IVsBuildPropertyStorage = TryCast(hierarchy, IVsBuildPropertyStorage)
If buildStorage Is Nothing Then
Debug.Fail("Unsupported VS object")
Exit Sub
End If
ErrorHandler.ThrowOnFailure(buildStorage.SetPropertyValue(_
propertyName, _
String.Empty, _
STORAGETYPE.PROJECT_FILE, _
propertyValue))
End Sub
' ...
Private Sub _SetObjects(cObjects As UInteger, ppunk() As Object) Implements IPropertyPage.SetObjects
If cObjects = 0 OrElse ppunk Is Nothing OrElse ppunk.Length = 0 Then
SetObjects(Nothing)
Exit Sub
End If
If ContainsMultipleProjects(ppunk) Then
SetObjects(Nothing)
Exit Sub
End If
Debug.Assert(cObjects = CUInt(ppunk.Length), "Huh?")
SetObjects(ppunk)
End Sub
' ...
Private Sub SetObjects(vsObjects As Object())
_dirtyProperties.Clear()
_objects = vsObjects
OnObjectsChanged(EventArgs.Empty)
End Sub
' ...
Private Sub WriteProperty(propertyName As String, propertyValue As String)
If _objects Is Nothing Then
Debug.Fail("Accessing property while not bound to project")
Exit Sub
End If
Debug.Assert(_objects.Length <> 0, "Should never have zero objects if collection is non-null")
For i As Integer = 0 To _objects.Length - 1
WriteProperty(_objects(i), propertyName, propertyValue)
Next
End Sub
End Class
パッケージを作成するのはかなり簡単でした。初期化ステップ中にRegisterProjectFactory
に電話することを忘れないでください。
Imports System
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.VisualStudio.Modeling.Shell
Imports Microsoft.VisualStudio.Shell
Imports Microsoft.VisualStudio.Shell.Interop
<ComVisible(True)>
<ProvideBindingPath()>
<Guid(Guids.MyCustomPackage)>
<PackageRegistration(_
UseManagedResourcesOnly:=True)>
<ProvideAutoLoad(UIContextGuids.SolutionExists)>
<ProvideProjectFactory(_
GetType(MyCustomProjectFactory), _
Nothing, _
Nothing, _
Nothing, _
Nothing, _
Nothing)>
<ProvideObject(_
GetType(MyCustomPropertyPageProvider))>
Public Class MyCustomPackage
Inherits Package
Protected Overrides Sub Initialize()
MyBase.Initialize()
Dim factory As New MyCustomProjectFactory(Me)
Try
Me.RegisterProjectFactory(factory)
Catch ex As ArgumentException
Debug.Fail(ex.Message, ex.ToString())
End Try
End Sub
End Class
私はMPF isn't designed for project sub-types以来、MPF ProjectFactory
クラスを使用していませんでした。代わりに、私はFlavoredProjectFactoryBase
から直接継承しました。
Imports System
Imports System.Diagnostics.CodeAnalysis
Imports System.Runtime.InteropServices
Imports Microsoft.VisualStudio.Shell.Flavor
<SuppressMessage(_
"Microsoft.Interoperability", _
"CA1405:ComVisibleTypeBaseTypesShouldBeComVisible", _
Justification:="Blame Microsoft? No other way around this")>
<ComVisible(True)>
<Guid(Guids.MyCustomProjectFactory)>
Public Class MyCustomProjectFactory
Inherits FlavoredProjectFactoryBase
Private ReadOnly _package As MyCustomPackage
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(package As MyCustomPackage)
If package Is Nothing Then
Throw New ArgumentNullException("package")
End If
_package = package
End Sub
Protected Overrides Function PreCreateForOuter(outerProjectIUnknown As IntPtr) As Object
Return New MyCustomProject(_package)
End Function
End Class
プロジェクトクラスは、カスタムプロパティページのGUIDをプロパティページGUIDのリストに追加する必要があります。あなたがXMLエディタでプロジェクトファイルを開いて、手動でビルドプロパティの一部を調整する必要があります。トラブルになっ物事が動作するように持っています誰のための
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics.CodeAnalysis
Imports System.Runtime.InteropServices
Imports Microsoft.VisualStudio
Imports Microsoft.VisualStudio.Shell.Flavor
Imports Microsoft.VisualStudio.Shell.Interop
<SuppressMessage(_
"Microsoft.Interoperability", _
"CA1405:ComVisibleTypeBaseTypesShouldBeComVisible", _
Justification:="Blame Microsoft? No other way around this")>
<ComVisible(True)>
<Guid(Guids.MyCustomProject)>
Public Class MyCustomProject
Inherits FlavoredProjectBase
Private Const GuidFormat As String = "B"
Private Shared ReadOnly PageSeparators As String() = {";"}
Private ReadOnly _package As MyCustomPackage
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(package As MyCustomPackage)
If package Is Nothing Then
Throw New ArgumentNullException("package")
End If
_package = package
End Sub
Protected Overrides Function GetProperty(itemId As UInteger, propId As Integer, ByRef [property] As Object) As Integer
If propId = CInt(__VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList) Then
ErrorHandler.ThrowOnFailure(MyBase.GetProperty(itemId, propId, [property]))
Dim pages As New HashSet(Of String)()
If [property] IsNot Nothing Then
For Each page As String In CStr([property]).Split(PageSeparators, StringSplitOptions.RemoveEmptyEntries)
Dim blah As Guid = Nothing
If Guid.TryParseExact(page, GuidFormat, blah) Then
pages.Add(page)
End If
Next
End If
pages.Add(Guids.MyCustomPropertyPageProviderGuid.ToString(GuidFormat))
[property] = String.Join(PageSeparators(0), pages)
Return VSConstants.S_OK
End If
Return MyBase.GetProperty(itemId, propId, [property])
End Function
Protected Overrides Sub SetInnerProject(innerIUnknown As IntPtr)
If MyBase.serviceProvider Is Nothing Then
MyBase.serviceProvider = _package
End If
MyBase.SetInnerProject(innerIUnknown)
End Sub
End Class
最後にもう一つのヒント。最低でもGeneratePkgDefFile
とIncludeAssemblyInVSIXContainer
をtrue
に設定する必要があります。