Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

include file in project from command line

Is there a way to include a file in project from command line in vs2012 ?

The reason why I'm asking is because is very frustrating to include any new file I add to the project folder whenever I use some other IDE ( like ST3 ) or when I save a file from Photoshop, etc.

I'm using Grunt for doing a lot of minifying, concatenation, running ngmin on my angular scripts, etc. There's a grunt-shell plugin that allows grunt tasks to run shell commands ( I'm already using it for unlocking locked files by TFS ). So I was thinking that I could create a task that would do the include in project for me for any new file I add ( by watching a certain folder with grunt-watch ).

like image 996
Roland Avatar asked Jun 29 '13 16:06

Roland


5 Answers

Here is a solution using PowerShell. It is a little long, but I promise it works. I tested quite a bit.

First, the easy part. Here's how you run the script from the command prompt.

powershell -File C:\AddExistingItem.ps1 -solutionPath "C:\Test.sln" -projectName "TestAddItem" -item "C:\Test.txt"

Now the scary part, AddExistingItem.ps1:

param([String]$solutionPath, [String]$projectName, [String]$item)

#BEGIN: section can be removed if executing from within a PowerShell window
$source = @" 

namespace EnvDteUtils
{ 
    using System; 
    using System.Runtime.InteropServices; 

    public class MessageFilter : IOleMessageFilter 
    { 
        // 
        // Class containing the IOleMessageFilter 
        // thread error-handling functions. 

        // Start the filter. 
        public static void Register() 
        { 
            IOleMessageFilter newFilter = new MessageFilter();  
            IOleMessageFilter oldFilter = null;  
            CoRegisterMessageFilter(newFilter, out oldFilter); 
        } 

        // Done with the filter, close it. 
        public static void Revoke() 
        { 
            IOleMessageFilter oldFilter = null;  
            CoRegisterMessageFilter(null, out oldFilter); 
        } 

        // 
        // IOleMessageFilter functions. 
        // Handle incoming thread requests. 
        int IOleMessageFilter.HandleInComingCall(int dwCallType,  
          System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr  
          lpInterfaceInfo)  
        { 
            //Return the flag SERVERCALL_ISHANDLED. 
            return 0; 
        } 

        // Thread call was rejected, so try again. 
        int IOleMessageFilter.RetryRejectedCall(System.IntPtr  
          hTaskCallee, int dwTickCount, int dwRejectType) 
        { 
            if (dwRejectType == 2) 
            // flag = SERVERCALL_RETRYLATER. 
            { 
                // Retry the thread call immediately if return >=0 &  
                // <100. 
                return 99; 
            } 
            // Too busy; cancel call. 
            return -1; 
        } 

        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,  
          int dwTickCount, int dwPendingType) 
        { 
            //Return the flag PENDINGMSG_WAITDEFPROCESS. 
            return 2;  
        } 

        // Implement the IOleMessageFilter interface. 
        [DllImport("Ole32.dll")] 
        private static extern int  
          CoRegisterMessageFilter(IOleMessageFilter newFilter, out  
          IOleMessageFilter oldFilter); 
    } 

    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),  
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 
    interface IOleMessageFilter  
    { 
        [PreserveSig] 
        int HandleInComingCall(  
            int dwCallType,  
            IntPtr hTaskCaller,  
            int dwTickCount,  
            IntPtr lpInterfaceInfo); 

        [PreserveSig] 
        int RetryRejectedCall(  
            IntPtr hTaskCallee,  
            int dwTickCount, 
            int dwRejectType); 

        [PreserveSig] 
        int MessagePending(  
            IntPtr hTaskCallee,  
            int dwTickCount, 
            int dwPendingType); 
    } 
} 
"@ 

Add-Type -TypeDefinition $source      

[EnvDTEUtils.MessageFilter]::Register()
#END: section can be removed if executing from within a PowerShell window

$IDE = New-Object -ComObject VisualStudio.DTE

$IDE.Solution.Open("$solutionPath")

$project = $IDE.Solution.Projects | ? { $_.Name -eq "$projectName" }
$project.ProjectItems.AddFromFile("$item") | Out-Null
$project.Save()

$IDE.Quit()

#BEGIN: section can be removed if executing from within a PowerShell window
[EnvDTEUtils.MessageFilter]::Revoke()
#END: section can be removed if executing from within a PowerShell window

95% of the code is only there to let you run from the command prompt. If you are writing and running code directly in PowerShell you can leave it out and go straight to $IDE = New-Object -ComObject VisualStudio.DTE.

Here is a blog post explaining why that scary stuff is needed.
And here is another one on the same thing but in C#.

Another thing worth noting. I tried using the EnvDTE assemblies, like you would in .net, but I kept getting a COM registration error. Once I started using the COM objects directly everything worked. I don't know enough about COM to really venture a guess as to why this is.

EDIT

If you receive this error when trying to run the script from the command prompt:

Execution Policy Error

Then you need to run this first (you should only need to run it once ever.)

powershell -command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned"

Here is a good, in-depth explanation of what that command is doing.

like image 159
Mark Rucker Avatar answered Oct 19 '22 11:10

