Programmatically caching Infopath templates using Application.CacheSolution

Infopath has a simple and elegent system for caching form templates - when you open a form with an unknown template, it extracts the template version and location information from an XML processing instruction (PI) in the form, and then loads the template from the specified location and caches it on the local machine. All this happens behind the scenes in a manner which is transparent to the end-user. In subsequent edits of that form the cached template will be used, unless the version number in the form is newer than the cached template version. In that case, the template will be automatically reloaded from the central location.

The above mechanism is great for online scenarios, when the template can be downloaded on-the-fly when a form is opened. But in cases where there is no network connectivity, things get a bit more complicated. It is possible to manually install a template (look up the REGFORM utility for more details), however doing so on a large number of computers and/or forms is a pain.

Enter the CacheSolution and RegisterSolution methods of the application object in InfoPath SP1. These methods enable programmatic installation of templates on the client. When deploying trusted forms use the RegisterSolution, for all others CacheSolution is usually enough. 

A sample usage scenario: Customer has 5000 Tablet PCs with Infopath installed. The Tablet users work offline (in the field) and periodically come into the office to sync with a central server. When they are online, they should automatically receive the latest versions of all the templates they work with, as well as any new templates that may have been published since their last sync. 

Sample solution: The following c# sample code snippet will download and install InfoPath templates from a Sharepoint document library. In the scenario above, it would be enough to save the customer templates to a WSS site and execute this snippet on the Tablets when users sync:

 private void CacheSolutionsFromWssSite(string siteURL, string docLibraryName)
  {

   WSS_WebService.Lists   wsList;
   InfoPath.Application   IPApp;

   XmlDocument    xmlDoc;
   XmlElement      query;
   XmlElement      viewFields;
   XmlElement      queryOptions;
   XmlNode           result;

   // instantiate InfoPath
IPApp = new InfoPath.Application();

// bind to web service
wsList = new WSS_WebService.Lists();
wsList.Credentials = System.Net.CredentialCache.DefaultCredentials;
wsList.Url = siteURL + "/_vti_bin/Lists.asmx";

   // prepare query of list items
string rowLimit = "100";
xmlDoc = new System.Xml.XmlDocument();
query = xmlDoc.CreateElement("Query");
viewFields = xmlDoc.CreateElement("ViewFields");
queryOptions = xmlDoc.CreateElement("QueryOptions");
queryOptions.InnerXml = "<queryOptions><ViewAttributes Scope=\"Recursive\" /></queryOptions>";

   // submit query to Lists web service
result = wsList.GetListItems(docLibraryName,"",query,viewFields,rowLimit,queryOptions);

// loop over the list item URLs
foreach(XmlNode xmlTemplate in result.SelectNodes("//*/@ows_EncodedAbsUrl"))
{
// if list item is a template (ends with .xsn)
if (xmlTemplate.InnerText.ToLower().EndsWith(".xsn"))
{
// Tell infopath to cache it
IPApp.CacheSolution(xmlTemplate.InnerText);
}
}
}

There isn't a whole lot of documentation about this - in fact, Google is speechless when it comes to Application.CacheSolution. This post is based primarily on my experiences of trial-and-error. It worked for me but your milleage may vary, and as with any code sample - this comes without warranty and you use this at your own risk.

If this doesn't work for you, leave me a comment with some details and I'll try to figure out why. And you can leave a comment even if does work :)