Walkthrough: Connecting a Host to a Generated Directive Processor

In Walkthrough: Creating a Custom Text Template Host, you created a text template transformation host that supports the basic transformation functionality. In this walkthrough, you expand your host so that it supports text templates that call customized or generated directive processors. When you create a domain model in Domain-Specific Language Tools, it generates a directive processor for the domain model. You can use the generated directive processor to access the model in text templates.

This walkthrough includes the following tasks:

  • Using Domain-Specific Language Tools to generate a directive processor that is based on a domain model.

  • Connecting a custom text template host to the generated directive processor.

  • Testing the custom host with the generated directive processor.

Prerequisites

To complete this walkthrough, you must have the custom text template transformation host that you created in Walkthrough: Creating a Custom Text Template Host.

Using Domain-Specific Language Tools to Generate a Directive Processor

In this walkthrough, you use the Domain-Specific Language Designer Wizard to create a domain-specific language for the solution DSLMinimalTest.

To use Domain-Specific Language Tools to generate a directive processor that is based on a domain model

  1. Create a domain-specific language solution that has the following characteristics:

    • Name: DSLMinimalTest

    • Solution template: Minimal Language

    • File extension: min

    • Company name: Fabrikam

    For more information about creating a domain-specific language solution, see Walkthrough: Creating a Domain-Specific Language Solution.

  2. On the Build menu, click Build Solution.

  3. On the Debug menu, click Start Debugging.

    The Visual Studio experimental build opens. For more information about the experimental build, see Experimental Build.

    Important noteImportant Note:

    This step generates the directive processor and adds the key for it in the registry.

  4. In the experimental build, in Solution Explorer, double-click the file sample.min.

    The file opens in the editor. Notice that the model has two boxes, ExampleElement1 and ExampleElement2, and a connector between them.

  5. Save the solution, and then close the experimental build.

  6. Save the solution, and then close the Domain-Specific Language Designer.

Connecting a Custom Text Template Host to a Directive Processor

After you generate the directive processor, you connect the directive processor and the custom text template host that you created in Walkthrough: Creating a Custom Text Template Host.