Mark Rucker


You can also run powershell script against your project file. We are using this for a long time.

AddContentToProject.ps1

    # Calling convension:
    #   AddContentToProject.PS1 "Mycsproj.csproj", "MyFolder/MyFile.txt"
    param([String]$path, [String]$include)

    $proj = [xml](Get-Content $path)
    [System.Console]::WriteLine("")
    [System.Console]::WriteLine("AddItemToProject {0} on {1}", $include, $path)

    # Create the following hierarchy
    #  <Content Include='{0}'>
    #  </Content>

    $xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"
    $itemGroup = $proj.CreateElement("ItemGroup", $xmlns);
    $proj.Project.AppendChild($itemGroup);

    $contentNode = $proj.CreateElement("Content", $xmlns);
    $contentNode.SetAttribute("Include", $include);
    $itemGroup.AppendChild($contentNode)

    $proj.Save($path)

Then run this with powershell

.\AddContentToProject.ps1 "Mycsproj.csproj" "MyFolder/MyFile.txt"

Or this from command prompt

powershell -noexit "& ""C:\my_path\AddContentToProject.ps1"""
like image 35
Kerem Demirer Avatar answered Sep 20 '22 19:09

Kerem Demirer


You could just create a small exe to handle this for you:

using System.IO;
using Microsoft.Build.Evaluation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace AddTfsItem
{
  class Program
  {
    static void Main(string[] args)
    {
      var projectFile = args[0];
      var projectDirectory = Path.GetDirectoryName(projectFile);
      var sourceFile = args[1];
      var itemType = args.Length > 2 ? args[2] : "Compile";

      var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(projectFile);
      using (var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri))
      {
        var workspace = workspaceInfo.GetWorkspace(server);

        workspace.PendEdit(projectFile);

        using (var projectCollection = new ProjectCollection())
        {
            var project = new Project(projectFile, null, null, projectCollection);
            project.AddItem(itemType, sourceFile);
            project.Save();
        }

        workspace.PendAdd(Path.Combine(projectDirectory, sourceFile));
      }
    }
  }
}

Structure of C:\Projects\Test:

  • Test.csproj
  • SomeFolder
    • Tester.cs

Usage: AddTfsItem.exe C:\Projects\Test\Test.csproj SomeFolder\Tester.cs

Notice that the file has to be relative to the .csproj file.

like image 5
drphrozen Avatar answered Oct 19 '22 12:10

drphrozen


You can manualy edit project file to add files.

Project file has xml based format which you can edit symply. Try use grunt-text-replace for edit.

You can replace

</Project>

with

  <ItemGroup>
     <Compile Include="PythonProjectFactory.cs" />
  </ItemGroup>
</Project>
like image 3
Vasiliy Avatar answered Oct 19 '22 10:10

Vasiliy


I know this is not the complete solution and I haven't tested yet, but it may help.

First I searched for a Powershell solution and found this questions:

  • Add an existing project to solution folder using PowerShell.

  • Visual Studio Macro: Find files that aren't included

  • Add existing item in VS2010 in project programmatically

They use EnvDTE the COM library containing the objects and members for Visual Studio core automation.

  • ProjectItem Interface

  • ProjectItems.AddFromFile Method

I couldn't find any resources how to use it, unless this from CodeProject Exporing EnvDTE.

Project project;

//path is a list of folders from the root of the project.
public void AddFromFile(List<string> path, string file) {
    ProjectItems pi = project.ProjectItems;
    for (int i = 0; i < path.Count; i++) {
        pi = pi.Item(path[i]).ProjectItems;
    }
    pi.AddFromFile(file);
}

//path is a list of folders from the root of the project.
public void AddFolder(string NewFolder, List<string> path) {
    ProjectItems pi = project.ProjectItems;
    for (int i = 0; i < path.Count; i++) {
        pi = pi.Item(path[i]).ProjectItems;
    }
    pi.AddFolder(NewFolder, 
       EnvDTE.Constants.vsProjectItemKindPhysicalFolder);
}

//path is a list of folders from the root of the project.
public void DeleteFileOrFolder(List<string> path, string item) {
    ProjectItems pi = project.ProjectItems;
    for (int i = 0; i < path.Count; i++) {
        pi = pi.Item(path[i]).ProjectItems;
    }
    pi.Item(item).Delete();
}

Since you want to use Grunt for automating your tasks. You could take a look at Edge - .NET for Node.js.

require('edge');

...

var fileAdder = edge.func(/*
  using EnvDTE;
  using EnvDTE80;
  using EnvDTE90;
  using EnvDTE100;

  using VSLangProj;
  using VsLangProj80;
  using VsLangProj90;
  using VsLangProj100;

  async (dynamic input) => { 
    // The C# code for adding the File to the project
  }
*/);    

grunt.registerTask('addFileVS', 
  'Adds new files to Visual Studio Project', 
   function (solutionname, projectname, filename){
     var input = {
       solution: solutionname,
       project: projectname,
       file: filename
     };
     fileAdder(input, function(error, results {...}));
   });
like image 3
Victor Avatar answered Oct 19 '22 10:10

Victor