10

有没有办法在 vs2012 的命令行中将文件包含在项目中?

我问的原因是因为每当我使用其他一些 IDE(如 ST3)或从 Photoshop 保存文件等时,包含我添加到项目文件夹中的任何新文件都非常令人沮丧。

我正在使用 Grunt 进行大量缩小、连接、在我的角度脚本上运行 ngmin 等。有一个 grunt-shell 插件允许 grunt 任务运行 shell 命令(我已经使用它来解锁 TFS 锁定的文件)。所以我想我可以创建一个任务,为我添加的任何新文件(通过使用 grunt-watch 观察某个文件夹)执行包含在项目中。

4

5 回答 5

8

这是使用 PowerShell 的解决方案。它有点长,但我保证它有效。我测试了很多。

首先,简单的部分。以下是从命令提示符运行脚本的方式。

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

现在是可怕的部分,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% 的代码只是为了让您从命令提示符运行。如果您是直接在 PowerShell 中编写和运行代码,您可以将其排除在外,直接转到$IDE = New-Object -ComObject VisualStudio.DTE.

是一篇博客文章,解释了为什么需要这些可怕的东西。
这是另一个关于同一件事的,但在 C# 中

还有一点值得注意。我尝试使用 EnvDTE 程序集,就像在 .net 中一样,但我一直收到 COM 注册错误。一旦我开始直接使用 COM 对象,一切正常。我对 COM 的了解还不够,无法真正冒险猜测这是为什么。

编辑

如果您在尝试从命令提示符运行脚本时收到此错误:

执行策略错误

然后你需要先运行它(你应该只需要运行一次。)

powershell -command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned"

是对该命令的作用的一个很好的、深入的解释。

于 2013-08-05T00:51:19.050 回答
5

您可以只创建一个小 exe 来为您处理此问题:

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

C:\Projects\Test 的结构:

  • 测试.csproj
  • 某个文件夹
    • 测试者.cs

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

请注意,该文件必须相对于 .csproj 文件。

于 2013-07-29T09:07:48.850 回答
3

您可以手动编辑项目文件以添加文件。

项目文件具有基于 xml 的格式,您可以简单地对其进行编辑。尝试使用 grunt-text-replace 进行编辑。

你可以更换

</Project>

  <ItemGroup>
     <Compile Include="PythonProjectFactory.cs" />
  </ItemGroup>
</Project>
于 2013-07-26T08:19:15.977 回答
3

我知道这不是完整的解决方案,我还没有测试过,但它可能会有所帮助。

首先,我搜索了一个 Powershell 解决方案,发现了这个问题:

他们使用EnvDTE包含用于Visual Studio 核心自动化的对象和成员的 COM 库。

我找不到任何资源如何使用它,除非来自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();
}

因为你想使用 Grunt 来自动化你的任务。您可以查看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 {...}));
   });
于 2013-08-03T09:56:37.977 回答
1

您还可以针对您的项目文件运行 powershell 脚本。我们已经使用了很长时间。

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)

然后用 powershell 运行

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

或者从命令提示符

powershell -noexit "& ""C:\my_path\AddContentToProject.ps1"""
于 2016-11-21T19:29:14.310 回答