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

To add the editor factory

  1. Right-click the MyPackage project, point to Add and click Class.

    The Add New Item dialog box displays.

  2. Make sure the Class template is selected, type EditorFactory.cs for the name, and then click Add to add the class to your project.

    The EditorFactory.cs file should be automatically opened.

  3. Reference the following assemblies from within your code:

    Imports System.Runtime.InteropServices
    Imports Microsoft.VisualStudio
    Imports Microsoft.VisualStudio.Shell
    Imports Microsoft.VisualStudio.Shell.Interop
    Imports Microsoft.VisualStudio.OLE.Interop
    Imports Microsoft.VisualStudio.TextManager.Interop
    Imports 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;
    
  4. Add a GUID to the EditorFactory class by adding the Guid attribute immediately 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")> _
    
        [Guid("0eea3187-c5fa-48d4-aa72-b5eecd3b17b1")] 
    
  5. Inside the class definition, add two private variables to contain the parent package and a service provider:

    Class EditorFactory
        Private parentPackage As Package
        Private serviceProvider As IOleServiceProvider
    
    class EditorFactory
    {
        private Package parentPackage;
        private IOleServiceProvider serviceProvider;
    }
    
  6. 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 EditorFactory(Package parentPackage)
    {
        this.parentPackage = parentPackage;
    }
    
  7. Modify the EditorFactory class declaration to derive from the IVsEditorFactory interface:

    Class EditorFactory Implements IVsEditorFacto
    
    class EditorFactory : IVsEditorFactory
    
  8. Right-click the IVsEditorFactory name, select Implement Interface, and then click Implement Interface Explicitly.

    This adds the four methods that must be implemented in the IVsEditorFactory interface.

  9. Replace the contents of the IVsEditorFactory.Close method with the following:

    Return VSConstants.S_OK
    
    return VSConstants.S_OK;
    
  10. Replace the contents of the IVsEditorFactory.SetSite with the following:

    Me.serviceProvider = psp
    Return VSConstants.S_OK
    
    this.serviceProvider = psp;
    return VSConstants.S_OK;
    
  11. Replace the contents of the IVsEditorFactory.MapLogicalView method with 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;
    
  12. Replace the contents of the IVsEditorFactory.CreateEditorInstance method with 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; 
    
  13. Compile the project and make sure there are no errors.

To register the editor factory

  1. From the Solution Explorer, double-click the Resources.resx file. This will open the resources file to the string table with the entry String1 selected

  2. Change the name of the identifier to IDS_EDITORNAME and the text to MyPackage Editor. This string will appear as the name of your editor.

  3. Now open the VSPackage.resx file and add a new string, setting the name to 101 and value to IDS_EDITORNAME. This provides the package with a resource ID to access the string you created in step 2.

Note

If the VSPackage.resx file contains another string with the name attribute set to 101, substitute another unique, numeric value for this and all following steps.

  1. From Solution Explorer, open the MyPackagePackage.cs file.

    This is the main package file.

  2. Add the following user attributes just before the Guid attribute.

    <ProvideEditorFactoryAttribute(GetType(EditorFactory), 101)> _
    <ProvideEditorExtensionAttribute(GetType(EditorFactory), _
          ".myext", 32, NameResourceID:=101 )> _
    
    [ProvideEditorFactory(typeof(EditorFactory), 101)]
    [ProvideEditorExtension(typeof(EditorFactory), 
          ".myext", 32, NameResourceID = 101)] 
    

    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.

  3. Add a private variable to the MyPackage class, just before the constructor, with the type EditorFactory:

    Private editorFactory As EditorFactory
    
    private EditorFactory editorFactory;
    
  4. Find the Initialize method (if necessary, open up the Package Members hidden region) and add the following code after the call to base.Initialize():

    'Create our editor factory and register it. 
    Me.editorFactory = New EditorFactory(Me)
    MyBase.RegisterEditorFactory(Me.editorFactory)
    
    // Create our editor factory and register it.
    this.editorFactory = new EditorFactory(this);
    base.RegisterEditorFactory(this.editorFactory);
    
  5. Compile the program and make sure there are no errors.

    This step registers the editor factory in the experimental registry hive for Visual Studio. If you are asked to override the resource.h file, click OK.

  6. Create a sample file named TextFile1.myext.

  7. Press F5 to open an instance of the experimental Visual Studio.

  8. In the experimental Visual Studio, on the File menu point to Open and then click File to open the Open File dialog box.

  9. Find the TextFile1.myext file, select it and then click Open.

    The file should now be loaded.

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.

See Also

Concepts

Instantiating the Core Editor

Other Resources

Editor Walkthroughs

Core Editor