
annotation
About a year ago we published a series of articles on the development of plug-ins for Visual Studio in C # on our blog. Now we have reworked these materials, added new sections and bring to your attention a new version of the manual.
Creating extension modules (or plug-ins) for the Microsoft Visual Studio development environment at first may seem very simple. After all, there is excellent MSDN documentation, articles, examples, and many additional materials. But it may seem difficult when some actions will not give the result that is expected. And although this behavior is often typical for any programmer task, the topic of plug-in development is not fully disclosed.
We are developing the
PVS-Studio static code analyzer. And although the tool itself is intended for C ++ programmers, a considerable part of it is written in C #. When we started to develop our plugin, Visual Studio 2005 was considered the newest and most modern version of Visual Studio in the world. And although now that Visual Studio 2012 has already been released, some may say that Visual Studio 2005 is not entirely relevant, we still support this version in your tool. During the time that we supported different versions of Visual Studio and used different features of the environment, we have gained a lot of practical knowledge about how to create (and especially incorrectly!) Plug-ins correctly. There was no more strength in holding this knowledge. Therefore, we decided to issue them and publish them. After all, some solutions that now seem obvious were found only a few years later. And those problems that have long been resolved, may still torment some plugin developers.
The following questions will be considered:
- basic information on creating and debugging MSVS plug-ins, as well as support for these extension projects in a single code base for several versions of Visual Studio;
- overview of object automation model and MPF classes (Managed Package Framework);
- extensions of the development environment interface using the object automation model API (EnvDTE) and MPF classes (Managed Package Framework) with custom menus, toolbars, tool windows and settings dialogs;
- Review of the Visual Studio project model, interaction with custom project models using the example of the Atmel Studio environment implemented as a Visual Studio Isolated Shell
- collecting all the necessary data, such as the parameters and settings for compiling different configurations and platforms, using the Visual C ++ project model for working with an external preprocessor / compiler;
A more detailed and complete description of the topics covered in the article is available at the links to the official materials of the MSDN library and other third-party resources given at the end of each section.
')
Only development of plug-ins for Visual Studio 2005 and above will be considered. This limitation is due to the fact that PVS-Studio only supports systems with VS2005 and higher. Such a limitation in the development of PVS-Studio is caused by the appearance of a new API model in the Visual Studio 2005 environment, which is not compatible with previous versions of the environment extension API.
Creating, debugging and deploying Microsoft Visual Studio 2005/2008/2010/2012 expansion packs
In this section, we review the various methods for extending the functionality of the Visual Studio environment. Details will be considered the creation of extensions of the form of Visual Studio Extension Package (Visual Studio expansion pack), their debugging, registration and deployment on the end-user machine.
Creating and debugging VSPackage Visual Studio and Visual Studio Isolated Shell Extension Modules
There are many ways to extend the functionality of Microsoft Visual Studio. At the most basic level, you can automate simple routine actions with macros. For software automation of simple actions with UI environment objects, manipulations with menu items, etc. You can use the plug-in (add-in).
Extending the built-in editors of the environment is possible through the MEF (Managed Extensibility Framework) components (starting with version MSVS 2010). For the integration of large independent components into Visual Studio, extensions of the
Extension Package type (extension packages, also known as VSPackage) are best suited. At the same time, VSPackage allows you to combine the automation of the management of IDE components through
the automation object model with the extension of the environment through the MEF (Managed Extensibility Framework) and
Managed Package Framework classes (such as the Package). In fact, while Visual Studio itself provides only common interface components and services, standard modules, such as Visual C ++ or Visual C #, are implemented as extensions of the environment.
The first versions of the PVS-Studio plugin (more precisely 1.XX and 2.XX, when our product was also called Viva64), we released as an Add-In. From the PVS-Studio 3.00 version we remade it on VSPackage. The reason for the transition was that we were cramped in the Add-In and it was inconvenient to debug. In addition, I wanted to have my own icon on the Visual Studio screen saver!
VSPackage modules allow you to expand the automation model itself, providing opportunities for adding custom automation objects to it. Such objects are made available through the automation model for other extension modules, giving them programmatic access to third-party integrated user components. This, in particular, allows third-party developers to add support for new programming languages ​​and compilers to the environment through extensions, as well as to provide interfaces for automating these new components.
In addition to expanding directly into the Visual Studio environment itself, the VSPackage modules can also be used to add functionality to the isolated \ Integrated Shell Visual Studio wrappers. Visual Studio's isolated / integrated shell allows any third-party developer to re-use standard interface components and Visual Studio services (code editor, auto-completion system, etc.), adding support for their own project model and / or compilers to the environment. Such a distribution kit will not contain Microsoft proprietary language components (Visual Basic, Visual C ++, etc.), and can be installed by the end user even on a system without a pre-installed version of Visual Studio IDE.
The isolated Visual Studio shell will remain detached after installation even on a system with Visual Studio preinstalled, and the integrated shell will be merged with the pre-installed environment. If the developer of an isolated / integrated shell extends the automation model of Visual Studio, adding interfaces for its specific components, the developer of the VSPackage plugin will have access to these interfaces. As an example of Visual Studio Isolated Shell, you can take the environment for creating embedded systems Atmel Studio. Atmel Studio uses its own project model, which is an implementation of the standard Visual Studio model for MSBuild, and a variant of the gcc compiler.
VSPackage plug-in projects, creating an expansion pack
Consider creating a view plugin for the Visual Studio Package (VSPackage, an extension package). Unlike plug-ins (Add-In), developing an environment expansion pack will require the installation of the Microsoft Visual Studio SDK for the target version of the development environment. That is, to develop an expansion pack for each version of Visual Studio, you will need to install a separate SDK. When you create an extension pack for Visual Studio Isolated \ Integrated Shell, you need an SDK for the version of Visual Studio that this shell is based on.
In the future, we will consider the development of extensions for versions of the environment 2005, 2008, 2010 and 2012 and Visual Studio 2010 Isolated Shell (using the example of Atmel Studio). The installation of the Visual Studio SDK adds a project of the type Visual Studio Package to the standard environment templates (the Other Project Types -> Extensibility item). This template will generate the simplest MSBuild project for the extension module, allowing you to set the development language and stubs for several typical components (menu item, editor, custom window).
We will use the C # project (csproj) VSPackage, which is the MSBuild dynamic link library (dll) project. In our case, this is a managed assembly, which also contains several package bundle-specific XML assembly nodes, such as the VCST compiler and IncludeInVSIX for the latest versions of Visual Studio.
The main class of the Visual Studio extension package must be inherited from the
Microsoft.VisualStudio.Shell.Package class. This base class provides the managed wrappers for the interaction interfaces with the IDE required by the full Visual Studio expansion pack.
public sealed class MyPackage: Package { public MyPackage () {} ... }
The Package class provides the ability to override the base Initialize method. The Initialize method gets control at the moment of the expansion pack initialization for the current IDE session.
protected override void Initialize() { base.Initialize(); ... }
The module is initialized when it is first accessed, and can also be called automatically, for example, when the IDE is started, when the user enters the specified UI context (for example, opening a project), etc.
In general, it is very important to understand how the expansion module starts and how it ends. After all, it may be that the developer is trying to use some kind of Visual Studio functionality, which cannot be used at the moment. During the development of PVS-Studio, we had situations when the environment “beat us hands” for not understanding that at the end of Visual Studio it is impossible to show the message box with a question “in the forehead”.
Debugging Expansion Packs and Experimental Instance
The task of debugging a plug-in or extension for a development environment is not entirely trivial. After all, the environment itself is used both for development and for debugging such a module. Connecting such an unstable new module to the IDE can lead to instability in the development environment itself. Additional inconvenience will create the need to uninstall the module being developed from the development environment each time before debugging its new version, which often requires restarting the environment itself (since the IDE can block an already connected dll, which will have to be replaced with a new version for debugging).
It should be noted that debugging VSPackage is much more convenient than Add-In. This was one of the reasons for the change of the Add-In model used in PVS-Studio to VSPackage.
To solve these problems when developing and debugging VSPackage packages, you can use an experimental instance of Visual Studio. It can be started by adding a special parameter to the environment launch string:
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\ Common7\IDE\devenv.exe" /RootSuffix Exp
The experimental instance of the environment uses a separate independent branch in the system registry (experimental hive) to save the registration information of the installed components and environment settings. Any changes in the IDE settings, registration or modification of new components in the Experimental Hive branch will not affect the instance of the environment that is used directly for the development and debugging of the module (ie, in the basic basic version that runs by default).
Visual Studio SDK provides a special utility for creating or clearing experimental instances -
CreateExpInstance . The call to CreateExpInstance to create a new experimental branch is as follows:
CreateExpInstance.exe /Reset /VSInstance=10.0 /RootSuffix=PVSExp
Execution of this command will create a new experimental registry branch with the PVSExp suffix in the name for version 10 of the IDE (Visual Studio 2010), with a preliminary reset of all environment settings to default values. The path to the new branch in the system registry will look like this:
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0PVSExp
Although the Exp (and the corresponding registry branch) suffix is ​​used by default when debugging in the VSPackage template project, nothing prevents you from creating other experimental branches, respectively, with other name suffixes. To launch an instance of the environment in the previously created new experimental branch (containing PVSExp in the name), you need to run:
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\ Common7\IDE\devenv.exe" /RootSuffix PVSExp
The ability to create several experimental branches on one local machine can be useful, for example, for the simultaneous development of several expansion packs in environments isolated from each other.
After installing the SDK, a link will also be added to the program menu, allowing you to reset the settings of the experimental IDE instance to default values ​​(for example, Reset the Microsoft Visual Studio 2010 Experimental Instance).
When developing extensions for Isolated Shell, the problems described above with the “damage” of the development environment do not arise, therefore there is no need to use Experimental Instance. But, in any case, the sooner you understand how to work with the debugging environment, the less you will have problems with not understanding what, why and how it is loaded when developing a plug-in.
Register and Deploy Visual Studio Expansion Packs
Registration of an extension package requires registration of the package itself, as well as all components that it integrates into the IDE (for example, menu items, settings pages, custom windows, etc.). Registration is done through the creation of records corresponding to components in the main branch of the Visual Studio registry.
All information about the components required for registration is recorded in a special pkgdef file during the assembly of a VSPackage project based on the special attributes of the main module class (the Package subclass). The pkgdef file can also be generated manually using the
CreatePkgDef utility. This utility collects registration information about the module using the .NET method of reflection through the special attributes of the package subclass. Consider these attributes.
The
PackageRegistration attribute informs the registration utility that this class is a module extension of Visual Studio. After its detection, additional registration attributes will be searched.
[PackageRegistration(UseManagedResourcesOnly = true)]
The Guid attribute sets the unique identifier of the extension module, which will then be used to create a registration sub-branch in the system registry, in the Visual Studio branch.
[Guid("a0fcf0f3-577e-4c47-9847-5f152c16c02c")]
The
InstalledProductRegistration attribute allows you to add information to the Help -> About dialog and to splash the loading screen of the Visual Studio environment.
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
The
ProvideAutoLoad attribute allows
you to assign an automatic module initialization to activate the specified environment context UI. When the user enters this context, the module will be loaded and initialized automatically. Let us give an example of the purpose of initializing a module to open a solution file.
[ProvideAutoLoad("D2567162-F94F-4091-8798-A096E61B8B50")]
The GUID values ​​of identifiers for different IDE contexts can be found in the class Microsoft.VisualStudio.VSConstants.UICONTEXT.
The
ProvideMenuResource attribute specifies the resource IDs of user commands and menus for registering with the IDE.
[ProvideMenuResource("Menus.ctmenu", 1)]
The
DefaultRegistryRoot attribute specifies the path for recording registration information in the system registry. Beginning with Visual Studio 2010, this attribute can be omitted, and the corresponding registration information should be recorded in the VSIX container manifest. An example of using an attribute to register a module in Visual Studio 2008:
[DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]
Registration of other custom components, such as tool and document windows, editors, settings pages, etc., also requires the addition of corresponding attributes for a custom subclass of Package. We will look at these attributes as we look at the components themselves.
If it is necessary to add the user keys to the registry when registering the extension package, or if values ​​need to be added for already existing keys, it is possible to create custom registration attributes by inheriting from the
RegistrationAttribute abstract class
. [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] public class CustomRegistrationAttribute : RegistrationAttribute { }
The attribute-successor of the RegistrationAttribute will be required to override the Register and Unregister methods that are used to modify the registration information in the registry.
To add registration information to the registry, you can use the
RegPkg utility, which automatically registers all the components listed in the pkgdef file transferred to it to the registry branch specified via the / root argument. For example, the RegPkg call is automatically registered in Visual Studio projects for registering the module being developed in the experimental registry branch for ease of debugging. After adding all the registration information to the registry, you need to run Visual Studio (devenv.exe) with the / setup parameter to register new components already in the IDE itself.
Deploying the plug-in to the developer’s machine and to the end-user’s machine, Package Load Key
Before proceeding with the description of the deployment procedure, remember the important rule:
When creating a distribution kit with a plugin developed by you, be sure to test it each time on a machine without Visual Studio SDK to make sure that a regular user logs in correctly to the system.Now, when the first releases of PVS-Studio are long gone, we have no problems with this. However, at first, several unsuccessful versions got to the users.
Deploying the plug-in for Visual Studio 2005/2008 environments will require starting the regpkg for the pkgdef file with the IDE's main registry key, or adding all the keys from the pkgdef file to the registry manually. An example of a command to automatically add registration information from the pkgdef file to the registry (in one line):
RegPkg.exe /root:Software\Microsoft\VisualStudio\9.0Exp "/pkgdeffile:obj\Debug\PVS-Studio-vs2008.pkgdef" "C:\MyPackage\MyPackage.dll"
After adding registration information to the registry, you must start Visual Studio with the / setup parameter, usually this is the last step in the installation procedure of the new module.
Devenv.exe /setup
Running the environment with this key instructs Visual Studio to read the metadata about the resources of custom components from all available extension modules in order to display them correctly in the interface. Running devenv with this key does not open the IDE GUI window.
In PVS-Studio, we do without starting RegPkg, and manually add the necessary information to the registry during installation. This is done from those considerations so as not to depend on another third-party utility, but to fully control the installation process yourself. But we still use RegPkg when developing a plugin for ease of debugging.
VSIX packages
Starting with Visual Studio 2010, it has become possible to significantly simplify the deployment of VSPackage modules using
VSIX packages. The VSIX package is a standard OPC (Open Packaging Conventions) archive containing the plug-in binaries and all the supporting files needed for their deployment. This archive can be transferred to the standard utility VSIXInstaller.exe, which will automatically register the extensions contained in it.
VSIXInstaller.exe MyPackage.vsix
Using the VSIX installer, you can also remove the installed package with the / uninstall command, indicating the module's unique identifier GUID.
VSIXInstaller.exe /uninstall: 009084B1-6271-4621-A893-6D72F2B67A4D
The contents of the VSIX container are specified through a special vsixmanifest file, which must be added to the plug-in project. Vsixmanifest allows you to specify:
- module supported target version and edition of the Visual Studio environment for installation;
- Module GUID;
- components required for registration (VSPackage, MEF component, toolbox control, etc.);
- information about the installed module (description, license, version, etc.).
To include additional files from the project into the container, you must specify the IncludeInVSIX node for these files in the MSBuild project (you can also mark such files in the SolutionExplorer through the Properties window).
<Content Include="MyPackage.pdb"> <IncludeInVSIX>true</IncludeInVSIX> </Content>
In fact, the VSIX file is a full-fledged installer for the latest versions of Visual Studio extensions (20010 and 2012), allowing you to install the package using the “one-click” method. The publication of the VSIX package on the IDE extensions official site of the
Visual Studio Gallery will allow the user to install such a module through the Tools -> Extension Manager environment dialog.
VSIX allows you to deploy an expansion pack in both the usual edition of Visual Studio and in isolated \ integrated shells. If you are integrated into an isolated shell, you will need to specify a special identifier string for such a target environment instead of the Visual Studio version in the manifest file. For example, for the Atmel Studio 6.1 environment, this line would look like this: "AtmelStudio, 6.1". If your extension is based on interaction with common interfaces of the automation model (interfaces for interacting with a text editor, an abstract project tree, etc.), and does not use any specific interfaces (such as interfaces for interacting with Visual C ++ projects) , no one bothers you to specify in the supported versions at once both several editions of Visual Studio and several different versions of Isolated Shell. This, in turn, will allow you to get one common installer for a wide range of Visual Studio products.
Appeared in VS2010 installation in the form of VSIX greatly facilitated the user (and the developer) installation of extensions. And so much so that some plugin developers make only the installer for VS2010, just not to get involved with the development of the plug-in and installer for older versions of Visual Studio.
In practice, unfortunately, as is often the case in the programming world, problems are possible when using the VSIX installer together with the extension manager interface in Visual Studio 2010. In particular, in some cases binary files are not always deleted correctly, which blocks the work of both the VSIX installer and and a studio extension manager and forces you to find and delete these files manually. Therefore, you should use VSIX with caution, if possible, ensuring that before starting the installation, direct removal of files from the old version of the plugin being installed.
Package load key
Each VSPackage module loaded into Visual Studio must contain a unique PLK (Package Load Key) key. The PLK key is specified via the ProvideLoadKey attribute of the Package class for IDE 2005 and 2008 versions.
[ProvideLoadKey("Standard", "9.99", "MyPackage", "My Company", 100)]
Starting from Visual Studio 2010, the presence of PLK and, accordingly, the ProvideLoadKey attribute is not mandatory, but it can be used if the developed module targets several versions of the MSVS environment. To obtain a PLK key, you must register on the
Visual Studio Industry Partner portal, i.e. The PLK key ensures that only Microsoft-certified packages are loaded in the development environment. However, an exception is made for machines with the Visual Studio SDK installed. Together with the SDK, the Developer License Key is installed, which allows you to later load any extension module into the corresponding Visual Studio SDK environment, regardless of the validity of its PLK key.
And once again it is worth recalling the need to test a ready-made distribution on a machine without the Visual Studio SDK installed. The fact is that if you have the wrong PLK key set, then on the developer's machine it will be difficult to understand, since the expansion module will work.
Features registration extensions with support for multiple versions of Visual Studio
By default, the VSPackage template generates an extension project for the current, used during development, version of Visual Studio. However, this is not a necessary requirement, i.e. It is possible to develop extensions for one version of the environment using another version. It is worth remembering that when you automatically update the project file to a newer version via devenv / Upgrade, the target version of the IDE and, accordingly, the connected managed wrapper library APIs will remain unchanged, i.e. from a previous version of visual studio.
To change the target version of Visual Studio (or rather, to register the plugin in this version of the environment), you need to change the values ​​that are passed to the DefaultRegistryRoot attribute (for versions 2005 and 2008, starting with the version of Visual Studio 2010, this attribute is not needed), or change the target version in the file VSIX manifest (for versions after 2008).
VSIX support appeared only in Visual Studio 2010, so to build and debug a plug-in for earlier versions of Visual Studio 2010 (and newer versions), you will need to ensure that all the previously described registration steps are performed manually, without using the VSIX manifest. When changing the target version of the IDE, you should remember to change the versions of the wrapper libraries used in the managed plugin for COM interfaces.
Changing the target IDE version for a plugin affects the following attributes of the Package class:
- The InstalledProductRegistration attribute since Visual Studio 2010 does not support overloading a constructor with a signature (Boolean, String, String, String);
- The DefaultRegistryRoot and ProvideLoadKey attributes are optional for modules developed only for a version of Visual Studio no less than 10th (that is, starting from Visual Studio 2010), since similar values ​​are now specified in the VSIX manifest file;
Recommended links
- Msdn Experimental Build.
- Msdn How to: Register a VSPackage.
- Msdn VSIX Deployment.
- Msdn How to: Obtain a PLK for a VSPackage.
- MZ-Tools. Resources about Visual Studio .NET extensibility.
- Msdn Creating Add-ins and Wizards.
- Msdn Using a Custom Registration Attribute to Register an Extension.
- Msdn Shell (Integrated or Isolated).
Visual Studio Automation Object Model, EnvDTE and Visual Studio Shell Interop Interfaces
This section reviews the Visual Studio automation object model. The general structure of the model is considered, access to its interfaces using the top-level DTE / DTE2 objects is described, and examples of using some of its elements are given. It also discusses the use of model interfaces in multi-threaded applications and provides an implementation of multi-thread interaction mechanisms with COM interfaces in managed code.
Introduction
The Visual Studio development environment is built on the principles of automation and extensibility, allowing developers to integrate into themselves virtually any new elements and interact with both standard and custom components. To implement these tasks, Visual Studio users are provided with several mutually complementary tools. The most basic and versatile of which is the Visual Studio automation object model.
The object automation model is a series of libraries containing an extensive, well-structured set of APIs covering all aspects of IDE automation and most aspects of its extensibility. Despite the fact that this model, in comparison with other IDE extension tools, does not provide opportunities for interacting with some areas of Visual Studio (mainly, it concerns the extension of some IDE functionality), it is the most flexible and versatile tool.
IDE , . , Visual Studio, .
Visual Studio , , . DTE (Development Tools Environment). 1 .

