简体   繁体   中英

How to create Visual Studio Solution (.sln) files programmatically, including Website projects?

I am working on a code generator project that creates solution with customizable layers. for now I am able to create the .sln file by writing all the elements in code. but the project files don't need this, they can be edited using MSBuild Project class.

I want to add a Website project template support and etc, so this way I edit the .sln file is not cool, I wanted to know that is there a better way to do this, like MSBuild or something else ?

I saw Programmatically generate Visual Studio Solution that says to use Visual Studio SDK (which is for extending visual studio, writing plugins ...), but there isn't any code sample.

thanks in advance

Here is how to use SLNTools to create sln file in code. The code is a mess! But it works!

SLN Tools project is able to create sln file from project files. It's open source and it provides UI for editing and merging sln files. In this case you can use the core project and by referencing the 'CWDev.SLNTools.Core.dll' in your project you can write a messy(!) code like this:

NOTE : Some parts of class are not shown here and also There's some custom objects of my own project like Sln, Project and ... that should not be mistaken with SLNTools Objects with same name. I commented whereever needed!

using CWDev.SLNTools.Core;
using SlnFile = CWDev.SLNTools.Core.SolutionFile;
using SlnProject = CWDev.SLNTools.Core.Project;
using SeptaCodeGenerator.Core.IO;

public class SlnFileManager
{
    // * Note: Sln is of my own type.
    Sln _sln;
    // * SolutionFile is the type in SLNTools project that we use
    SlnFile _slnFile;

    public void WriteNewSlnFile(Sln slnObject)
    {
        _sln = slnObject;
        _slnFile = new SlnFile(); // or you can read sln file like below
        using (SolutionFileReader reader = new SolutionFileReader(_sln.ObjectFullPath))
            _slnFile = reader.ReadSolutionFile();
        AddHeaders();
        AddGlobalSections();
        // add projects
        List<SolutionNode> sns = _sln.GetAllSubItemsOf(typeof(SolutionFolder), typeof(ProjectNode));
        foreach (SolutionNode sn in sns)
            _slnFile.Projects.Add(CreateProject(sn));

        using (SolutionFileWriter writer = new SolutionFileWriter(_sln.ObjectFullPath))
            writer.WriteSolutionFile(_slnFile);
    }

    // this is how I create and add project sections, using Section and PropertyLine
    private SlnProject CreateProject(SolutionNode sn)
    {
        List<Section> projectSections = CreateProjectSections(sn);
        List<PropertyLine> versionControlLines = CreateVersionControlLines(sn);
        List<PropertyLine> projectConfigurationPlatformsLines = CreateProjectConfigurationPlatformsLines(sn);

        string parentGuid = (sn.Parent is Sln ? null : sn.Parent.InstanceGuidSt);
        string relativePath = (sn is Project) ? (sn as Project).ProjFile.ObjectRelativePath : sn.ObjectRelativePath;
        return new CWDev.SLNTools.Core.Project(_slnFile, sn.InstanceGuidSt, sn.TypeGuidSt, sn.Name, relativePath, parentGuid,
            projectSections, versionControlLines, projectConfigurationPlatformsLines);
    }

    // this method creates ProjectSections(WebsiteProperties)
    private List<Section> CreateProjectSections(SolutionNode project)
    {
        Section sec = null;
        var lines = new List<PropertyLine>();
        if (project is SolutionFolder)
        {
            List<SolutionNode> files = project.GetAllSubItemsOf(typeof(SolutionFile));
            foreach (SolutionNode item in files)
            {
                // * consideration required
                lines.Add(new PropertyLine(item.ObjectRelativePath, item.ObjectRelativePath));
            }
            if (lines.Count > 0)
                sec = new Section("SolutionItems", "ProjectSection", "preProject", lines);
        }
        // *** here is the code that I wrote to add website projects ***
        else if (project is WebSiteProject)
        {
            var website = project as WebSiteProject;
            if (_sln.IsUnderSourceControl)
            {
                    lines.AddRange(new PropertyLine[]
                    {
                        new PropertyLine("SccProjectName", "\"SAK\""),
                        new PropertyLine("SccAuxPath", "\"SAK\""),
                        new PropertyLine("SccLocalPath", "\"SAK\""),
                        new PropertyLine("SccProvider", "\"SAK\"")
                    });

            }
            if (_sln.SolutionVersion == SolutionFileVersion.V11VS2010)
                lines.Add(new PropertyLine("TargetFrameworkMoniker", "\".NETFramework,Version%3Dv4.0\""));
            else
                lines.Add(new PropertyLine("TargetFramework", "\"3.5\""));
            // add project references
            string projectReferences = "";
            foreach (Project proj in website.ReferencedProjects)
                projectReferences += proj.InstanceGuidSt + "|" + proj.ClassName + ".dll;";
            lines.Add(new PropertyLine("ProjectReferences", "\"" + projectReferences + "\""));

            string debugConf = "Debug.AspNetCompiler.";
            string releaseConf = "Release.AspNetCompiler.";
            string str = debugConf;
            int k = 0;
            while (k < 2)
            {
                // other properties
                lines.AddRange(new PropertyLine[]
                {
                    new PropertyLine(str + "VirtualPath", "\"/" + website.Name + "\""),
                    new PropertyLine(str + "PhysicalPath", "\"" + website.ObjectRelativePath + "\\\""),
                    new PropertyLine(str + "TargetPath", ("\"PrecompiledWeb\\" + website.Name + "\\\"")),
                    new PropertyLine(str + "Updateable", "\"true\""),
                    new PropertyLine(str + "ForceOverwrite", "\"true\""),
                    new PropertyLine(str + "FixedNames", "\"false\""),
                    new PropertyLine(str + "Debug", (k == 0 ? "\"True\"" : "\"False\""))
                });

                if (k++ == 0)
                    str = releaseConf;
            }
            Random rand = new Random();
            lines.Add(new PropertyLine("VWDPort", "\"" + rand.Next(1111, 65000).ToString() + "\""));
            lines.Add(new PropertyLine("DefaultWebSiteLanguage",
                website.Language == SeptaCodeGenerator.Core.Language.CodeLanguage.CSharp ? "\"Visual C#\"" : "\"Visual Basic\""));

            sec = new Section("WebsiteProperties", "ProjectSection", "preProject", lines);
        }
        var sections = new List<Section>();
        if (sec != null)
            sections.Add(sec);
        return sections;
    }
}

