PowerShellでVisualStudioの操作を自動化

## 概要
VisualStudio(以下 VS)をPowerShellで操作する方法を紹介します。
色んな操作を自動化したりできるので便利です。
Jenkinsおじさんに使わせたりしても便利だと思います。

## 経緯
ディレクトリを再帰的に検索して発見したプロジェクトファイルをソリューションに加えていっていくのを自動化したかった↓
![mov1.gif](https://qiita-image-store.s3.amazonaws.com/0/275289/93600149-72b9-7673-dac3-dc0177fcd237.gif)
↓これのgit
https://github.com/Y0KUDA/projects2sln

## 下準備
### VSにアタッチする
アタッチする方法は二つあります。

* 起動しているVSにアタッチする
* 機動と同時にアタッチする

起動しているVSにアタッチする場合  

```powershell
PS > $VSDTE = [Runtime.InteropServices.Marshal]::GetActiveObject("VisualStudio.DTE")
```
起動と同時にアタッチする場合

```powershell
PS > $VSDTE=New-Object -ComObject VisualStudio.DTE
```
これで変数`$VSDTE`を介してVSにアクセスできます。

起動と同時にアタッチする方法を使う場合、ウインドウが可視化されません。
可視化しなくても操作はできますが、可視化する場合は以下の操作をします。

```powershell
PS > $VSDTE.MainWindow.Visible=$True
```





### ステータスバーに文字を表示する
StatusBar.Textの値を書き換えます。

```powershell
PS > $VSDTE.StatusBar.Text="HelloWorld"
```
こんな感じに表示が変更されれば成功です。正常にアタッチできています。
![スクリーンショット (4).png](https://qiita-image-store.s3.amazonaws.com/0/275289/e0a271a1-8e84-f16d-9e9d-e7ae152fe145.png)
この方法を使えばステータスバーにSUSHIを流すことができます。
![bar.gif](https://qiita-image-store.s3.amazonaws.com/0/275289/976c9adf-5833-e510-bd20-f01e53228088.gif)
↓これのGit
https://github.com/Y0KUDA/VisualStudioSUSHI

## ソリューションの操作
### ソリューションを作成する
`Solution.Create(string,string)`関数を使います。
第一引数はファイルの保存先、第二引数はソリューションの名前です。

```powershell
PS > $VSDTE.Solution.Create("C:\Users\yokuda\Desktop\PROJECTS","hello")
```
VSでソリューションを保存すると第一引数に指定した場所に保存されます。

### ソリューションを保存する

`Solution.SaveAs(string)`関数を使います。
引数のパスにソリューションファイルが生成されます。

```powershell
PS > $VSDTE.Solution.SaveAs("C:\Users\yokuda\Desktop\PROJECTS\hello.sln")
```


### ソリューションを開く
`Solution.Open(string)`関数を使います。
引数のパスにあるソリューションファイルを開きます。

```powershell
PS > $VSDTE.Solution.Open("C:\Users\yokuda\Desktop\PROJECTS\hello.sln")
```

### ソリューションのビルド
`Solution.SolutionBuild.Build(bool)`関数を使います。

```powershell
PS > $VSDTE.Solution.SolutionBuild.Build($False)
```
引数を`$True`にするとビルドが終了するまで関数がコントロールを保持します。
ビルドが終了するのを待ちたい場合に有効でしょう。

### ソリューションフォルダの作成
`Solution.AddSolutionFolder(string)`関数を使います。
引数に指定した名前のフォルダが作成されます。
testという名前のフォルダを作成する例を紹介します。

```powershell
PS > $VSDTE.Solution.AddSolutionFolder("test")
```

### ソリューションフォルダの検索
NewFolder1というソリューションフォルダを検索する例を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? FileName -eq "" |  # プロジェクトはファイル名が存在するがフォルダは存在しない。
         ? Name -eq "NewFolder" |  # NewFolder1で検索
         % Name # NewFolder1にアクセスできる。
```

### ソリューションフォルダに対する操作
ソリューションフォルダに対してもプロジェクトの追加やフォルダの作成などの操作が可能です。
NewFolder1にNewFolder2を追加する例を紹介します。
Objectを介してアクセスする必要があるので少しクセがあります。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? FileName -eq "" |
         ? Name -eq "NewFolder1"|
         % {$_.Object.AddSolutionFolder("NewFolder2")}
```



## プロジェクトの操作
### プロジェクトをソリューションに作成する
`Solution.AddFromTemplate(string,string,string)`関数を使います。
第一引数はテンプレートファイルのパス、第二引数はプロジェクトを保存するパス、第三引数はプロジェクトの名前です。

```powershell
PS > $VSDTE.Solution.AddFromTemplate("C:\Users\yokuda\Desktop\PROJECTS\Template1\ConsoleApplication1.vcxproj","C:\Users\yokuda\Desktop\PROJECTS\","hello")
```

### 既存プロジェクトの追加
`Solution.AddFromFile(string)`関数を使います。
引数のパスにあるプロジェクトファイルを開きます。

```powershell
PS > $VSDTE.Solution.AddFromFile("C:\Users\yokuda\Desktop\PROJECTS\ConsoleApplication1\ConsoleApplication1.vcxproj")
```

### 各プロジェクトへのアクセス
`Solution.Projects`を使います。すべてのプロジェクトが配列に格納されています。
特定のプロジェクトにアクセスするためには、検索する必要があります。
ここではプロジェクト名を使って特定する例を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
               ? Name -eq "ConsoleApp1" | # Name==ConsoleApp1でフィルタ
               % FullName #この行でConsoleApp1にアクセスできる
```

### プロジェクトの保存
`Solution.Projects[N].Save()`関数を使います。
Nは目的のプロジェクトが格納された配列のインデックスを指定してください。
ConsoleApp1を保存する例を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? Name -eq "ConsoleApp1" |
         % Save
```

### プロジェクトの名前を変更する
`Solution.Projects[N].Name`を変更します。
Nは目的のプロジェクトが格納された配列のインデックスを指定してください。
ConsoleApp1をApp1に変更する例を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? Name -eq "ConsoleApp1" |
         % { $_.Name="App1"}
```
### プロジェクトのビルド
`Solution.SolutionBuild.BuildProject(string,string)`関数を使います。
第一引数がビルドのコンフィグです。デフォルトでRelease/Debugが選べるやつです。
第二引数がプロジェクトのユニークネームです。これは`$VSDTE.Solution.Projects[N].UniqueName`で取得することができます。
App1プロジェクトをビルドする例を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? Name -eq "App1" |
         % { $VSDTE.Solution.SolutionBuild.BuildProject("Release",$_.UniqueName)}
```

### プロジェクトにファイルを追加する
`Solution.Projects[N].Object.AddFile(string)`関数を利用します。
App1にC++ソースコードを追加する方法を紹介します。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? Name -eq "App1" |
         % {$_.Object.AddFile("C:\Users\yokuda\Desktop\PROJECTS\src.cpp")}
```
新しく生成する場合は、存在しないファイルへのパスを指定するとバグるのでファイルを作ってから実行するとうまくいきます。

```powershell
PS >  $VSDTE.Solution.Projects |
         ? Name -eq "App1" |
         % {   echo "" >> C:\Users\yokuda\Desktop\PROJECTS\src.cpp # かんたんtouch
               $_.Object.AddFile("C:\Users\yokuda\Desktop\PROJECTS\src.cpp") }
```

## 終了する
```powershell
PS >  $VSDTE.Quit()
```


## 参考
マイクロソフト公式ドキュメント
https://docs.microsoft.com/en-us/dotnet/api/envdte?view=visualstudiosdk-2017