1 — Visual Studio Automation Object Model ( )
:
VSPackage.
2 . 1 – EnvDTE Visual Studio Interop, Visual Studio, , , , .. 2- – . (late-bound properties), .. - . (.. Visual Studio ) , Visual C++ Visual Basic, . , .
, 1- Visual Studio, \ (Visual Studio Isolated\Integrated Shell). .
, , . , , .. -, Add-In VSPackage. , .
Microsoft.VisualStudio.Shell.Interop COM , Visual Studio managed . MPF (Managed Package Framework), , , VSPackage , . EnvDTE, VS Package , .
DTE/DTE2
Visual Studio . , -, , managed API EnvDTE. -, — DTE2.
Visual Studio . -, EnvDTE EnvDTE80, EnvDTE90, EnvDTE100 .. , EnvDTE, , , Solution Solution2. , . , DTE2 , DTE, .. dte2.Solution Solution, Solution2, .
, EnvDTE80, EnvDTE90, EnvDTE100 , EnvDTE . , , managed - COM , DTE, DTE2.
EnvDTE Visual Studio. 3 : Add-In, VSPackage MSVS .
Add-In
Add-In, DTE OnConnection,
IDTExtensibility , Add-In . OnConncetion IDE, , . :
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _dte2 = (DTE2)application; ... }
Add-In , IDE. , OnConnection
connectMode .
switch(connectMode) { case ext_ConnectMode.ext_cm_UISetup: ... break; case ext_ConnectMode.ext_cm_Startup: ... break; case ext_ConnectMode.ext_cm_AfterStartup: ... break; case ext_ConnectMode.ext_cm_CommandLine: ... break; }
, Add-in IDE ( startup Add-In manager), , . ext_ConnectMode.ext_cm_UISetup 1 , . UI ( ).
Add-In Visual Studio (ext_ConnectMode.ext_cm_Startup), OnConnect . DTE . IDTExtensibility OnStartupComplete.
public void OnStartupComplete(ref Array custom) { ... }
VSPackage
VSPackage DTE Visual Studio GetService Package:
DTE dte = MyPackage.GetService(typeof(DTE)) as DTE;
, GetService null , Visual Studio , «zombie» . DTE , . DTE Initialize, IVsShellPropertyEvents ( Package), OnShellPropertyChange.
DTE dte; uint cookie; protected override void Initialize() { base.Initialize(); IVsShell shellService = GetService(typeof(SVsShell)) as IVsShell; if (shellService != null) ErrorHandler.ThrowOnFailure( shellService.AdviseShellPropertyChanges(this,out cookie)); ... } public int OnShellPropertyChange(int propid, object var) {
, Visual Studio VSPackage IDE -. VS2005 VS2008, , DTE null. Visual Studio 2010 , Initialize() DTE . , DTE «» , DTE . IDE Visual Studio.
DTE - Visual Studio . ProgID COM , VisualStudio.DTE.10.0 Visual Studio 2010. IDE DTE .
DTE, devenv.exe CreateInstance. GUI Activate. DTE Visual Studio :
EnvDTE80.DTE2 dte2; dte2 = (EnvDTE80.DTE2) System.Runtime.InteropServices.Marshal.GetActiveObject( "VisualStudio.DTE.10.0");
IDE, GetActiveObject . DTE Visual Studio PID .
using EnvDTE80; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; [DllImport("ole32.dll")] private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc); [DllImport("ole32.dll")] private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot); public static DTE2 GetByID(int ID) {
DTE, IDE COM (ROT, Running Object Table) . DTE Visual Studio, :
Process Devenv; ...
, - ( ), CSharpProjects, DTE , GetObject:
Projects projects = (Projects)dte.GetObject("CSharpProjects");
GetObject Projects Project , , , .
Visual Studio
Visual Studio
TextDocument . C/C++ . TextDocument (
Document ), Visual Studio . Object Document. , , (.. ) IDE.
EnvDTE.TextDocument objTextDoc = (TextDocument)PVSStudio.DTE.ActiveDocument.Object("TextDocument");
TextSelection . Visual Studio, .. UI .
EnvDTE.TextDocument Doc = (TextDocument)PVSStudio.DTE.ActiveDocument.Object(string.Empty); Doc.Selection.SelectLine(); TextSelection Sel = Doc.Selection; int CurLine = Sel.TopPoint.Line; String Text = Sel.Text; Sel.Insert("test\r\n");
, 'test'.
TextDocument
EditPoint . TextSelection, , , . , , WordWrap Virtual Spaces. , read-only .
EditPoint, ( ).
objEditPt = objTextDoc.StartPoint.CreateEditPoint(); int lineNumber = objTextDoc.Selection.CurrentLine; objEditPt.LineDown(lineNumber - 1); EditPoint objEditPt2 = objTextDoc.StartPoint.CreateEditPoint(); objEditPt2.LineDown(lineNumber - 1); objEditPt2.CharRight(objEditPt2.LineLength); String line = objEditPt.GetText(objEditPt.LineLength); String newLine = line + "test"; objEditPt.ReplaceText(objEditPt2, newLine, (int)vsEPReplaceTextOptions.vsEPReplaceTextKeepMarkers);
VSPackage , . Managed Package Framework Package.GetGlobalService(). , EnvDTE, Package Visual Studio. , IDE Document, .
IVsUIShellOpenDocument . .
String path = "C:\Test\test.cpp"; IVsUIShellOpenDocument openDoc = Package.GetGlobalService(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; IVsWindowFrame frame; Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp; IVsUIHierarchy hier; uint itemid; Guid logicalView = VSConstants.LOGVIEWID_Code; if (ErrorHandler.Failed( openDoc.OpenDocumentViaProject(path, ref logicalView, out sp, out hier, out itemid, out frame)) || frame == null) { return; } object docData; frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData);
, , .
VsTextBuffer .
IVsTextManager , . , NavigateToLineAndColumn :
IVsTextManager mgr = Package.GetGlobalService(typeof(VsTextManagerClass)) as IVsTextManager; mgr.NavigateToLineAndColumn(buffer, ref logicalView, line, column, line, column);
DTE.Events. () IDE (CommandEvents, SolutionEvents), ( , , ..), . GetObject.
DTE , . , DTE.Events -. DTE - , .
Visual C++,
VCProjectEngineEvents Solution :
VCProjectEngineEvents m_ProjectItemsEvents = PVSStudio.DTE.Events.GetObject("VCProjectEngineEventsObject") as VCProjectEngineEvents; m_ProjectItemsEvents.ItemRemoved += new _dispVCProjectEngineEvents_ItemRemovedEventHandler( m_ProjectItemsEvents_ItemRemoved);
MDI
MDI Events.WindowEvents. ( EnvDTE.Window), .
:
WindowEvents WE = PVSStudio.DTE.Events.WindowEvents; WE.WindowActivated += new _dispWindowEvents_WindowActivatedEventHandler( Package.WE_WindowActivated);
WindowEvents:
WindowEvents WE = m_dte.Events.WindowEvents[MyPackage.DTE.ActiveWindow]; WE.WindowActivated += new _dispWindowEvents_WindowActivatedEventHandler( MyPackage.WE_WindowActivated);
IDE
. ( ). Events.CommandEvents. CommandEvents, MDI , IDE, .
:
CommandEvents CEvents = DTE.Events.CommandEvents; CEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(C_AfterExecute);
. GUID:ID, , (VSCT). Visual Studio , . ( Visual Studio 2010):
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\General] "EnableVSIPLogging"=dword:00000001
, ( , ) CTRL+SHIFT , . Guid CmdID. File.NewFile:
CommandEvents CEvents = DTE.Events.CommandEvents[ "{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 221]; CEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(C_AfterExecute);
, , .
void C_AfterExecute(string Guid, int ID, object CustomIn, object CustomOut) { ... }
( ) , ( - , ). IDE .
, - (VSPackage) PVS-Studio , - (, CommandEvents, WindowEvents ), Package. , , . , .NET. , DTE, -.
( VSPackage )
Microsoft.VisualStudio.Shell.Interop, Visual Studio. EnvDTE, VS Package (.. , Package Managed Package Framework). , , DTE. , , MPF.
IVsSolutionEvents , Package, Visual Studio, Visual Studio 2005, \ (Visual Studio isolated\integrated shell). , , , OnAfterCloseSolution, OnBeforeCloseProject, OnQueryCloseSolution .. For example:
public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) {
, IVsHierarchy, . , Visual Studio.
IVsSolutionLoadEvents , , Package Visual Studio 2010. , () ( OnBeforeLoadProjectBatch OnBeforeBackgroundSolutionLoadBegins), ( OnAfterBackgroundSolutionLoadComplete). , - , \. () , , runtime .
Visual Studio PVS-Studio VSPackage . Package , , (, ), - . , , .
, , , :
class MyPackage: Package, IVsSolutionLoadEvents, IVsSolutionEvents {
Visual Studio
, MDI ( VSPackage ), Visual Studio. Visual Studio 2012 (Dark Light), « ».
«» GetVSSysColorEx
IVsUIShell2 Visual Studio Interop. VSPackage .
IVsUIShell2 vsshell = this.GetService(typeof(SVsUIShell)) as IVsUIShell2;
__VSSYSCOLOREX __VSSYSCOLOREX3, GetVSSysColorEx, Visual Studio . , :
uint Win32Color; vsshell.GetVSSysColorEx((int)__VSSYSCOLOREX3.VSCOLOR_COMMANDBAR_MENU_BACKGROUND_GRADIENTBEGIN, out Win32Color); Color BackgroundGradient1 = ColorTranslator.FromWin32((int)Win32Color);
Color «» . , UI , , , , (Tools -> Options), , .
- IVsUIShell2 (, VSPackage ), Visual Studio, UI , . ,
Visual Studio. C# Visual Studio 2012 managed .
COM
PVS-Studio - Visual Studio API. , , . . ComException COM Interop.
Visual Studio -, . Visual Studio COM (Component Object Mode) . COM- ( -) - COM , STA (single-threaded apartment) . COM Apartment , . STA , . - STA . , STA, .
Apartment managed
.NET Framework Apartment COM. , COM managed COM , CLR (Common Language Runtime) apartment . Managed MTA (multi-threaded apartment, , , STA, ), STA, , , MTA. apartment Thread.SetApartmentState :
Thread t = new Thread(ThreadProc); t.SetApartmentState(ApartmentState.STA); ... t.Start();
Since apartment , managed STA STAThread:
[STAThread] static void Main(string[] args) {...}
COM managed
STA COM , , , apartment . , COM , .NET COM Interop System.Runtime.InteropServices.COMException («The message filter indicated that the application is busy»).
Visual Studio (VSPackage, Add-In) , STA UI ( event', ..). COM . EnvDTE (, , ), .
COM Exception PVS-Studio Visual Studio IDE. , , STA, , .
COM IMessageFilter. , HandleIncomingCall, RetryRejectedCall. , (, ). managed .
[ComImport()] [Guid("00000016-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IMessageFilter { [PreserveSig] int HandleInComingCall( int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] int RetryRejectedCall( IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] int MessagePending( IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } class MessageFilter : MarshalByRefObject, IDisposable, IMessageFilter { [DllImport("ole32.dll")] [PreserveSig] private static extern int CoRegisterMessageFilter( IMessageFilter lpMessageFilter, out IMessageFilter lplpMessageFilter); private IMessageFilter oldFilter; private const int SERVERCALL_ISHANDLED = 0; private const int PENDINGMSG_WAITNOPROCESS = 2; private const int SERVERCALL_RETRYLATER = 2; public MessageFilter() {
MessageFilter COM :
using (new MessageFilter()) {
- MSDN. Referencing Automation Assemblies and the DTE2 Object.
- MSDN. Functional Automation Groups.
- Microsoft Development Network. FAQ — Visual Studio .
- MZ-Tools. HOWTO: Use correctly the OnConnection method of a Visual Studio add-in.
- The Code Project. Understanding The COM Single-Threaded Apartment.
- MZ-Tools. HOWTO: Add an event handler from a Visual Studio add-in .
- Dr. eX's Blog. Using EnableVSIPLogging to identify menus and commands with VS 2005 + SP1.
- MSDN. Visual Studio Interop Assemblies.
Visual Studio
, Visual Studio - IDE . IDE UI .
Introduction
Visual Studio . . . UI , IDE, , , , .
- PVS-Studio IDE UI ( ) Visual Studio , , .
IDE , ( ) IDE, Command Window Immediate Window, devenv.exe /command .
- , File. Keyboard, Environment Options. Tools -> Customize -> Commands , (, ), , .
, . File -> New -> File, , Command Window:
>File.NewFile Mytext /t:"General\Text File" /e:"Source Code (text) Editor"
:
- (^)
- , /case (/c) /word (/w) /cw
command, :
devenv.exe /command "MyGroup.MyCommandName arg1 arg2"
alias:
>alias MyAlias File.NewFile MyFile
, IDE - PVS-Studio, /command, , , . , PVS-Studio.exe , , .. , stdout/strerr. , (, MSBuild, NMake GNU Make) C/C++ . , ( ) . PVS-Studio.exe, , , , .
PVS-Studio , (.. ) - Visual Studio /command, , PVS-Studio.CheckSolution. , Visual C++ (vcproj/vcxproj).
Visual Studio /command, . UI , , /. , Visual Studio UI . , , Microsoft MSBuild, Visual Studio.
Visual Studio /command (, Windows). , PVS-Studio Microsoft Team Foundation, , .. Team Foundation Windows . , , . Visual Studio , . , Visual Studio , . LocalSystem, Team Foundation. , Visual Studio /command, . , . , Visual Studio LocalSystem psexec
PSTools .
VSPackage, Vsct
IDE VSPackage (Visual Studio Command Table, vsct ). — XML, VSCT- cto- (command table output). CTO - IDE. VCST IDE . VSCT Visual Studio 2005, IDE CTC (command table compiler) , .
vsct — CommandID, , . ( ), ..
VSCT . CommandTable - Commands, , , , .. Commands Package , . - Symbols VSCT . - KeyBindings .
<CommandTable"http://schemas.microsoft.com/VisualStudio/2005-10- 18/CommandTable"> <Extern href="stdidcmd.h"/> <Extern href="vsshlids.h"/> <Commands> <Groups> ... </Groups> <Bitmaps> ... </Bitmaps> </Commands> <Commands package="guidMyPackage"> <Menus> ... </Menus> <Buttons> ... </Buttons> </Commands> <KeyBindings> <KeyBinding guid="guidMyPackage" id="cmdidMyCommand1" editor="guidVSStd97" key1="221" mod1="Alt" /> </KeyBindings> <Symbols> <GuidSymbol name="guidMyPackage" value="{B837A59E-5BF0-4190-B8FC- FDC35BE5C342}" /> <GuidSymbol name="guidMyPackageCmdSet" value="{CC8B1E36-FE6B-48C1- B9A9-2CC0EAB4E71F}"> <IDSymbol name="cmdidMyCommand1" value="0x0101" /> </GuidSymbol> </Symbols> </CommandTable>
Buttons IDE , .
<Button guid="guidMyPackageCmdSet" id="cmdidMyCommand1" priority="0x0102" type="Button"> <Parent guid="guidMyPackageCmdSet" id="MyTopLevelMenuGroup" /> <Icon guid="guidMyPackageCmdSet" id="bmpMyCommand1" /> <CommandFlag>Pict</CommandFlag> <CommandFlag>TextOnly</CommandFlag> <CommandFlag>IconAndText</CommandFlag> <CommandFlag>DefaultDisabled</CommandFlag> <Strings> <ButtonText>My &Command 1</ButtonText> </Strings> </Button>
Menus UI , Groups. , Menu, .
<Menu guid=" guidMyPackageCmdSet" id="SubMenu1" priority="0x0000" type="Menu"> <Parent guid="guidMyPackageCmdSet" id="MyTopLevelMenuGroup"/> <Strings> <ButtonText>Sub Menu 1</ButtonText> </Strings> </Menu> <Menu guid="guidMyPackageCmdSet" id="MyToolBar1" priority="0x0010" type="Toolbar"> </Menu>
Groups .
<Group guid="guidMyPackageCmdSet" id="MySubGroup1" priority="0x0020"> <Parent guid="guidMyPackageCmdSet" id="MyGroup1" /> </Group>
vsct MSBuild VSPackage VSCT csproj ( SDK VSPackage vsct ):
<ItemGroup> <VSCTCompile Include="TopLevelMenu.vsct"> <ResourceName>Menus.ctmenu</ResourceName> </VSCTCompile> </ItemGroup>
Visual Studio, parent. , Solution Explorer:
<Group guid="guidMyCmdSet" id="ProjectNodeContextMenuGroup" priority="0x07A0"> <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE" /> </Group>
, IDM_VS_CTXT_PROJNODE.
, Visual Studio.
ProvideMenuResource Package:
[ProvideMenuResource("Menus.ctmenu", 1)] ... public sealed class MyPackage : Package
, VSCT , ,
IMenuCommandService . GetService Package:
OleMenuCommandService MCS = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
( vsct ):
EventHandler eh = new EventHandler(CMDHandler); CommandID menuCommandID = new CommandID(guidCommand1CmdSet, id);
EventArgs OleMenuCmdEventArgs:
void CMDHandler(object sender, EventArgs e) { OleMenuCmdEventArgs eventArgs = (OleMenuCmdEventArgs)e; if (eventArgs.InValue != null) param = eventArgs.InValue.ToString(); ... }
EnvDTE.DTE
EnvDTE.DTE (, , ) dte.Commands dte.ExecuteCommand.
, IDE , VSCT, VSPackage, - Visual Studio Add-In.
DTE ,
DTE.Commands . Commands.AddNamedCommand IDE ( Add-In ):
dte.Commands.AddNamedCommand(add_in, "MyCommand", "My Command", "My Tooltip", true);
IDE , . Add-In ( , Visual Studio). OnConnection Add-In ( ), , UI IDE.
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { switch(connectMode) { case ext_ConnectMode.ext_cm_UISetup: ... break; ... }
}
EnvDTE.Command IDE. . VSPackage, Add-In . EnvDTE.Command MyCommand1 « » :
EnvDTE.Command MyCommand1 = MyPackage.DTE.Commands.Item("MyGroup.MyCommand1", -1); MyCommand1.Bindings = new object[1] { "Global::Alt+1" };
MyGroup.MyCommand1 Keyboard, Environment.
, Visual Studio IDE. Commands.AddCommandBar UI , , , , .
CommandBar MyToolbar = dte.Commands.AddCommandBar("MyToolbar1", vsCommandBarType.vsCommandBarTypeToolbar) as CommandBar; CommandBar MyMenu = dte.Commands.AddCommandBar("MyMenu1", vsCommandBarType.vsCommandBarTypeMenu) as CommandBar; CommandBarButton MyButton1 = MyCommand1.AddControl(MyToolbar) as CommandBarButton; MyButton1.Caption = "My Command 1";
IDE Delete Command/ CommandBar:
MyCommand1.Delete();
Add-In , .. IDE, OnDisconnect . , / , , , DTE , . Add-Inn DTE , EnvDTE.
( , ) ExecuteCommand. MyCommand1:
MyPackage.DTE.ExecuteCommand("MyGroup.MyCommand1", args);
Add-In IDTCommandTarget Exec:
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { handled = false; if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { if(commandName == "MyAddin1.Connect.MyCommand1") { ... handled = true; return; } } }
- MSDN. Visual Studio Commands and Switches.
- MSDN. Visual Studio Command Table (.Vsct) Files.
- MSDN. Designing XML Command Table (.Vsct) Files.
- MSDN. Walkthrough: Adding a Toolbar to the IDE.
- MSDN. How VSPackages Add User Interface Elements to the IDE.
- MZ-Tools. HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in.
- MSDN. How to: Create Toolbars for Tool Windows.
Visual Studio
Visual Studio . VSPackage Add-In, , .
Introduction
(tool window) — MDI (Multiple Document Interface) Visual Studio . Solution Explorer Error List . ,
.
- PVS-Studio IDE , (PVS-Studio Output Window). , . PVS-Studio Visual Studio (PVS-Studio -> Show PVS-Studio Output Window) .
IDE (single instance window), . , , . IDE Multi-Instance (.. , ) . IDE (.. ). .
IDE VSPackage Add-In ( ), .
Visual Studio SDK VSPackage -. , Visual Studio.
, VSPackage
Visual Studio. pkgdef , . pkgdef Package.
VSPackage
ProvideToolWindow Package:
[ProvideToolWindow(typeof(MyWindowPane), Orientation = ToolWindowOrientation.Right, Style = VsDockStyle.Tabbed, Window = Microsoft.VisualStudio.Shell.Interop.ToolWindowGuids.Outputwindow, MultiInstances = false, Transient = true, Width = 500, Height = 250, PositionX = 300, PositionY = 300)]
. Typeof (ToolWindowPane). MultiInstances Multi-Instance , .. . Orientaton, Size Style . , IDE . Transient , Visual Studio , IDE.
, VSPackage ( ) Package, . , PVS-Studio , ( ) Visual Studio, , ProvideToolWindow Transient = true. , , IDE, , .
ProvideToolWindowVisibility , :
[ProvideToolWindowVisibility(typeof(MyWindowPane), /*UICONTEXT_SolutionExists*/"f1536ef8-92ec-443c-9ed7-fdadf150da82")]
UI «Solution Exists»., , .
VSPackage
FindToolWindow Package. toolwindow , ( single-instance ). single-instance :
private void ShowMyWindow(object sender, EventArgs e) { ToolWindowPane MyWindow = this.FindToolWindow(typeof(MyToolWindow), 0, true); if ((null == MyWindow) || (null == MyWindow.Frame)) { throw new NotSupportedException(Resources.CanNotCreateWindow); } IVsWindowFrame windowFrame = (IVsWindowFrame) MyWindow.Frame; ErrorHandler.ThrowOnFailure(windowFrame.Show()); }
, , . FindToolWindow bool , , .
Multi-Instance
CreateToolWindow , . toolwindow :
private void CreateMyWindow(object sender, EventArgs e) { for (int i = 0; ; i++) {
, FindToolWindow 3- false, .. , .
single-instance , , . - , SetFramePos IVsWindowFrame:
Guid gd = Guid.Empty; windowFrame.SetFramePos(VSSETFRAMEPOS.SFP_fDockBottom, ref gd, 20, 20, 200, 200);
, SetFramePos() Show().
Add-In
Add-In EndDTE Window2:
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; EnvDTE80.Windows2 window; AddIn add_in; object ctlobj = null; Window myWindow;
, MyToolWindowControl.MyUserControl . MyToolWindowControl.MyUserControl assembly, add-in, , COM (, Register for COM interop ). MyUserControl UserControl.
VSPackage
- . (docking), . (pane) , . WinForms WPF , OnShow, OnMove ..
, , , IDE —
ToolWindowPane :
[Guid("870ab1d8-b434-4e86-a479-e49b3c6797f0")] public class MyToolWindow : ToolWindowPane { public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; ... }
}
Guid . , Guid. ToolWindowPane .
ToolWindowPane . WinForms WPF .
Visual Studio 2008 WinForms , WPF WPF Interoperability ElementHost. Visual Studio 2010, WPF, WinForms .
WinForms Window ToolWindowPane:
public MyUserControl control; public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; this.control = new MyUserControl(); } public override IWin32Window Window { get { return (IWin32Window)control; } }
MyUserControl System.Windows.Forms.UserControl, . WPF WPF ElementHost.
Visual Studio 2010 WPF . WPF Content :
public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; base.Content = new MyWPFUserControl(); }
, . WPF base.Content Window .
Output Window - PVS-Studio , open-source
SourceGrid , . ADO.NET System.Data.Datatable, . 4.00 PVS-Studio IDE Error List, , , . , , Error List, real grid , 1-2 . IDE. , , Chromium LLVM, ( , ) .
PVS-Studio , , . , «» 1-2 , . Datatable SQL , .
( ToolWindowPane) IDE.
IVsWindowFrameNotify3. :
public sealed class WindowStatus: IVsWindowFrameNotify3 {
, WindowStatus , , , .. . OnToolWindowCreated - ToolWindowPane:
public class MyToolWindow: ToolWindowPane { public override void OnToolWindowCreated() { base.OnToolWindowCreated();
IVsWindowFrameNotify3.
OnShow - , / , , . fShow,
__FRAMESHOW.OnClose , IDE pgrfSaveOptions, (
__FRAMECLOSE ).
OnDockableChange docking . fDockable , - , .
OnMove OnSize / .
- MSDN. Kinds of Windows.
- MSDN. Tool Windows.
- MSDN. Tool Window Essentials.
- MSDN. Tool Window Walkthroughs.
- MSDN. Arranging and Using Windows in Visual Studio.
- MZ-Tools. HOWTO: Understanding toolwindow states in Visual Studio.
Visual Studio
Visual Studio . IDE , . Visual Studio, .
Introduction
Visual Studio . IDE Tools -> Options. Visual Studio . Options . , Visual Basic : «Text Editor, Basic».
- IDE . MPF (Managed Package Framework, VSPackage ). Visual Studio -, , .
- Visual Studio Tools -> Options. UI IDE. , IDE - ( MPF).
MPF
Managed Package Framework
DialogPage . , Tools -> Options, .
, , Visual Studio (VSPackage)
ProvideOptionPage Package.
[ProvideOptionPageAttribute(typeof(OptionsPageRegistration), "MyPackage", "MyOptionsPage", 113, 114, true)]
IDE . pkgdef , . :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<VsVersion>\ ToolsOptionsPages
<VsVersion> — Visual Studio, 10.0. ProvideOptionPage. , - Visual Studio , , . Visual Studio 2010 VSPackage VSIX , VSIX . IDE , standalone .
bool . , EnvDTE, plug-in . ( ) :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<Version\Packages\ <PackageGUID>\Automation HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<Version>\ AutomationProperties
ProvideProfile , IProfileManager, IDE .
MPF DialogPage
DialogPage - (public properties). :
namespace MyPackage { class MyOptionsPage : DialogPage { bool myOption = true; public bool MyOption { get { return this. myOption; } set { this. myOption = value; } } } }
DialogPage PropertyGrid, -. , PropertyGrid . (, DPI) Visual Studio.
Window DialogPage:
[BrowsableAttribute(false)] protected override IWin32Window Window { get { return MyUserControl; } }
IWin32Window, . , Visual Studio , .. . Since , Windows Forms, handle, , UserControl.
AutomationObject , DialogPage, , IDE . AutomationObject DialogPage, - , . . DialogPage.
SaveSettingsToStorage ( , LoadSettingsFromStorage ).
public override void SaveSettingsToStorage() { ... }
, , XML Tools -> Import/Export Settings SaveSettingsToXml, .
, Visual Studio IDE . PropertyGrid , - , . (, IDE ), , , DPI .. , .
- PVS-Studio xml-, , IDE, \ . Visual Studio PVS-Studio, . , - Visual Studio (, ) .
xml Add-In
IDE XML AddIn. , System.Windows.Forms.UserControl. Add-In , , . addin , . xml Add-In , .
<?xml version="1.0" encoding="UTF-16" standalone="no"?> <Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility"> <HostApplication> <Name>Microsoft Visual Studio Macros</Name> <Version>10.0</Version> </HostApplication> <HostApplication> <Name>Microsoft Visual Studio</Name> <Version>10.0</Version> </HostApplication> <Addin> <FriendlyName>My Add in</FriendlyName> <Description>My Addin 1</Description> <Assembly>c:\MyAddIn1\MyAddin1.dll</Assembly> <FullClassName>MyAddin1.Connect</FullClassName> <LoadBehavior>0</LoadBehavior> <CommandPreload>1</CommandPreload> <CommandLineSafe>0</CommandLineSafe> </Addin> <ToolsOptionsPage> <Category Name="MyAddIn1"> <SubCategory Name="My Tools Options Page"> <Assembly> c:\MyAddIn1\MyAddin1.dll</Assembly> <FullClassName>MyAddin1.UserControl1</FullClassName> </SubCategory> </Category> </ToolsOptionsPage> </Extensibility>
<ToolsOptionsPage>. <Assembly> , , . <FullClassName> Namespace.ClassName. <Category> <SubCategory> Tools -> Options, . <Category> , . , MyAddin1.UserControl1 , .
Visual Studio Options. Managed Package Framework, xml addin, , , . Visual Studio addin . addin , Environment -> Add-In/Macross Security. , MPF, , , , IDE .
Visual Studio Tools -> Options, Dynamic Help Fonts and Colors ( API). , , .
get_Properties EnvDTE.DTE:
Properties propertiesList = PVSStudio.DTE.get_Properties("MyPackage", "MyOptionsPage");
. :
Property MyProp1 = propertiesList.Item("MyOption1");
MyProp1.Value.
Options ShowOptionPage MPF Package:
MyPackage.ShowOptionPage(typeof(MyOptionsPage));
( typeof) DialogPage. , VSPackage (, IDE , ), GUID , :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\ ToolsOptionsPages\<OptionsPageNme>\
<OptionsPageName> — Tools -> Options. IDE TextEditor -> General IMenuCommandService:
string targetGUID = "734A5DE2-DEBA-11d0-A6D0-00C04FB67F6A"; var command = new CommandID(VSConstants.GUID_VSStandardCommandSet97, VSConstants.cmdidToolsOptions); var mcs = GetService(typeof(IMenuCommandService)) as MenuCommandService; mcs.GlobalInvoke(command, targetGUID);
, Tools.Options. ExecuteCommand EnvDTE.DTE:
dte.ExecuteCommand("Tools.Options", "734A5DE2-DEBA-11d0-A6D0-00C04FB67F6A").
- MSDN. Options Pages.
- MSDN. State Persistence and the Visual Studio IDE.
- MSDN. User Settings and Options.
- MSDN. Registering Custom Options Pages .
- MSDN. Providing Automation for VSPackages.
Visual Studio
Visual Studio Visual C++ (VCProject). . () (Visual Studio Isolated Shell) Atmel Studio
Introduction
Visual Studio , , , MSVS- .
late-bound VCProjects. Visual C++ Visual Studio, Visual C++ (vcrpoj/vcxproj) . Visual C++ COM , VCProjectEngine.dll, Visual Studio. , Visual Studio .
Visual Studio - , , , , .. MSVS . , , Project. Visual C++ :
Projects |- Project -- Object(unique for the project type) |- ProjectItems (a collection of ProjectItem) |- ProjectItem (single object) -- ProjectItems (another collection) |- Object(unique for the project type)
Projects Project. Project , .. , . , , . Project.Object. , Visual C++ VCProject, Atmel Studio , , AvrGCCNode:
Project proj; ... VCProject vcproj = proj.Object as VCProject; AvrGCCNode AvrGccProject = proj.Object as AvrGCCNode;
Projects IDE solution dte.Solution.Projects DTE.GetObject. , Visual C++:
Projects vcprojs = m_dte.GetObject("VCProjects") as Projects;
:
Projects AllProjs = PVSStudio.DTE.Solution.Projects;
ProjectItems ProjectItem. Project, ProjectItem , ProjectItems ( ProjectItem.ProjectItems) Project. ProjectItem.Object. , Visual C++ VCFile, Atmel Studio — AvrGccFileNode:
ProjectItem projectItem; ... VCFile file = projectItem.Object as VCFile; AvrGccFileNode file = projectItem.Object as AvrGccFileNode;
, , :
Project proj = projectItem.Object as Project;
Solution
Solution
IVsHierarchy . , , . DWORD VSITEMID. .
VsShellUtilities.GetHierarchy:
public static IVsHierarchy ToHierarchy(EnvDTE.Project project) { System.IServiceProvider serviceProvider = new ServiceProvider(project.DTE as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); Guid guid = GetProjectGuid(serviceProvider, project); if (guid == Guid.Empty) return null; return VsShellUtilities.GetHierarchy(serviceProvider, guid); }
GUID . GetProjectGuid, :
private static Guid GetProjectGuid(System.IServiceProvider serviceProvider, Project project) { if (ProjectUnloaded(project)) return Guid.Empty; IVsSolution solution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution; IVsHierarchy hierarchy; solution.GetProjectOfUniqueName(project.FullName, out hierarchy); if (hierarchy != null) { Guid projectGuid; ErrorHandler.ThrowOnFailure( hierarchy.GetGuidProperty( VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, out projectGuid)); if (projectGuid != null) { return projectGuid; } } return Guid.Empty; }
IEnumHierarchies , , solution.
GetProjectEnum . Visual C++ Solution :
IVsSolution solution = PVSStudio._IVsSolution; if (null != solution) { IEnumHierarchies penum; Guid nullGuid = Guid.Empty; Guid vsppProjectGuid = new Guid("8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942");
GetProjectEnum GUID . GUID Visual Studio/MSBuild
. penum.Next() ( rgelt). , , , . XML .
IDE PVS-Studio, , , , .. , GUID , , . , VCProject, Android. , , .. API VCProject (, OpenMP). , , , . , ( ) IDE, .
IVsHierarchy , Solution hierarchy.
GetProperty , :
EnumHierarchyItemsFlat(VSConstants.VSITEMID_ROOT, MyProjectHierarchy, 0, true); ... public void EnumHierarchyItemsFlat(uint itemid, IVsHierarchy hierarchy, int recursionLevel, bool visibleNodesOnly) { if (hierarchy == null) return; int hr; object pVar; hr = hierarchy.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_ExtObject, out pVar); ProjectItem projectItem = pVar as ProjectItem; if (projectItem != null) { ... } recursionLevel++;
ProjectItem Visual ++ ( ) Object, .
Solution
DTE.Solution.Projects:
if (m_DTE.Solution.Projects != null) { try { foreach (object prj in m_DTE.Solution.Projects) { EnvDTE.Project proj = prj as EnvDTE.Project; if (proj != null) WalkSolutionFolders(proj); } } }
, Solution - (Solution Folders). Project :
public void WalkSolutionFolders(Project prj) { VCProject vcprj = prj.Object as VCProject; if (vcprj != null && prj.Kind.Equals(VCCProjectTypeGUID)) { if (!ProjectExcludedFromBuild(prj)) { IVsHierarchy projectHierarchy = ToHierarchy(prj); EnumHierarchyItemsFlat(VSConstants.VSITEMID_ROOT, projectHierarchy, 0, false); } } else if (prj.ProjectItems != null) { foreach (ProjectItem item in prj.ProjectItems) { Project nextlevelprj = item.Object as Project; if (nextlevelprj != null && !ProjectUnloaded(nextlevelprj)) WalkSolutionFolders(nextlevelprj); } } }
, , .. :
public bool ProjectExcludedFromBuild(Project project) { if (project.UniqueName.Equals("<MiscFiles>", StringComparison.InvariantCultureIgnoreCase)) return true; Solution2 solution = m_DTE.Solution as Solution2; SolutionBuild2 solutionBuild = (SolutionBuild2)solution.SolutionBuild; SolutionContexts projectContexts = solutionBuild.ActiveConfiguration.SolutionContexts;
, Solution Explorer, DTE.SelectedItems.
foreach (SelectedItem item in items) { VCProject vcproj = null; if (item.Project != null) { vcproj = item.Project.Object as VCProject; if (vcproj != null && item.Project.Kind.Equals("{" + VSProjectTypes.VCpp + "}")) { IVsHierarchy projectHierarchy = ToHierarchy(item.Project); PatternsForActiveConfigurations.Clear(); EnumHierarchyItemsFlat(VSConstants.VSITEMID_ROOT, projectHierarchy, 0, false, files, showProgressDialog); } else if (item.Project.ProjectItems != null) {
Visual C++.
, Visual Studio, EnvDTE . — Microsoft Visual C++, Microsoft.VisualStudio.VCProjectEngine.
Visual C++ Visual Studio, , . , Visual C++, Microsoft.VisualStudio.VCProjectEngine.dll, .
Visual C++ ( , , - , ..) /++ xml (vcproj/vcxproj). Visual Studio (Property Pages).
(, Debug Release) (Win32, x64, IA64 ..). , ( ). , ExcludedFromBuild, cpp .
Visual C++
VCConfiguration ( )
VCFileConfiguration ( ). ProjectItem, Solution .
ProjectItem item; VCFile vcfile = item.Object as VCFile; Project project = item.ContainingProject; String pattern = "Release|x64"; if (String.IsNullOrEmpty(pattern)) return null; VCFileConfiguration fileconfig = null; IVCCollection fileCfgs = (IVCCollection)vcfile.FileConfigurations; fileconfig = fileCfgs.Item(pattern) as VCFileConfiguration; if (fileconfig == null) if (fileCfgs.Count == 1) fileconfig = (VCFileConfiguration)fileCfgs.Item(0);
VCFile (/C++ ) Item(), pattern ( ) . Pattern . ( IDE) .
ConfigurationManager cm = project.ConfigurationManager; Configuration conf = cm.ActiveConfiguration; String platformName = conf.PlatformName; String configName = conf.ConfigurationName; String pattern = configName + "|" + platformName; return pattern;
ActiveConfiguration , .. IDE - PVS-Studio. , , Visual Studio . , . , COM , , EnvDTE , .
, :
VCConfiguration cfg=(VCConfiguration)fileconfig.ProjectConfiguration;
General, , VCConfiguration.Tools VCFileConfiguration.Tool ( ).
, , , C++
VCCLCompilerTool :
ct = ((IVCCollection)cfg.Tools).Item("VCCLCompilerTool") as VCCLCompilerTool; ctf = fileconfig.Tool as VCCLCompilerTool;
AdditionalOptions , Evaluate.
String ct_add = fileconfig.Evaluate(ct.AdditionalOptions); String ctf_add = fileconfig.Evaluate(ctf.AdditionalOptions);
Property Sheets
(property sheets) XML props, (.. , , ). Property sheets , .. , (vcproj/vcxproj), props .
(Property sheets) Visual C++
VCPropertySheet. VCPropertySheet VCConfiguration. PropertySheets:
IVCCollection PSheets_all = fileconfig.PropertySheets;
PropertySheets VCPropertySheet . :
private void ProcessAllPropertySheets(VCConfiguration cfg, IVCCollection PSheets) { foreach (VCPropertySheet propertySheet in PSheets) { VCCLCompilerTool ctPS = (VCCLCompilerTool)((IVCCollection)propertySheet.Tools).Item( "VCCLCompilerTool"); if (ctPS != null) { ... IVCCollection InherPSS = propertySheet.PropertySheets; if (InherPSS != null) if (InherPSS.Count != 0) ProcessAllPropertySheets(cfg, InherPSS); } } }
VCCLCompilerTool ( ) PropertySheet . , , , .
VCPropertySheet , Evaluate . , , , props . , MSBuild, 4 , vcxproj Visual Studio 2010. MSBuildThisFileDirectory, , , cfg.Evaluate vcxproj , props , .
Visual C++ . props , . , MSVC props . props , . , CharacterSet Property Sheets props , (Unicode, _Unicode). , , props , , , API . , .
Atmel Studio,
Visual Studio C/C++ Microsoft Visual C++, Visual Studio. Visual Studio , ( - VSPackage). , , , , Visual C++, .
, embedded Atmel Studio. , – Atmel Studio, Visual Studio? Atmel Studio Visual Studio (Isolated Shell). , , , Visual Studio, . Visual Studio, isolated shell , .
Atmel Visual Studio. , Visual C++, , Atmel Studio. , , AvrGCC.dll, AvrProjectManagement.dll Atmel.Studio.Toolchain.Interfaces.dll, , Atmel Studio Extension Developer's Kit (XDK).
Atmel Studio cproj, , MSBuild (, , Visual Studio). Atmel Studio C/C++ GCC.
Atmel Studio 2 : C C++ . , GUID , .
Atmel 2 – GNU C compiler GNU C++ Compiler, . , C C , C++ C C++ . , , .. «» 2- !
ProjectToolchainOptions.
ProjectItem item; ... AvrGccFileNode file = item.Object as AvrGccFileNode; AvrGCCNode project = file.ProjectMgr as AvrGCCNode; AvrProjectConfigProperties ActiveProps = project.ConfigurationManager.GetActiveConfigProperties(); ProjectToolchainOptions ToolChainOptions = ActiveProps.ToolchainOptions; if (ToolChainOptions.CppCompiler != null)
CompilerOptions ( CppCompilerOptions CCompilerOptions). , Include :
CompilerOptions options; ... List<String> Includes = options. IncludePaths;
, (.. C C++ ). , Include :
List<String> SystemIncludes = options. DefaultIncludePaths;
Dictionary<String, IList<String>> OtherProperties. , () .
, MSBuild (, , ), , CommandLine ( , VCProjectEngine!):
String RawCommandLine = this.compilerOptions.CommandLine;
General , , Include .
, , , , , , MSBuild . GetAllProjectProperties AvrGCCNode:
AvrGCCNode project; ... Dictionary<string, string> MSBuildProps = new Dictionary<string, string>(); project.GetAllProjectProperties().ForEach(x =>MSBuildProps.Add(x.Key, x.Value));
MSBuildProps.
, – , . AvrFileNodeProperties:
AvrGccFileNode file; ... AvrFileNodeProperties FileProps = file.NodeProperties as AvrFileNodeProperties; String AdditionalFlags = FileProps.CustomCompilationSetting;
- MSDN. Visual C++ Project Model.
- MSDN. Project Modeling.
- MSDN. Automation Model Overview.