Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically include a file in my project?

Background

I'm making a helper application that reformats some code files and creates new code files, which are to be added to my other project, so I could use the new code right away, but I'm having serious trouble adding that new code file into my project automatically. By the way it's in c# and the helper app is WinForms.

Failed attempts

This question's only answer has two ways of doing that, but I couldn't make any of them work. With the first I can't find a Microsoft.Build assembly to reference, and in the other there are clearly not enough arguments for a command line.

Question

How do I programmatically include a file into a project without the use of third-party applications?

Basically, I'm looking for the equivalent of this:

...But done using code.

Requirements

These are the features I suppose the solution should offer:

  • Select the solution which has the project we're adding the file to
  • Select project into which the file is to be added
  • Select directory within the project
  • And, of course, the file which we're adding

Progress

With user @psubsee2003's help I was able to find the Microsoft.Build.dll file in C:\Windows\Microsoft.NET\Framework\v4.0.30319 folder on my computer and successfully import it by changing my project's target framework to version 4 Full profile, not the default Client profile.

And I found how to use the AddItem method:

var p = new Microsoft.Build.Evaluation.Project(@"C:\projects\MyProject.csproj");
p.AddItem("Compile", @"C:\folder\file.cs");
p.Save();

The file will appear in project's root folder unless the project already had a folder called folder, in which case the file will be placed there. So basically the file will be placed in the deepest folder chain found in the original file's path going towards the root folder.

like image 923
user1306322 Avatar asked Aug 31 '13 04:08

user1306322


5 Answers

It worked for my just adding the it to the ProjectFolder, and also add the folder programmatically like this.

var p = new Microsoft.Build.Evaluation.Project(@"C:\projects\BabDb\test\test.csproj");
        p.AddItem("Folder", @"C:\projects\BabDb\test\test2");
        p.AddItem("Compile", @"C:\projects\BabDb\test\test2\Class1.cs");
        p.Save();
like image 105
Boot750 Avatar answered Nov 11 '22 08:11

Boot750


As a supplement to Boot750's answer (first suggested as an edit to his answer but re-posted as a standalone)

You should note that the call to new Microsoft.Build.Evaluation.Project(path) actually causes the project to be loaded into a global cache maintained by the Microsoft.Build.Evaluation assembly. Calling new Project(path) again with the same path, without unloading it from the global collection first/restarting your app will cause an exception like:

An equivalent project (a project with the same global properties and tools version) is already present in the project collection, with the path "YOUR_PATH". To load an equivalent into this project collection, unload this project first.

even if the variable p has gone out of scope.

You might hence need to adopt a pattern more like this, if you plan to use the Load/AddItem/Save pattern repeatedly:

 var p =
Microsoft.Build.Evaluation
  .ProjectCollection.GlobalProjectCollection
    .LoadedProjects.FirstOrDefault(pr => pr.FullPath == projFilePath);

if (p == null)
   p = new Microsoft.Build.Evaluation.Project(projFilePath);
like image 42
Caius Jard Avatar answered Nov 11 '22 07:11

Caius Jard


Just to add to the response from @caius-jard

Once the project is in the GlobalProjectCollection it's held in memory and does not reflect any manual changes made to the project. This includes updating the .csproj file and using VS to remove the generated files.

If you remove the generated file and run the code again the project will update to contain 2 of the generated file. Use the ReevaluateIfNecessary() function to update the instance of the project before use like this:

var p = Microsoft.Build.Evaluation
        .ProjectCollection.GlobalProjectCollection
        .LoadedProjects.FirstOrDefault(pr => pr.FullPath == projFilePath);

if (p == null)
    p = new Microsoft.Build.Evaluation.Project(projFilePath);

// Update instance of project
p.ReevaluateIfNecessary();

// Check folder is not already in the project
var folderLoc = @"C:\projects\BabDb\test\test2";
if(p.Items.FirstOrDefault(i => i.EvaluatedInclude == folderLoc) == null)
    p.AddItem("Folder", folderLoc);

// Check file is not already in the project
var fileLoc = @"C:\projects\BabDb\test\test2\Class1.cs";
if(p.Items.FirstOrDefault(i => i.EvaluatedInclude == fileLoc) == null)
    p.AddItem("Compile", fileLoc);

p.Save();
like image 28
Aetbmo Avatar answered Nov 11 '22 09:11

Aetbmo


The simplest way to do this is to modify the Project file. As it is just an MSBUILD file, VS will pick up the change and prompt up to reload the project. Load the project file as an XML and find the first <Compile Include="Name of File.cs" />. Insert a new <Compile Include="NewFile.CS" /> and your done.

As another option you can remove all the <Compile> tags and replace them with <Compile Include="*.cs" />

like image 20
rerun Avatar answered Nov 11 '22 07:11

rerun


I know that this is an old post. However, I was in need of this option and didn't find the suitable one as I wished. So, I developed a VS extension with extended features. You can set the file types, and then, select the list of projects' directory that you want their files to be loaded into the project once they get generated.

Market (free) | Source-Code

like image 1
AbuDawood Avatar answered Nov 11 '22 08:11

AbuDawood