Macro Code Walkthroughs

This topic provides code walkthroughs of the IPFullTrust and SignCode macros so that developers will feel comfortable modifying the macros for their own environments and needs. To view the code for the IPFullTrust and SignCode macros, use the following procedure.

  1. In Visual Studio .NET, on the Tools menu, point to Macros, and then click Macro Explorer.
  2. In the Macro Explorer window, expand the InfoPathSDK node.
  3. Right-click the macro you want to work with, and then click Edit.

IPFullTrust Macro Code Walkthrough

To enable full trust, both the manifest.xsf and template.xml files must be modified. The IPFullTrust macro has a Manifest class and a Template class defined with methods and properties to make these modifications. Because manifest.xsf and template.xml are both XML files, the two classes inherit from XmlDocument class in the System.Xml namespace of the .NET Framework Class Library. This facilitates the modifications needed to the files.

The Manifest class contains the method RemovePublishUrl. Because this class inherits from the XmlDocument class, this method simply needs to call RemoveAttribute:

Me.DocumentElement.RemoveAttribute("publishUrl")

The following lines of code in the AddRequireFullTrust method of the Manifest class add the requireFullTrust attribute:

fullTrustAttrib = Me.CreateAttribute("requireFullTrust")
fullTrustAttrib.Value = "yes"
Me.DocumentElement.Attributes.Append(fullTrustAttrib)

The solutionName property of the Manifest class sets a valid URN if one is not already set.

Try
    Me.DocumentElement.Attributes("name").Value = Value
Catch nrex As System.NullReferenceException 'no name attribute found
    Me.DocumentElement.Attributes.Append(Me.CreateAttribute("name"))
    Me.DocumentElement.Attributes("name").Value = Value
End Try

The Template class defines the method RemoveHrefAttribute. The URL for the form must be removed from template.xml for a form to be fully trusted. This method parses out the URL from the InfoPath processing instruction and deletes it.

The IPFullTrust macro uses the Solution and Project objects of the Visual Studio .NET automation model to find any InfoPath projects in the current solution in much the same way as the SignCode macro does.

For Each proj In DTE.Solution.Projects 'iterate through all projects
    If proj.Kind = IPProjectKind Then

Once the code finds the InfoPath project, it passes the manifest file name to the constructor of the Manifest class. The class constructor does some basic checking to be sure read/write access to the file is available. If those checks pass, the code can call the object methods and set properties to make the required file modifications.

manifestProjItem = proj.ProjectItems.Item(ManifestFilename)
projectManifest = New Manifest(manifestProjItem.FileNames(1))
projectManifest.RemovePublishUrl()
projectManifest.AddRequireFullTrust()
If Not validUrn(projectManifest.solutionName) Then  ' create a valid urn if one is not present.
    projectManifest.solutionName = "urn:" + proj.Name + ":" + "-myXSD-" + DateTime.Now().ToString("s").Replace(":", "-")
End If

The code then moves on to the template.xml file. Again, the Template class contains the functionality required to make the necessary changes to the file. The constructor for the Template class also makes the same checks to be sure read/write access is available. Just like the Manifest class, the Template class can call methods and set properties to make the required modifications.

'modify the template file
templateProjItem = proj.ProjectItems.Item(TemplateFilename)
projectTemplate = New Template(templateProjItem.FileNames(1))
projectTemplate.templateName = projectManifest.solutionName
projectTemplate.RemoveHrefAttribute()

Once the file modifications have been made, the project must be registered on the system. This is the final step in making an InfoPath form fully trusted. Fortunately, InfoPath exposes this functionality. Using COM automation, the RegisterSolution function of the IPFullTrust macro code calls the RegisterSolution method of the InfoPath object model to register the solution.

IPApp = CreateObject("InfoPath.Application")
IPApp.RegisterSolution(solutionPath, "overwrite")
IPApp.Quit()
IPApp = Nothing

This completes the process and your InfoPath project is now fully trusted.

SignCode Macro Code Walkthrough

The SignCode macro contains two string constants that define the names of the certificate and key files used to sign the form template. If you need to use a certificate or key file with a different name, the CertificateFileName and KeyFileName constants can be modified.

Private Const CertificateFileName As String = "myCertificate.cer"
Private Const KeyFileName As String = "myKey.pvk"

The SignCode macro uses the Solution and Project objects of the Visual Studio .NET automation model to determine which, if any, projects open in the solution explorer are InfoPath projects and then obtain the necessary file names and paths.

First, the code iterates over all the projects currently open in the solution:

For Each proj In DTE.Solution.Projects 'iterate through all projects

InfoPath projects have a Kind property that matches a specific GUID value (which is defined in the IPProjectKind constant). The following macro code determines if any projects match that value.

' find InfoPath project
If proj.Kind = IPProjectKind Then
    Dim projectPath As String = proj.Object.ProjectPath

The managed code component is listed as a subproject under the main InfoPath project. The macro code loops through the InfoPath project items to find the managed code subproject.

        ' find the managed code subproject
        For Each projectItem In proj.ProjectItems

            ' is this a subProject?
            If projectItem.Kind = SubProjectKind Then
                buildProj = projectItem.SubProject

The macro code can then determine the output path and file name from this project's OutputPath and OutputFileName properties.

                outputPath = projectPath & buildProj.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value
                outputFileName = CStr(buildProj.Properties.Item("OutputFileName").Value)
                Exit For
            End If
        Next

Because we want to sign the .xsn file instead of the .dll, the code just changes the file extension.

        Dim xsnFile As String = outputPath + Path.GetFileNameWithoutExtension(outputFileName) + ".xsn"

The macro code then calls the signSolution function to perform the actual signing process.

        'sign the .xsn file in the build path
        signSolution(projectPath, xsnFile)

    End If
Next

The signSolution function does the work of calling the SignCode.exe utility and passing the necessary parameters to sign the correct XSN file with the certificate and key file specified by the CertificateFileName and KeyFileName constants.

To call out to an external process, the System.Diagnostics.Process class in the .NET Framework Class Library is used. This class provides capabilities that are similar to the Shell command of the Microsoft® Visual Basic® development system but with more functionality and flexibility. Here's how the macro code initially creates the Process object:

' create a process object to run signcode.exe
Dim proc As New System.Diagnostics.Process
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.UseShellExecute = False

The macro code then sets properties that tell the object what executable to start, where to start from, and where to send the output. Setting the RedirectStandardOutput property to True allows the code to capture any output from the SignCode utility.

' setup process parameters
proc.StartInfo.WorkingDirectory = projectPath
proc.StartInfo.RedirectStandardOutput = True
proc.EnableRaisingEvents = False
proc.StartInfo.FileName = Quote + SignCodePath + "signcode.exe" + Quote

The macro code passes command line arguments to signcode.exe through the Arguments property of the Process.StartInfo class.

' add signcode command line arguments
proc.StartInfo.Arguments = "/spc " + Quote + projectPath + CertificateFileName + Quote
proc.StartInfo.Arguments += " /v " + Quote + projectPath + KeyFileName + Quote
proc.StartInfo.Arguments += " " + Quote + outputFile + Quote

The macro code then simply starts the process and waits for it to finish.

' run the process
proc.Start()
proc.WaitForExit(timeout)

StreamReader and StringBuilder objects are used to capture any output from the process. If any error messages are included in the output, the macro will throw an exception and this will be reported in the build output window.