To connect a custom text template host to the generated directive processor

  1. Open the solution that you created in Walkthrough: Creating a Custom Text Template Host.

  2. On the Project menu, click Add Reference.

    The Add Reference dialog box opens with the .NET tab displayed.

  3. Add the following references:

    • Microsoft.VisualStudio.Modeling.Sdk

    • Microsoft.VisualStudio.Modeling.Sdk.Diagrams

    • Microsoft.VisualStudio.Modeling.Sdk.Utilities

    • Microsoft.VisualStudio.TextTemplating.VSHost

  4. Verify that the project already has a reference to Microsoft.VisualStudio.TextTemplating.

  5. At the top of Program.cs or Module1.vb, add the following line of code:

    using Microsoft.Win32;
    
    Imports Microsoft.Win32
    
  6. Locate the code for the property StandardAssemblyReferences, and replace it with the following code:

    Note

    In this step, you add references to the assemblies that are required by the generated directive processor that your host will support.

    //the host can provide standard assembly references
    //the engine will use these references when compiling and
    //executing the generated transformation class
    //--------------------------------------------------------------
    public IList<string> StandardAssemblyReferences
    {
        get
        {
            return new string[]
            {
                //if this host searches standard paths and the GAC
                //we can specify the assembly name like this
                //---------------------------------------------------------
                //"System"
    
                //since this host only resolves assemblies from the 
                //fully qualified path and name of the assembly
                //this is a quick way to get the code to give us the
                //fully qualified path and name of the System assembly
                //---------------------------------------------------------
                typeof(System.Uri).Assembly.Location,
                typeof(Microsoft.VisualStudio.Modeling.Diagrams.AnchorPoint).Assembly.Location,
                typeof(Microsoft.VisualStudio.Modeling.Diagrams.BinaryLinkShape).Assembly.Location,
                typeof(Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation).Assembly.Location
            };
        }
    }
    
            'the host can provide standard assembly references
            'the engine will use these references when compiling and
            'executing the generated transformation class
            '--------------------------------------------------------------
            Public ReadOnly Property StandardAssemblyReferences() As IList(Of String) Implements Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.StandardAssemblyReferences
                Get
    
                    'if this host searches standard paths and the GAC
                    'we can specify the assembly name like this
                    '---------------------------------------------------------
                    '"System"
    
                    'since this host only resolves assemblies from the 
                    'fully qualified path and name of the assembly
                    'this is a quick way to get the code to give us the
                    'fully qualified path and name of the System assembly
                    '---------------------------------------------------------
                    Return New String() _
                    { _
    
    
                      GetType(System.Uri).Assembly.Location, _
                      GetType(Microsoft.VisualStudio.Modeling.Diagrams.AnchorPoint).Assembly.Location, _
                      GetType(Microsoft.VisualStudio.Modeling.Diagrams.BinaryLinkShape).Assembly.Location, _
                      GetType(Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation).Assembly.Location
                       }
                End Get
            End Property
    
  7. Locate the code for the function ResolveDirectiveProcessor, and replace it with the following code:

    Important noteImportant Note:

    This code contains hard-coded references to the name of the generated directive processor to which you want to connect. You could easily make this more general, in which case it looks for all directive processors listed in the registry and tries to find a match. In that case, the host would work with any generated directive processor.

    //the engine calls this method based on the directives the user has 
            //specified it in the text template
            //this method can be called 0, 1, or more times
            //---------------------------------------------------------------------
            public Type ResolveDirectiveProcessor(string processorName)
            {
                //check the processor name, and if it is the name of the processor the 
                //host wants to support, return the type of the processor
                //---------------------------------------------------------------------
                if (string.Compare(processorName, "DSLMinimalTestDirectiveProcessor", StringComparison.InvariantCultureIgnoreCase) == 0)
                {
    
                    try
                    {
                        string keyName = @"Software\Microsoft\VisualStudio\9.0Exp\Configuration\TextTemplating\DirectiveProcessors\DSLMinimalTestDirectiveProcessor";
    
                        using (RegistryKey specificKey = Registry.CurrentUser.OpenSubKey(keyName))
                        {
                            if (specificKey != null)
                            {
                                List<string> names = new List<String>(specificKey.GetValueNames());
    
                                string classValue = specificKey.GetValue("Class") as string;
                                if (!string.IsNullOrEmpty(classValue))
                                {
                                    string loadValue = string.Empty;
                                    System.Reflection.Assembly processorAssembly = null;
    
                                    if (names.Contains("Assembly"))
                                    {
                                        loadValue = specificKey.GetValue("Assembly") as string;
                                        if (!string.IsNullOrEmpty(loadValue))
                                        {
                                            //the assembly must be installed in the GAC
                                            processorAssembly = System.Reflection.Assembly.Load(loadValue);
                                        }
                                    }
                                    else if (names.Contains("CodeBase"))
                                    {
                                        loadValue = specificKey.GetValue("CodeBase") as string;
                                        if (!string.IsNullOrEmpty(loadValue))
                                        {
                                            //loading local assembly
                                            processorAssembly = System.Reflection.Assembly.LoadFrom(loadValue);
                                        }
                                    }
                                    if (processorAssembly == null)
                                    {
                                        throw new Exception("Directive Processor not found");
                                    }
    
                                    Type processorType = processorAssembly.GetType(classValue);
                                    if (processorType == null)
                                    {
                                        throw new Exception("Directive Processor not found");
                                    }
                                    return processorType;
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        //if the directive processor can not be found, throw an error
                        throw new Exception("Directive Processor not found");
                    }
                }
    
                //if the directive processor is not one this host wants to support
                throw new Exception("Directive Processor not supported");
            }
    
            'the engine calls this method based on the directives the user has 
            'specified it in the text template
            'this method can be called 0, 1, or more times
            '---------------------------------------------------------------------
            Public Function ResolveDirectiveProcessor(ByVal processorName As String) As System.Type Implements Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.ResolveDirectiveProcessor
                'this host will not resolve any specific processors
    
                'check the processor name, and if it is the name of a processor the 
                'host wants to support, return the type of the processor
                '---------------------------------------------------------------------
                If String.Compare(processorName, "DSLMinimalTestDirectiveProcessor", StringComparison.InvariantCultureIgnoreCase) = 0 Then
    
                    Try
                        Dim keyName As String = "Software\Microsoft\VisualStudio\9.0Exp\Configuration\TextTemplating\DirectiveProcessors\DSLMinimalTestDirectiveProcessor"
    
                        Using specificKey As RegistryKey = Registry.CurrentUser.OpenSubKey(keyName)
    
                            If Not specificKey Is Nothing Then
    
                                Dim names As List(Of String) = New List(Of String)(specificKey.GetValueNames())
                                Dim classValue As String = specificKey.GetValue("Class")
    
                                If Not String.IsNullOrEmpty(classValue) Then
    
                                    Dim loadValue As String = String.Empty
                                    Dim processorAssembly As System.Reflection.Assembly = Nothing
    
                                    If names.Contains("Assembly") Then
    
                                        loadValue = specificKey.GetValue("Assembly")
    
                                        If Not String.IsNullOrEmpty(loadValue) Then
                                            processorAssembly = System.Reflection.Assembly.Load(loadValue)
                                            'because we use Assembly.Load, the assembly must be installed in the GAC
                                        End If
                                    ElseIf names.Contains("CodeBase") Then
    
                                        loadValue = specificKey.GetValue("CodeBase ")
    
                                        If Not String.IsNullOrEmpty(loadValue) Then
                                            'the assembly is installed locally
                                            processorAssembly = System.Reflection.Assembly.LoadFrom(loadValue)
                                        End If
    
                                    End If
    
                                    If processorAssembly Is Nothing Then
                                        Throw New Exception("Directive Processor not found")
                                    End If
    
                                    Dim processorType As Type = processorAssembly.GetType(classValue)
    
                                    If processorType Is Nothing Then
                                        Throw New Exception("Directive Processor not found")
                                    End If
    
                                    Return processorType
                                End If
                            End If
                        End Using
    
                    Catch e As Exception
    
                        'if the directive processor can not be found, throw an error
                        Throw New Exception("Directive Processor not found")
                    End Try
                End If
    
                'if the directive processor can not be found, throw an error
                Throw New Exception("Directive Processor not found")
            End Function
    
  8. On the File menu, click Save All.

  9. On the Build menu, click Build Solution.

Testing the Custom Host with the Directive Processor

To test the custom text template host, first you must write a text template that calls the generated directive processor. Then you run the custom host, pass to it the name of the text template, and verify that the directive is processed correctly.

To create a text template to test the custom host

  1. Create a text file, and name it TestTemplateWithDP.txt.

    Note

    You can use any text editor, such as Notepad, to create the file.

  2. Add the following to the text file:

    Note

    The programming language of the text template does not need to match that of the custom host.

    Text Template Host Test
    
    <#@ template debug="true" inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>
    
    
    <# //this is the call to the examplemodel directive in the generated directive processor #>
    <#@ DSLMinimalTest processor="DSLMinimalTestDirectiveProcessor" requires="fileName='<Your Path>\Sample.min'" provides="ExampleModel=ExampleModel" #>
    
    
    <# //uncomment this line to test that the host allows the engine to set the extension #>
    <# //@ output extension=".htm" #>
    
    <# //uncomment this line if you want to see the generated transformation class #>
    <# //System.Diagnostics.Debugger.Break(); #>
    
    
    <# //this code uses the results of the examplemodel directive #>
    <#
        foreach ( ExampleElement box in this.ExampleModel.Elements ) 
        { 
            WriteLine("Box: {0}", box.Name);
    
            foreach (ExampleElement linkedTo in box.Targets)
            {
                WriteLine("Linked to: {0}", linkedTo.Name);
            }
    
            foreach (ExampleElement linkedFrom in box.Sources)
            {
                WriteLine("Linked from: {0}", linkedFrom.Name);
            }
    
            WriteLine("");
        } 
    #>
    
    Text Template Host Test
    
    <#@ template debug="true" language="VB" inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>
    
    <# 'this is the call to the examplemodel directive in the generated directive processor #>
    <#@ DSLMinimalTest processor="DSLMinimalTestDirectiveProcessor" requires="fileName='<Your Path>\Sample.min'" provides="ExampleModel=ExampleModel" #>
    
    <# 'Uncomment this line to test that the host allows the engine to set the extension. #>
    <# '@ output extension=".htm" #>
    
    <# 'Uncomment this line if you want to see the generated transformation class. #>
    <# 'System.Diagnostics.Debugger.Break() #>
    
    <# 'this code uses the results of the examplemodel directive #>
    
    <#    
       For Each box as ExampleElement In Me.ExampleModel.Elements 
    
           WriteLine("Box: {0}", box.Name)
    
            For Each LinkedTo as ExampleElement In box.Targets
                WriteLine("Linked to: {0}", LinkedTo.Name)
            Next
    
            For Each LinkedFrom as ExampleElement In box.Sources
                WriteLine("Linked from: {0}", LinkedFrom.Name)
            Next
    
            WriteLine("")
    
       Next 
    #>
    
  3. In the code, replace <YOUR PATH> with the path of the Sample.min file from the design-specific language you created in the first procedure.

  4. Save and close the file.

To test the custom host

  1. Open a Command Prompt window.

  2. Type the path of the executable file for the custom host, but do not press ENTER yet.

    For example, type:

    <YOUR PATH>CustomHost\bin\Debug\CustomHost.exe

    Note

    Instead of typing the address, you can browse to the file CustomHost.exe in Windows Explorer, and then drag the file into the Command Prompt window.

  3. Type a space.

  4. Type the path of the text template file, and then press ENTER.

    For example, type:

    <YOUR PATH>TestTemplateWithDP.txt

    Note

    Instead of typing the address, you can browse to the file TestTemplateWithDP.txt in Windows Explorer, and then drag the file into the Command Prompt window.

    The custom host application runs and starts the text template transformation process.

  5. In Windows Explorer, browse to the folder that contains the file TestTemplateWithDP.txt.

    The folder also contains the file TestTemplateWithDP1.txt.

  6. Open this file to see the results of the text template transformation.

    The results of the generated text output appears and should look like this:

    Text Template Host Test
    
    
    
    
    
    
    
    
    Box: ExampleElement1
    Linked to: ExampleElement2
    
    Box: ExampleElement2
    Linked from: ExampleElement1
    

Security

For more information, see Security of Text Templates.

See Also

Tasks

Walkthrough: Creating a Custom Text Template Host

Concepts

Architecture of the Text Template Transformation Process

Reference

ITextTemplatingEngineHost

Other Resources

Creating Custom Text Template Hosts

Generating Artifacts By Using Text Templates

Domain-Specific Language Tools Glossary

Change History

Date

History

Reason

July 2008

Rewrote and refactored project.

Content bug fix.