placeholders).
Because you plan to deploy this macro to other machines, you’ll create a separate macro project for
it called CodeNotes (right-click the Macros node in the Macro Explorer tool window and select
New Macro Project from the context menu). Rename the default module that was created under
it from Module1 to CodeModificationNotes (right-click the module and select Rename from the
context menu). Double-click the CodeModificationNotes module to open it in the Macros IDE. Add
the following function to the module (this will be the macro):
Public Sub InsertCodeModificationNote()
Const MY_NAME As String = "Chris Anderson"
Dim textSelection As EnvDTE.TextSelection
textSelection = CType(DTE.ActiveDocument.Selection(), EnvDTE.TextSelection)
textSelection.Text = "// Modified by " & MY_NAME & " at " & DateTime.Now
End Sub
1030 .
chaPter 52 mAcroS
Now you can debug your macro by placing the cursor within the function and pressing F5 (Run).
The Visual Studio IDE will quickly become active as the macro runs, returning to the Macros IDE
when complete. If you return to the Visual Studio IDE and look in the Macro Explorer, you will
note that the macro now appears under the module, and can be run by double-clicking its name.
runninG a Macro
You have a number of ways to run a macro once you’ve created it that we’ve already discussed. As a
recap you can:
.
Double-click the name of the macro in Macro Explorer.
.
Press F5 in the Macros IDE when the cursor is inside the macro code.
In addition, you can also assign keyboard
shortcuts to macros that you use regularly
to make them easier to activate and run. Go to
Tools . Options and find the Keyboard node
under the Environment category. Search for
your macro by its name (enter the name into
the Show Commands Containing textbox) and
select it from the list. Select the Press Shortcut
Keys textbox, and press the key combination
that you want to use to activate the macro (as
shown in Figure 52-5). Ensure that the key
combination isn’t currently being used, or you
don’t mind overwriting it, and then click the
Assign button. Now when you press that key combination in the Visual Studio IDE the macro will run.
For long-running macros, a tape icon will appear in the bottom-right corner of the IDE, and in the
taskbar. If you right-click this icon, you can choose to stop any macros currently running.
Debugging macros is similar to debugging any other type of .NET application. You simply start
your macro project in Debug mode by pressing F5 or choosing appropriate menu items in the
Macros IDE. You also have the same debugging tools as in Visual Studio, enabling you to set
breakpoints, step through code, and view the value of a variable by putting your mouse over it.
dePloyinG Macros
Because macros can’t be compiled, when you deploy a macro you are actually deploying its source
code, which any user can view. Therefore, be sure you’re not including any sensitive code in your
macro project. You cannot deploy just a single macro — you need to deploy the entire macro
project. If you have other macros in the project that you don’t want distributed, you should first
move the macro to a new macro project and deploy that instead.
For the macros to be available to a developer automatically in Visual Studio, the macro projects
must be deployed to the My Documents\Visual Studio 2010\Projects\VSMacros80 folder in
that user’s Windows profile.
fiGure 52-5
summary .
1031
As with add-ins, you can deploy your macro to another user’s machine using a simple XCOPY
operation, a Windows installer package (.msi), or a Visual Studio Content Installer package.
Creating a Visual Studio Content Installer package is probably your best option, and is created in
much the same way as the one demonstrated for add-ins. Let’s take a look at deploying the macro
project using a Visual Studio Content Installer package.
Create a text file called CodeNotes.vscontent in the same location as your .vsmacros file, with
the following content:
<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
<Content>
<FileName>CodeNotes.vsmacros</FileName>
<DisplayName>Code Notes</DisplayName>
<Description>A macro project to put notes in code.</Description>
<FileContentType>Macro Project</FileContentType>
<ContentVersion>1.0</ContentVersion>
</Content>
</VSContent>
Now, in Windows Explorer (or your favorite zip tool), combine the CodeNotes.vsmacros and
CodeNotes.vscontent files into a zip file, and name it CodeNotes.vsi (do not include the .zip
extension). Now when someone double-clicks this .vsi file, the macro will automatically be
installed and ready to use when he or she next opens Visual Studio.
suMMary
This chapter discussed how macros can be used to automate repetitive tasks in Visual Studio by
either recording those actions or coding them against the Visual Studio automation model. You
looked at the support that Visual Studio has for macros, how to create them, run them, and finally,
deploy them to other developers.
53 53
Managed extensibility
framework (Mef)
what’s in this chaPter?
.
Architecting extensible applications
.
Hosting the Managed Extensibility Framework in your applications
.
Understanding the Visual Studio 2010 Editor components
.
Extending the Visual Studio 2010 Editor
.
Importing Visual Studio Services
Creating loosely coupled applications that can be extended after deployment can be a diffi cult
process. You have many design decisions to make, including identifying and loading extensions
that have been deployed, and making application services available to loaded extensions. The
Managed Extensibility Framework (MEF) is an open source library created by Microsoft designed
to reduce the complexity of creating extensible applications. It allows you to expose reusable parts
of your application to plug - ins or extensions that are discovered and loaded at run time and
design your application in a very loosely coupled fashion.
Visual Studio 2010 uses the MEF library to provide extension points for the main editor control.
It is expected that in future versions of Visual Studio, more areas will be exposed for this kind of
extension.
This chapter is split into three sections. The first section is an introduction to how MEF works
and how to use it in your own applications. The middle section describes the components of
the new Visual Studio 2010 Editor control and how they interact. The fi nal section describes the
process of extending the editor with MEF and provides a complete sample which emphasizes
certain types of comment in your code.
1034 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
At the time of writing, the MEF library is still in development so some
details may have changed. For the latest information about MEF, check
http://mef.codeplex.com.
GettinG started with Mef
In this section, you create a simple application that demonstrates the manner in which most
applications will utilize the capabilities offered by MEF. The MEF library is contained within the
System.ComponentModel.Composition assembly, which is installed in the GAC as a part of
the .NET Framework 4.0.
The key component of MEF is the CompositionContainer, which is found in the
System.ComponentModel.Composition.Hosting namespace. A composition container is responsible
for creating composable parts of your application, which in the default MEF implementation are
just normal .NET objects. These parts might be a core aspect of your application or they might
come from externally deployed extension assemblies that are loaded dynamically at run time.
Each part is able to provide one or more exports that other composable parts need and may require
one or more externally provided imports that other parts provide. Imports and exports can be simple
properties or fields, or they can be entire classes. When you request a part from the composition
container, it will attempt to locate the part and satisfy any import dependencies it might have. Each of
these imports must be provided (exported) by other parts that the container is aware of and may have
import requirements of their own, which in turn must also be satisfied.
To build a bare-bones MEF application, create a new command-line project, add a reference to the
System.ComponentModel.Composition assembly, and replace the contents of Program.cs (C#) or
Module1.vb (VB) with the following:
c#
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace GettingStartedCS
{
class Program
{
static void Main(string[] args)
{
var app = new ApplicationRoot();
app.Run();
}
}
class ApplicationRoot
{
public void Run()
{
Getting started with Mef .
1035
Compose();
}
private void Compose()
{
var compositionContainer = new CompositionContainer();
compositionContainer.ComposeParts(this);
}
}
}
Code snippet GettingStarted\Program.cs
Vb
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
Module Module1
Sub Main()
Dim app As New ApplicationRoot
app.Run()
End Sub
End Module
Class ApplicationRoot
Sub Run()
Compose()
Console.WriteLine("OK")
End Sub
Private Sub Compose()
Dim compositionContainer As New CompositionContainer
compositionContainer.ComposeParts(Me)
End Sub
End Class
Code snippet GettingStarted\Module1.vb
The ComposeParts method is an extension method in the System.ComponentModel
.Composition namespace, so if you do not have this namespace included, this code
will not compile.
All the sample does is create a CompositionContainer and then ask it to compose the Application
Root class. The ComposeParts method, satisfies the import requirements of the parts that you provide
it. If it cannot satisfy these requirements it will throw a System.ComponentModel.Composition
1036 .
chaPter 53 mAnAged exTenSibiliTy FrAmeWork (meF)
.CompositionException. As the ApplicationRoot class has no import requirements, the
application simply writes OK to the console and ends. This is not very exciting, but it does provide a
base on which you can add functionality.
imports and exports
The previous code sample asks the container to satisfy the import requirements of the ApplicationRoot
class. Before you add an import requirement to that class, you will need an exported class to satisfy the
dependency. The ApplicationRoot class prints a status message once composition is complete. You
can delegate this responsibility to another class and then ask the composition container to provide an
instance of that class during composition.
To make a part available to the rest of your program you can export it by applying an
ExportAttribute to it. This code snippet creates a simple class and exports it:
c#
[System.ComponentModel.Composition.Export]
class StatusNotificationService
{
public void ShowStatus(string statusText)
{
System.Console.WriteLine(statusText);
}
}
Code snippet GettingStarted\StatusNotificationService.cs
Vb
<System.ComponentModel.Composition.Export()>
Public Class StatusNotificationService
Public Sub ShowStatus(ByVal statusText As String)
System.Console.WriteLine(statusText)
End Sub
End Class
Code snippet GettingStarted\StatusNotificationService.vb
By adding an ExportAttribute onto the StatusNotificationService class, MEF is able to treat
it as a composable part. Note, however, that the Export attribute is just metadata and MEF is still
not aware of this part and will not use it. The simplest way to make the part available to MEF
during part composition is to provide an instance of the exported class to the ComposeParts
method. Change the Compose method of the ApplicationRoot class to instantiate an instance of the
StatusNotification class and pass it into the ComposeParts method call as a second parameter.
Finally, to specify that the ApplicationRoot class requires an instance of this part, add a property
to the ApplicationRoot class and mark it up with an ImportAttribute. Following is the full
listing for the ApplicationRoot class. There is some code added after the call to Compose in the
Run method that uses the newly imported part.
Getting started with Mef .
1037
c#
class ApplicationRoot
{
public void Run()
{
Compose();
NotifcationService.ShowStatus("Composition Complete");
}
public void Compose()
{
var compositionContainer = new CompositionContainer();
var statusNotificationService = new StatusNotificationService();
compositionContainer.ComposeParts(this, statusNotificationService);
}
[System.ComponentModel.Composition.Import]
public StatusNotificationService NotifcationService { get; set; }
}
Code snippet GettingStarted\Program.cs
Vb
Class ApplicationRoot
Sub Run()
Compose()
NotificationService.ShowStatus("Composition Complete")
End Sub
Private Sub Compose()
Dim compositionContainer As New CompositionContainer
Dim statusNotificationService As New StatusNotificationService
compositionContainer.ComposeParts(Me, statusNotificationService)
End Sub
<System.ComponentModel.Composition.Import()>
Property NotificationService() As StatusNotificationService
End Class
Code snippet GettingStarted\Module1.vb
contracts
When the composition container is attempting to resolve dependencies during a composition, it
uses a string called a contract to match imports up to exports. By default, if no contract is supplied,
MEF will use the fully qualified type name of the exported item as the contract. You can override