Exposing Project Objects
Visual Studio provides a dynamic environment where VSPackages can be extended to seamlessly contribute to the Visual Studio automation model. These new objects extend the automation model DTE object.
The DTE object is the main object in the Visual Studio automation model. It sits atop the Visual Studio automation object hierarchy.
The names of the VSPackage-specific objects are stored in the registry, and the code that creates and returns the object is part of your VSPackage implementation. The following process describes how VSPackage-specific objects are found.
The Visual Studio integrated development environment (IDE) starts.
It reads from the registry all value names under the Automation, AutomationEvents and AutomationProperties keys of all VSPackages, and stores these names in a table.
An automation consumer calls the GetObject method or IDispatch::GetIDsOfNames, and passes the name of an object, as shown below:
DTE.GetObject("VCProjects")
The IDE finds the string parameter in the table and loads the corresponding VSPackage.
The IDE calls the GetAutomationObject method by using a name such as "VCProjects" passed in the call.
The VSPackage creates and returns the IDispatch pointer of the object. If the object already exists, its reference count is incremented. In this example, the caller is passed back a pointer to a standard Projects collection object, the one implemented by Visual C++.
The object that is returned is the entry point for looking at the rest the automation objects of a project.
After you obtain a particular Projects object, use the Item method to obtain a Project object that corresponds to a Visual C++ project. After you obtain a standard Project object, call the Object object to get to a project-specific object. The Object object is where you return an object that extends the Project object by using your unique methods and properties. You can even create and return other objects.
The names of the automation objects you want to expose are stored as names of string values under the following registry key:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\<PkgGUID>\Automation]
Note
The text <PkgGUID> represents the actual GUID of your VSPackage.
The following code example and table contain typical registry entries.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\<PkgGUID>\Automation]
@=""
"FigProjects"="Figures project automation object"
Name |
Type |
Range |
Description |
---|---|---|---|
Default (@) |
REG_SZ |
null |
Unused. You can use this for documentation. |
FigProjects |
REG_SZ |
Name of your unique Projects collection object. |
Only the key name is relevant. You can use the data field for documentation. |
All VSPackages that provide new project types should provide automation objects. For example, Visual C++ creates VSPackage-specific objects that add-ins can then call through DTE.VCProjects or DTE.GetObject("VCProjects") to get the project objects of Visual C++ through the Projects collection. The Projects and its contained Project items are the Visual C++ implementation but follow the standard pattern. However, the consumer can get to Project.Object, which is unique for the project type, and can provide its own unique objects as well. A projects automation model can also have Project.CodeModel, which can be queried for a most-derived object (VCCodeModel).
The same goes for ProjectItem, which also surfaces ProjectItem.Object, and a FileCodeModel.
The Visual C++ VSPackage then creates the objects to pass back to the automation consumer.
To contribute a VSPackage-specific object for a project
Add the appropriate keys to the registry settings of your VSPackage.
Implement the code in the GetAutomationObject of the IVsPackage interface that returns an IDispatch object.
The following code example shows an implementation of the GetAutomationObject method.
Private Function GetAutomationObject(ByVal pszPropName As LPCOLESTR, ByVal ppIDispatch As IDispatch**) As STDMETHODIMP Implements CVsPackage.GetAutomationObject ExpectedPtrRet(ppIDispatch) *ppIDispatch = NULL If _wcsicmp(pszPropName, g_wszAutomationProjects) = 0 Then 'Is the requested name our Projects object? ' Gets our Projects object Return GetAutomationProjects(ppIDispatch) ElseIf _wcsicmp(pszPropName, g_wszAutomationProjectsEvents) = 0 Then 'Is the requested name our ProjectsEvents object? ' Gets our ProjectEvents object Return CAutomationEvents.GetAutomationEvents(ppIDispatch) ElseIf _wcsicmp(pszPropName, g_wszAutomationProjectItemsEvents) = 0 Then 'Is the requested name our ProjectsItemsEvents object? ' Gets our ProjectItemsEvents object Return CAutomationEvents.GetAutomationEvents(ppIDispatch) End If Return E_INVALIDARG End Function
STDMETHODIMP CVsPackage::GetAutomationObject( /* [in] */ LPCOLESTR pszPropName, /* [out] */ IDispatch ** ppIDispatch) { ExpectedPtrRet(ppIDispatch); *ppIDispatch = NULL; if (_wcsicmp(pszPropName, g_wszAutomationProjects) == 0) //Is the requested name our Projects object? { return GetAutomationProjects(ppIDispatch); // Gets our Projects object } else if (_wcsicmp(pszPropName, g_wszAutomationProjectsEvents) == 0) //Is the requested name our ProjectsEvents object? { return CAutomationEvents::GetAutomationEvents(ppIDispatch); // Gets our ProjectEvents object } else if (_wcsicmp(pszPropName, g_wszAutomationProjectItemsEvents) == 0) //Is the requested name our ProjectsItemsEvents object? { return CAutomationEvents::GetAutomationEvents(ppIDispatch); // Gets our ProjectItemsEvents object } return E_INVALIDARG; }
In the code, g_wszAutomationProjects is the name of your project collection. In this example, this is "FigProjects," the string registered under <PkgGUID>\Automation key (as shown in the registry entries). The GetAutomationProjects method creates an object that implements the Projects interface and returns an IDispatch pointer to the calling object, as shown in the following code example.
Private Function GetAutomationProjects(ByVal ppIDispatch As IDispatch**) As HRESULT Implements CVsPackage.GetAutomationProjects ExpectedPtrRet(ppIDispatch) *ppIDispatch = NULL If Not m_srpAutomationProjects Then Dim hr As HRESULT = CACProjects.CreateInstance(AddressOf m_srpAutomationProjects) IfFailRet(hr) ExpectedExprRet(m_srpAutomationProjects <> NULL) End If Return m_srpAutomationProjects.CopyTo(ppIDispatch) End Function
HRESULT CVsPackage::GetAutomationProjects(/* [out] */ IDispatch ** ppIDispatch) { ExpectedPtrRet(ppIDispatch); *ppIDispatch = NULL; if (!m_srpAutomationProjects) { HRESULT hr = CACProjects::CreateInstance(&m_srpAutomationProjects); IfFailRet(hr); ExpectedExprRet(m_srpAutomationProjects != NULL); } return m_srpAutomationProjects.CopyTo(ppIDispatch); }
You should choose a unique name for your automation object. Name conflicts are unpredictable, and collisions cause a conflicting object name to be arbitrarily thrown out if multiple VSPackages use the same name. To avoid such collisions, we recommend that you include your corporate name or some unique aspect of its product name in the name of the automation object.
The Projects collection object is the entry point for the remaining part of your project automation model. After you have created the appropriate code and registry entries that provide consumers with Projects collection objects, your implementation must provide remaining standard objects for the project model. For more information, see Project Modeling.