Walkthrough: Invoking the Core Editor
This walkthrough demonstrates how to create a Visual Studio package (or VSPackage) that starts the Visual Studio core editor whenever a file is loaded with the extension ".myext".
Prerequisites
This walkthrough requires the Visual Studio SDK to be installed. The result of this walkthrough writes information to the experimental registry hive for Visual Studio.
If you already have a VSPackage, load its solution and skip to the "To add the editor factory" procedure.
To create the VSPackage
Start Visual Studio and create a new Visual C# VSPackage named MyPackage as outlined in Walkthrough: Creating a Menu Command VSPackage.
The following procedures assume your VSPackage project is loaded.
To add the editor factory
Right-click the MyPackage project, point to Add and click Class.
The Add New Item dialog box displays.
Make sure the Class template is selected, type MyEditorFactory.cs for the name, and then click Add to add the class to your project.
The MyEditorFactory.cs file should be automatically opened.
Reference the following assemblies from within your code:
Imports System.Runtime.InteropServicesImports Microsoft.VisualStudioImports Microsoft.VisualStudio.ShellImports Microsoft.VisualStudio.Shell.InteropImports Microsoft.VisualStudio.OLE.InteropImports Microsoft.VisualStudio.TextManager.InteropImports IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider
using System.Runtime.InteropServices;using Microsoft.VisualStudio;using Microsoft.VisualStudio.Shell;using Microsoft.VisualStudio.Shell.Interop;using Microsoft.VisualStudio.OLE.Interop;using Microsoft.VisualStudio.TextManager.Interop;using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
Add a GUID to the MyEditorFactory class by adding the Guid attribute before the class declaration.
You can generate a new GUID using the guidgen.exe program from the Visual Studio command prompt or, on the Tools menu, click Create GUID. The GUID used here is only an example and should not be used in your project.
<Guid("0eea3187-c5fa-48d4-aa72-b5eecd3b17b1")> _ Class MyEditorFactory
[Guid("0eea3187-c5fa-48d4-aa72-b5eecd3b17b1")] class MyEditorFactory
Add two private variables to contain the parent package and a service provider:
Class MyEditorFactory Private parentPackage As Package Private serviceProvider As IOleServiceProvider
class MyEditorFactory { private Package parentPackage; private IOleServiceProvider serviceProvider; }
Add a public class constructor that takes a single parameter of type Package:
Public Sub New(ByVal parentPackage As Package) Me.parentPackage = parentPackage End Sub
public MyEditorFactory(Package parentPackage){ this.parentPackage = parentPackage;}
Modify the MyEditorFactory class declaration to derive from the IVsEditorFactory interface:
Class MyEditorFactory Implements IVsEditorFacto
class MyEditorFactory : IVsEditorFactory
Right-click the IVsEditorFactory name, select Implement Interface, and then click Explicitly.
This adds the four methods that must be implemented in the IVsEditorFactory interface.
Modify the IVsEditorFactory.Close method to look like the following:
Return VSConstants.S_OK
{ return VSConstants.S_OK; }
Modify the IVsEditorFactory.SetSite method to look like the following:
Me.serviceProvider = psp Return VSConstants.S_OK
{ this.serviceProvider = psp; return VSConstants.S_OK; }
Modify the IVsEditorFactory.MapLogicalView method to look like the following:
Dim retval As Integer = VSConstants.E_NOTIMPL pbstrPhysicalView = Nothing ' We support only one view. If rguidLogicalView.Equals(VSConstants.LOGVIEWID_Designer)OrElse _ rguidLogicalView.Equals(VSConstants.LOGVIEWID_Primary) Then retval = VSConstants.S_OK End If Return retval
{ int retval = VSConstants.E_NOTIMPL; pbstrPhysicalView = null; // We support only one view. if (rguidLogicalView.Equals(VSConstants.LOGVIEWID_Designer) || rguidLogicalView.Equals(VSConstants.LOGVIEWID_Primary)) { retval = VSConstants.S_OK; } return retval; }
Modify the IVsEditorFactory.CreateEditorInstance method to look like the following:
Dim retval As Integer = VSConstants.E_FAIL ' Initialize these to empty to start with ppunkDocView = IntPtr.Zero ppunkDocData = IntPtr.Zero pbstrEditorCaption = "" pguidCmdUI = Guid.Empty pgrfCDW = 0 If (grfCreateDoc And (VSConstants.CEF_OPENFILE Or VSConstants.CEF_SILENT)) = 0 Then Throw New ArgumentException("Only Open or Silent is valid") End If If punkDocDataExisting <> IntPtr.Zero Then Return VSConstants.VS_E_INCOMPATIBLEDOCDATA End If ' Instantiate a text buffer of type VsTextBuffer. ' Note: we only need an IUnknown (object) interface for ' this invocation. Dim clsidTextBuffer As Guid = GetType(VsTextBufferClass).GUID Dim iidTextBuffer As Guid = VSConstants.IID_IUnknown Dim pTextBuffer As Object = pTextBuffer = parentPackage.CreateInstance(clsidTextBuffer, iidTextBuffer, GetType(Object)) If Not pTextBuffer Is Nothing Then ' "Site" the text buffer with the service provider we were ' provided. Dim textBufferSite As IObjectWithSite = TryCast(pTextBuffer, IObjectWithSite) If Not textBufferSite Is Nothing Then textBufferSite.SetSite(Me.serviceProvider) End If ' Instantiate a code window of type IVsCodeWindow. Dim clsidCodeWindow As Guid = GetType(VsCodeWindowClass).GUID Dim iidCodeWindow As Guid = GetType(IVsCodeWindow).GUID Dim pCodeWindow As IVsCodeWindow = CType(Me.parentPackage.CreateInstance(clsidCodeWindow, iidCodeWindow, GetType(IVsCodeWindow)), IVsCodeWindow) If Not pCodeWindow Is Nothing Then ' Give the text buffer to the code window. ' We are giving up ownership of the text buffer! pCodeWindow.SetBuffer(CType(pTextBuffer, IVsTextLines)) ' Now tell the caller about all this new stuff ' that has been created. ppunkDocView = Marshal.GetIUnknownForObject(pCodeWindow) ppunkDocData = Marshal.GetIUnknownForObject(pTextBuffer) ' Specify the command UI to use so keypresses are ' automatically dealt with. pguidCmdUI = VSConstants.GUID_TextEditorFactory ' This caption is appended to the filename and ' lets us know our invocation of the core editor ' is up and running. pbstrEditorCaption = " [MyPackage]" retval = VSConstants.S_OK End If End If Return retval
{ int retval = VSConstants.E_FAIL; // Initialize these to empty to start with ppunkDocView = IntPtr.Zero; ppunkDocData = IntPtr.Zero; pbstrEditorCaption = ""; pguidCmdUI = Guid.Empty; pgrfCDW = 0; if ((grfCreateDoc & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) == 0) { throw new ArgumentException("Only Open or Silent is valid"); } if (punkDocDataExisting != IntPtr.Zero) { return VSConstants.VS_E_INCOMPATIBLEDOCDATA; } // Instantiate a text buffer of type VsTextBuffer. // Note: we only need an IUnknown (object) interface for // this invocation. Guid clsidTextBuffer = typeof(VsTextBufferClass).GUID; Guid iidTextBuffer = VSConstants.IID_IUnknown; object pTextBuffer = pTextBuffer = parentPackage.CreateInstance( ref clsidTextBuffer, ref iidTextBuffer, typeof(object)); if (pTextBuffer != null) { // "Site" the text buffer with the service provider we were // provided. IObjectWithSite textBufferSite = pTextBuffer as IObjectWithSite; if (textBufferSite != null) { textBufferSite.SetSite(this.serviceProvider); } // Instantiate a code window of type IVsCodeWindow. Guid clsidCodeWindow = typeof(VsCodeWindowClass).GUID; Guid iidCodeWindow = typeof(IVsCodeWindow).GUID; IVsCodeWindow pCodeWindow = (IVsCodeWindow)this.parentPackage.CreateInstance( ref clsidCodeWindow, ref iidCodeWindow, typeof(IVsCodeWindow)); if (pCodeWindow != null) { // Give the text buffer to the code window. // We are giving up ownership of the text buffer! pCodeWindow.SetBuffer((IVsTextLines)pTextBuffer); // Now tell the caller about all this new stuff // that has been created. ppunkDocView = Marshal.GetIUnknownForObject(pCodeWindow); ppunkDocData = Marshal.GetIUnknownForObject(pTextBuffer); // Specify the command UI to use so keypresses are // automatically dealt with. pguidCmdUI = VSConstants.GUID_TextEditorFactory; // This caption is appended to the filename and // lets us know our invocation of the core editor // is up and running. pbstrEditorCaption = " [MyPackage]"; retval = VSConstants.S_OK; } } return retval; }
Compile the project and make sure there are no errors. If there are none, then proceed to To register the editor factory.
To register the editor factory
Open Resource View by clicking the View menu, pointing to Other Windows, and then clicking Resource View.
In Resource View, navigate to the String Table for the MyPackageUI project, and open the String Table.
Right-click the String Table view and choose New String to add a new string that is to be the name of the editor.
Make note of the resource number for this new string, and then change the new string entry as follows:
Change the name of the identifier to IDS_EDITORNAME.
Change the text to MyPackage Editor.
Open Solution Explorer and then open the vspkg.cs file.
This is the main package file.
Add the following user attributes just before the Guid attribute. (Be sure to substitute the 101 value in this example with the proper resource number that was added in step 2. Note that you cannot use the label you created.)
<ProvideEditorFactoryAttribute(GetType(MyEditorFactory), 101), _ ProvideEditorExtensionAttribute(GetType(MyEditorFactory), _ ".myext", _ 32, _ NameResourceID:=101 ), _ Guid("0eea3186-c5fa-48d4-aa72-b5eecd3b17b1")> _ Public [class] As sealed 'third parameter of ProvideEditorExtensionAttribute is priority
[ProvideEditorFactoryAttribute(typeof(MyEditorFactory), 101)][ProvideEditorExtensionAttribute(typeof(MyEditorFactory), ".myext", 32, // priority NameResourceID = 101 )] [Guid("0eea3186-c5fa-48d4-aa72-b5eecd3b17b1")] public sealed class MyPackage : MSVSIP.Package
The ProvideEditorExtensionAttribute attribute associates the .myext file extension with your editor factory so that any time a file with that extension is loaded, your editor factory is invoked.
Add a private variable to the MyPackage class with the type MyEditorFactory:
Public NotInheritable Class MyPackage Inherits Package Private editorFactory As MyEditorFactory
public sealed class MyPackage : MSVSIP.Package { private MyEditorFactory editorFactory;
Find the Initialize method (if necessary, open up the Package Members hidden region) and add the following code:
MyBase.Initialize()
' Create our editor factory and register it.
Me.editorFactory = New MyEditorFactory(Me)
MyBase.RegisterEditorFactory(Me.editorFactory)
Robust Programming
The visual cue that your invocation of the core editor is running is when you see "[MyPackage]" appended to the name of the file in the Visual Studio title bar.
The Visual Studio core editor handles a wide range of text-based file types and the editor works closely with language services to provide a rich set of features such as syntax highlighting, brace matching, and IntelliSense word- and member-completion lists, to name just a few. If you are working with text-based files, then you can use the core editor along with a custom language service that supports your specific file types.
A VSPackage can invoke the Visual Studio core editor by supplying an editor factory. This editor factory is used any time a file associated with it is loaded. If the file is part of a project, then the core editor is automatically invoked unless overridden by your VSPackage. However, if the file is loaded outside of a project, then the core editor must be explicitly invoked by your VSPackage.
For more information about the core editor, see Core Editor.