Hope it helps.

You can use the IVsSolution interface to create solutions and add projects to them. You pass in the SVsSolution type to the GetService() method to get an instance of that interface like so:

var solution = (IVsSolution)GetService(typeof(SVsSolution));

In my solution files, web site projects are stored in the WebsiteProperties project section. These properties are passed into the aspnet_compiler as input parameters depending on the build configuration. Try to search for projectID (GUID) (C9683FA5-AABA-497F-B780-80EFC3EB81F2 in this case) inside your sln files containing website project, what parts of sln file should be updated/generated by your code generator.

Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "MasterData.Host", "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host", "{C9683FA5-AABA-497F-B780-80EFC3EB81F2}"
    ProjectSection(WebsiteProperties) = preProject
        SccProjectName = "SAK"
        SccAuxPath = "SAK"
        SccLocalPath = "SAK"
        SccProvider = "SAK"
        TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5"
        ProjectReferences = "{C7C785E8-89E6-459A-969C-BFC2BC7E8ED2}|Porthus.Customs.Framework.Services.MasterData.ServiceImplementation.dll;{ADFA3898-854C-4058-8726-523EA8802FFD}|Porthus.Customs.Framework.Services.MasterData.FaultContracts.dll;{9A565742-4843-40A6-A2E3-2B432573D12B}|Porthus.Customs.Framework.Services.MasterData.MessageContracts.dll;{E8B5D2A1-E934-4C85-AB71-A2148050913A}|Porthus.Customs.Framework.Services.MasterData.ServiceContracts.dll;{3C752087-CED2-499F-AF3B-84E1548229CB}|Porthus.Customs.Services.MasterData.BusinessEntities.dll;{437C651C-3C8D-4B3D-B967-E58DBCD83EE4}|Porthus.Customs.Services.MasterData.BusinessLogic.dll;{59BBDE80-6F20-4FB6-A65B-68BC584AD2CB}|Porthus.Customs.Services.MasterData.DataAccess.dll;"
        Debug.AspNetCompiler.VirtualPath = "/MasterData.Host"
        Debug.AspNetCompiler.PhysicalPath = "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host\"
        Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\MasterData.Host\"
        Debug.AspNetCompiler.Updateable = "true"
        Debug.AspNetCompiler.ForceOverwrite = "true"
        Debug.AspNetCompiler.FixedNames = "false"
        Debug.AspNetCompiler.Debug = "True"
        Release.AspNetCompiler.VirtualPath = "/MasterData.Host"
        Release.AspNetCompiler.PhysicalPath = "..\..\Customs.Framework\Services\MasterDataService\Tests\MasterData.Host\"
        Release.AspNetCompiler.TargetPath = "PrecompiledWeb\MasterData.Host\"
        Release.AspNetCompiler.Updateable = "true"
        Release.AspNetCompiler.ForceOverwrite = "true"
        Release.AspNetCompiler.FixedNames = "false"
        Release.AspNetCompiler.Debug = "False"
        VWDPort = "1333"
        DefaultWebSiteLanguage = "Visual C#"
    EndProjectSection
EndProject

You should update (at least) ProjectConfigurationPlatforms & NestedProjects global sections. This was needed in my case.

I have implemented merge solution MsBuild task. It supports merging of multiple solution files into the one MergedSolution.sln file. It works in on our 20+ solution files containing 500+ projects (c# & website & sql db project files/sections). Output solution file size is 1.3MB :)

EDIT: SLN Tools project is able to create sln file from project files.

How to generate SLN file with custom MSBuild task:

  • Here is good info about solution and project files format.
  • If you have a set of project files, you can generate your solution file by custom MSBuild task (inside Execute method)
  • To read MSBuild project files use Microsoft.Build.BuildEngine.Project class (it is obsolete but I'm still using it) or Microsoft.Build.Evaluation.Project class

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM