JDennis
JDennis

Reputation: 682

Programmatically adding files to the FileSystem in a .NET Install Project

The Problem: In have a process that builds our executable. Before running the build of the setup/deploy project, I build files that need to be included in the FileSystem/Common Documents Folder of the setup/deploy project. In cases, where the file name is always created as the same file name, this is quite simple; however, I have a case where I cannot predetermine the file name since the process can and will create a unique file name on each successive run.

The Question: How can I programmatically add files to the FileSystem/Common Documents Folder?

My Research: I have looked into Custom Actions but am uncertain how to reference the FileSystem of the setup/deploy project so that I may add these files.

Further Detail As part of our daily build process, we create http://lucene.apache.org/(Lucene) indexes where files in the form of *.cfs can have different file names from the previous day. Since we do not wish to open the vdproj file in Visual Studio and replace the filename manually using the file system editor, we needed a more automated approach.

Our Solution As a solution, I used http://www.ewaldhofman.nl/post/2011/04/06/Customize-Team-Build-2010-e28093-Part-16-Specify-the-relative-reference-path.aspx(Ewald Hofman's) excellent tutorial on TFS Team Build. In this, I replicated his check-out activity and check-in activity while added my own custom activity that opens the vdproj file and edits the file according to the pre-generated Lucene index file names.

Code Example


    protected override void Execute(CodeActivityContext context)
    {

        string fileFullName = context.GetValue(this.FileFullName);

        string dropFolder = context.GetValue(this.DropLocation);

        string[] indexerNames = context.GetValue(this.LuceneIndexes);

        try
        {
            //read the vdproj file into memory
            string text = File.ReadAllText(fileFullName);

            //for each lucene index folder 
            foreach (string index in indexerNames)
            {

                //traversing twice so that the lucene index and spell index can be handled
                //these are subfolder names we use to segregate our normal lucene index from our spelling indexes.
                foreach (string subFolderInLuceneIndex in new string[] { "en_US_9", "en_US_9_sp" })
                {

                    //retrieve all the files in folder \\[DropFolder]\[index]\[subFolderInLuceneIndex]\*.cfs
                    foreach (string file in Directory.GetFiles(System.IO.Path.Combine(dropFolder, index, subFolderInLuceneIndex), "*.cfs"))
                    {
                        FileInfo cfsFile = new FileInfo(file);

                        context.TrackBuildMessage(string.Format("Exiting file in lucene index directory: {0}", cfsFile.FullName));

                        string fileNamePattern = ".+.cfs";

                        string div = Dividor(4);

                        //matching pattern for sourcepath ie("SourcePath" = "8:\\\\...\\[index]\\[subFolderInLuceneIndex]\\_0.cfs")
                        string sourcePattern = string.Format("(\".+{1}{0}{2}{0}{3})", div, index, subFolderInLuceneIndex, fileNamePattern);

                        //matching pattern for targetname ie("TargetName" = "8:_0.cfs")
                        string targetPattern = string.Format("(\"TargetName\"\\s=\\s\"8:{0})", fileNamePattern);

                        StringBuilder sb = new StringBuilder();
                        sb.Append(sourcePattern);
                        sb.Append("(.+\\r\\n.+)"); //carriage return between targetpattern and sourcepattern
                        sb.AppendFormat(targetPattern);

                        //(.+[index]\\\\[subFolderInLuceneIndex].+.cfs)(.+\r\n.+)(TargetName.+8:.+.cfs)

                        MatchCollection matches = Regex.Matches(text, sb.ToString(), RegexOptions.Multiline);
                        //if more than one match exists, a problem with the setup and deployment file exists
                        if (matches.Count != 1)
                        {
                            throw new Exception("There should exist one and only one match.");

                        }
                        else
                        {

                            foreach (Match match in matches)
                            {
                                string newText = text;

                                string existingPattern = match.Value;

                                if (match.Groups != null)
                                {
                                    //if the value found using the match doesn't contain the filename, insert the filename
                                    //into the text
                                    if (!match.Value.Contains(cfsFile.Name))
                                    {
                                        //matched by sourcePattern
                                        string sourceValue = match.Groups[1].Value;

                                        //matched by targetPattern
                                        string targetNameValue = match.Groups[3].Value;

                                        int idIndex = targetNameValue.IndexOf("8:") + 2;

                                        //get the old *.cfs file name
                                        string oldFileName = targetNameValue.Substring(idIndex, targetNameValue.Length - idIndex);

                                        //replace old cfs file name with new cfs file name in the target pattern
                                        string newTargetNameValue = Regex.Replace(targetNameValue, oldFileName, cfsFile.Name);

                                        //replace old cfs file name with new cfs file name in the source pattern
                                        string newSourceValue = Regex.Replace(sourceValue, oldFileName, cfsFile.Name);

                                        //construct the new text that will be written to the file
                                        StringBuilder newSb = new StringBuilder();
                                        newSb.Append(newSourceValue);
                                        //account for the quote, carriage return and tabs. this ensures we maintain proper 
                                        //formatting for a vdproj file
                                        newSb.Append("\"\r\n\t\t\t");
                                        newSb.AppendFormat(newTargetNameValue);

                                        newText = Regex.Replace(text, sb.ToString(), newSb.ToString(), RegexOptions.Multiline);

                                        File.WriteAllText(fileFullName, newText);

                                        context.TrackBuildMessage(string.Format("Text {0} replaced with {1}.", oldFileName, cfsFile.Name));

                                    }
                                    else
                                    {
                                        context.TrackBuildMessage("No change applied for current file.");
                                    }
                                }
                            }
                        }
                    }
                }

            }

        }
        catch (Exception ex)
        {

            context.TrackBuildError(ex.ToString());

            throw ex;
        }
    }
    private static string Dividor(int n)
    {
        return new String('\\', n);
    }

Upvotes: 2

Views: 575

Answers (1)

Jyoti Prakash Lal
Jyoti Prakash Lal

Reputation: 26

You need to add required information to .V?PROJ (like in C++ you have file extension VDPROJ). Required information to be added into File section of the installer file. It is not a single step process, please go through a installer project file (VDPROJ file) and understand it.

Upvotes: 1

Related Questions