SharePoint developers and administrators are familiar with the concept of creating and deploying solution packages (.wsp files) to the farm using the stsadm command line utility. In some cases, (for example, when deploying an admin approved InfoPath Forms Services template), we may no longer have a local copy of the solution package. Ideally, we would export a copy of the solution from the SharePoint solution store. While the SharePoint object model provides a developer with a straightforward way to obtain solution packages out of the solution store, this doesn’t really help a SharePoint administrator. So how do we empower the SharePoint administrator with the means necessary to export an installed SharePoint solution? The answer; develop an extension for the stsadm command line utility.

Microsoft has done a fine job of making the stsadm command line extendable. We will create a class that implements the ISPStsadmCommand interface and implement the two methods defined by this interface; GetHelpMessage and Run. The GetHelpMessage method gets a remark about the syntax of the specified custom operation, while the Run method executes the specified operation.

We will be adding an operation named exportsolution, so we will begin by defining the desired command syntax:

stsadm –o exportsolution –name SomeSolution.wsp –filename "c:\SomeSolution.wsp"

The Run method is the driver behind any operation in your custom command handler. The method accepts three parameters

  • command: String - The name of the custom operation. In our case this will be a single value, exportsolution, however you can use the same handler to process multiple commands.
  • keyValues: StringDictionary – The parameters, if any, that are added to the command line. In our implementation there will be two
    • name – The name of the .wsp file in the solution store
    • filename – The destination file path of the exported solution
  • output: String – An output string, if needed. We will use this parameter to echo the success or failure of any operations to the command window.

The Run method also has some specific requirements around the return value of the method. The output argument is used for any message, however the integer return value of the Run method is what is used to determine the outcome of the command:

  • 0: The command executed successfully. The value of the output variable is streamed to the console window
  • -1: General error
  • -2: Syntax error. The command was not properly formatted. The result of the GetHelpMessage method is streamed to the console window

The Run method and the PerformSolutionExport method definitions are shown below. The actual exporting is handled by PerformSolutionExport so that you can see how you could use this handler to perform the execution of a number of commands.

public int Run(string command, StringDictionary keyValues, out string output)
{
    string cmd    = command.ToLowerInvariant();
    int    retVal = 0;

    output = string.Empty;

    /* */
    switch (cmd)
    {
        case "exportsolution":

            retVal = this.PerformSolutionExport(keyValues, out output);

            break;
    }

    return retVal;
}

private int PerformSolutionExport(StringDictionary keyValues, out string output)
{
    SPFarm farm   = null;
    int    retVal = 0;

    output = string.Empty;

    /* Validate arguments */
    if (!keyValues.ContainsKey("name") || 
        !keyValues.ContainsKey("filename"))
    {
        output = "Parameters -name and -filename must be specified";
        return -2;
    }            

    /* Perform the export */
    try
    {
        farm = SPFarm.Local;

        /* Iterate the solutions in the farm */
        foreach (SPSolution sln in farm.Solutions)
        {
            if (string.Compare(sln.Name, keyValues["name"], true) == 0)
            {
                sln.SolutionFile.SaveAs(keyValues["filename"]);

                output = string.Format(
                                    "Solution {0} successfully exported to {1}", 
                                    keyValues["name"], 
                                    keyValues["filename"]);

                retVal = 0;

                break;
            }
        }
    }
    catch (Exception ex)
    {
        output = ex.Message;

        retVal = -1;
    }

    return retVal;
}

Now that we have the implementation in place, we need to register the command handler. Using VSeWSS, you will add this XML file as a root file to your project. This file will reside in the CONFIG folder in the RootFiles folder in the project. 

<?xml version="1.0" encoding="utf-8" ?>
<commands>
  <command name="exportsolution"
           class="Aptera.Samples.SharePoint.ExportSolution.ExportSolutionHandler, Aptera.Samples.SharePoint.ExportSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a2ddba277d0aab8c" />
</commands>

There you have it. We've now created code necessary to export a solution from the SharePoint solution store. The sample code for this post is also available for download.