8

Есть ли способ включить файл в проект из командной строки в vs2012?включить файл в проект из командной строки

Причина, почему я спрашиваю, потому что это очень сложно, чтобы включить любой новый файл я добавляю в папку проекта всякий раз, когда я использую некоторые другие IDE (например, ST3), или когда я сохранить файл из Photoshop и т.д.

Я использую Grunt для выполнения большого количества минимизации, конкатенации, работы ngmin на моих угловых сценариях и т. Д. Существует плагин grunt-shell, который позволяет grunt-задачам запускать команды оболочки (я уже использую его для разблокировки заблокированных файлов по TFS). Поэтому я подумал, что могу создать задачу, которая будет включать в себя проект для меня для любого нового добавляемого файла (путем просмотра определенной папки с помощью grunt-watch).

+0

Что вы ищете что два ответа ниже не предлагают? –

+0

@MarkRucker ~ еще несколько вариантов, я предпочитаю решение с C над одним с заменой конца csproj, потому что в большинстве проектов, над которыми я работал, есть всевозможные вещи, а последние не являются группами элементов. Но я думаю, что C в порядке, мне просто нужно больше описания от автора. – Roland

+0

Хорошо, я дам drphrozen день, чтобы добавить подробную информацию, так как его ответ составляет 90% пути. Если он не отвечает, я постараюсь ответить более подробно (если кто-то еще не вскочит). –

ответ

6

Это решение с использованием 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.

Here - это сообщение в блоге, объясняющее, почему этот страшный материал необходим.
И here - это еще одно, но на C#.

Еще одна вещь, которую стоит отметить. Я попытался использовать сборки EnvDTE, как и в .net, но я продолжал получать ошибку регистрации COM. Когда я начал использовать COM-объекты, все сработало. Я не знаю достаточно о COM, чтобы действительно рискнуть предположить, почему это так.

EDIT

Если вы получаете эту ошибку при попытке запустить скрипт из командной строки:

Execution Policy Error

Затем вам нужно запустить этот первый (вы должны только бежать он когда-либо.)

powershell -command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned"

Here - хорошее, подробное объяснение того, что делает эта команда.

+0

Я получаю сообщение об ошибке при запуске от CP как admin:' File .... не может быть загружен, потому что запущенные скрипты отключены в этой системе. ... ' – Roland

+0

О, хорошо, да, это, вероятно, ваша политика исполнения. Позвольте мне сделать несколько тестов, и я скоро вернусь с вами. –

+0

@rolandjitsu. См. Мое редактирование. –

3

Вы можете вручную редактировать файл проекта для добавления файлов.

Файл проекта имеет формат на основе xml, который вы можете редактировать symply. Попробуйте использовать grunt-text-replace для редактирования.

Вы можете заменить

</Project> 

с

<ItemGroup> 
    <Compile Include="PythonProjectFactory.cs" /> 
    </ItemGroup> 
</Project> 
3

Вы можете просто создать небольшой ех, чтобы справиться с этим для вас:

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
  • SomeFolder
    • Tester.cs

Использование: AddTfsItem.exe C:\Projects\Test\Test.csproj SomeFolder\Tester.cs

Уведомление, что файл должен быть по отношению к файлу .csproj.

+0

Не могли бы вы объяснить, что делает этот cs? Из того, что я вижу, я запускаю этот cs exe, пока он загружает путь к файлу csproj и файлу, в который я включаю? Я также должен держать этот exe внутри проекта? Или это файл, который я включаю в себя, который должен быть относительно proj? – Roland

+0

В файле cs используются файлы Build и TFS api (обычно устанавливаются вместе с VS), чтобы добавить файл в файл csproj и tfs. Правильно, следовательно, «Использование» :) Или вы можете сбросить exe в каталоге% path%. Все вращается вокруг файла csproj, поэтому файл, который вы хотите включить, должен относиться к файлу csproj. – drphrozen

+0

Более подробное описание того, как использовать этот файл, было бы замечательно. Должен ли я скомпилировать его и сохранить его как '.dll' или' .exe'? Нужны ли мне какие-либо зависимости (обычно при установке VS вы получаете TFS exe с его командами тоже, но что происходит, когда я этого не делаю?) ... Я хотел бы понять, что там происходит ... И что насчет конфликты? Что делать, если файл/файлы уже существуют в '.csproj'? – Roland

2

Я знаю, что это не полное решение, и я еще не тестировал его, но это может помочь.

Сначала я искал решение Powershell и нашел эти вопросы:

Они используют EnvDTE СОМ библиотека, содержащая объекты и элементы для Visual Studio core automation.

Я не смог найти каких-либо ресурсов, как использовать его, если это от 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 {...})); 
    }); 
+0

Будет ли он совместим с VS2012? В статье [this] (http://www.codeproject.com/Articles/36219/Exploring-EnvDTE) говорится, что это только 2005/2008 ... – Roland

+0

Мне было бы хорошо, если бы вы могли немного расширить desc, как продолжение функции 'fileAdder'. Я также смотрел на плагин edge, и я вижу, что вы можете использовать функцию вне grunt как отдельный файл '.cs'. Но как я могу написать код с помощью async? Я не совсем эксперт на C# :) – Roland

+0

Я еще не пробовал. Возможно, это потребует незначительных изменений, но я уверен, что это работает. Статья была написана до VS 2010, поэтому я полагаю, что они не тестировали ее с более высокой версией VS. – Victor

0

Вы также можете запустить скрипт 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"""