如何以编程方式创建 Visual Studio 解决方案 (.sln) 文件,包括网站项目?

发布于 2024-12-25 01:35:18 字数 409 浏览 4 评论 0原文

我正在开发一个代码生成器项目,该项目创建具有可自定义层的解决方案。 现在我可以通过在代码中编写所有元素来创建 .sln 文件。但项目文件不需要这个,可以使用 MSBuild Project 类来编辑它们。

我想添加网站项目模板支持等,所以这种方式编辑 .sln 文件并不酷,我想知道是否有更好的方法来做到这一点,例如 MSBuild 或其他方法?

我看到 以编程方式生成 Visual Studio 解决方案 说要使用 Visual Studio SDK(用于扩展 Visual Studio、编写插件...),但没有任何代码示例。

提前致谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

云柯 2025-01-01 01:35:18

这里介绍如何使用SLNTools在代码中创建sln文件。代码很乱!但它有效!

SLN Tools 项目能够从项目文件创建 sln 文件。它是开源的,并提供用于编辑和合并 sln 文件的 UI。在这种情况下,您可以使用核心项目,并通过在项目中引用“CWDev.SLNTools.Core.dll”,您可以编写如下所示的混乱(!)代码:

注意:类的某些部分此处未显示,还有我自己的项目的一些自定义对象,如 Sln、Project 等,不应与同名的 SLNTools 对象混淆。需要的地方我都评论了!

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;
    }
}

希望有帮助。

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.

伴随着你 2025-01-01 01:35:18

您可以使用 IVsSolution 界面来创建解决方案并向其中添加项目。您将 SVsSolution 类型传递给GetService()方法来获取该接口的实例,如下所示:

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

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));
救赎№ 2025-01-01 01:35:18

在我的解决方案文件中,网站项目存储在 WebsiteProperties 项目部分中。
根据构建配置,这些属性将作为输入参数传递到 aspnet_compiler。尝试在包含网站项目的 sln 文件中搜索 projectID (GUID)(在本例中为 C9683FA5-AABA-497F-B780-80EFC3EB81F2),sln 文件的哪些部分应由代码生成器更新/生成。

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

您应该更新(至少)ProjectConfigurationPlatforms & NestedProjects 全局部分。在我的例子中这是需要的。

我已经实现了合并解决方案 MsBuild 任务。它支持将多个解决方案文件合并到一个 MergedSolution.sln 文件中。它适用于我们的 20 多个解决方案文件,其中包含 500 多个项目(c# 和网站以及 sql db 项目文件/部分)。输出解决方案文件大小为 1.3MB :)

编辑:
SLN Tools 项目 能够从项目文件创建 sln 文件。

如何使用自定义 MSBuild 任务生成 SLN 文件:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文