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.
- In Visual Studio .NET, on the Tools menu, point to Macros, and then click Macro Explorer.
- In the Macro Explorer window, expand the InfoPathSDK node.
- Right-click the macro you want to work with, and then click Edit.
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:
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.
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.