VBA.ExcelとC#で連携-爆速.xlsm-

同じMicrosoftなのだから連携できそう➡出来た！という話。
重たいExcelはもう許さない。
慣れた関数やグラフを使用したいユーザーのワガママに応えてみよう。
## Wscript.Shell
簡単にいうとC#（他の言語でも可）で作成したexeファイルをExcelから呼び出す。
大量データで固まる処理を非同期化したり、Excelでは不可能だった処理を描画することができる。
```:全体の流れ
ExcelからC#アプリ.exeを呼び出す（Excelは操作不能にする）
➡ C#アプリ.exeが処理しExcelへ結果を描画
➡ C#アプリ.exeを閉じる
➡ 操作をExcelに戻す
```

## 注意事項
:::note alert
#### ※操作後、Excelプロセスは必ず解放すること
この意味と危険性が理解できなければ引き返してほしい。
アプリを閉じても解放されないプロセスがいつまでも残ることになる…。
```C#:
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
```
:::
詳しくは

https://learn.microsoft.com/ja-jp/archive/blogs/office_client_development_support_blog/office-5

過激派

https://drerium.com/2018-03-17-122736/#google_vignette

## 手順1:Excelから呼び出すexeを作成する
VisualStudio2022
Windowsフォームアプリケーション(.NetFramework4.7.2)

プロジェクト＞NuGetパッケージの管理から以下をインストール。
Microsoft.Office.Interop.Excel
色々操作できるので試してみてほしい
```C#:
using Microsoft.Office.Interop.Excel;
using Excel = Microsoft.Office.Interop.Excel;
public partial class Form1 : Form
{
    Excel.Application excelApp;
    read_List = new List<string>();
} 
```

読込
```C#:ExcelRead()
    private void ExcelRead()
    {
        try
        {
            excelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
            Workbooks excelBooks = excelApp.Workbooks;
            //複数Excelによる事故防止
            if (excelBooks.Count > 1) return;
            Worksheet sheet = excelApp.Worksheets["読み込みたいシート名"];
            //使用されている最終行の取得
            int yCount = sheet.UsedRange.Rows.Count - 1;
            Range cells = sheet.Cells;
            Range id;
            for (int i = 0; i < yCount; i++)
            {
                //3行目の2列目から最終行まで文字があれば取得
                id = cells[3 + i, 2] as Range;
                string str = from_start.Text.ToString() ?? "";
                if (str != "")
                {
                    read_List.Add(str);
                }
            }
                
        }catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            Marshal.ReleaseComObject(excelApp);
        }
        //読込んだread_listを元に何かしら重い処理
    }               
```
書込
```C#:ExcelWrite()
private void ExcelWrite(System.Data.DataTable data)
{
    excelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
    Worksheet sheet = excelApp.Worksheets["任意のシート"];
    //任意のシートの後に新しいシートを追加しそこに結果を吐き出す
    Worksheet worksheet = excelApp.Worksheets.Add(After:sheet);
    List<int> times = new List<int>();
    //DataTable内のデータをExcelに書込む
    String[,] pData = new String[data.Rows.Count, data.Columns.Count];
    for (int row = 0; row < data.Rows.Count; Row++)
    {
        //カラム分ループ
        for (int column = 0; column < data.Columns.Count; Column++)
        {
             pData[Row, column] = data.Rows[Row][column].ToString();
             if(column = 3) times.Add(sum);
        }
        
    }
    //シートのフォーマット仕上げ
    Excel.Range Rng = (Excel.Range)worksheet.Cells[2, 2];
    Rng = Rng.get_Resize(base_data.Rows.Count, base_data.Columns.Count);
    //多次元配列をそのまま書き込む
    Rng.Value = pData;
    //カラムに合わせて高さ幅を仕上げる
    Range ran = worksheet.Range["A1:J1"];
    ran.Columns.AutoFit();

    //書き込んだ後でも処理ができる
     int yCount = worksheet.UsedRange.Rows.Count - 1;
     
    //時間の統計　
    Double sums = times.Average();
    TimeSpan ti = TimeSpan.FromMinutes(sums);
    worksheet.Cells[yCount, 3].Value = new DateTime(0).Add(ti).ToString("HH:mm");
    //シートの名前変更
    worksheet.Name = "変更後の名前";
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    } finally
    {
        Marshal.ReleaseComObject(excelApp);
    }
}
```
## 手順2:Excelからexeを呼び出す
任意の階層に作成したexeファイルを置く。
ボタンや任意のタイミングで呼び出せばいい。
```vb:Excel
'イベントを無効
Application.EnableEvents = False
With CreateObject("Wscript.Shell")
        .Run ThisWorkbook.Path & "/C#あぷり.exe",
End With
Application.EnableEvents = true
```

## おまけ
Excel➡C#が起動できる。
なら
逆にC#アプリケーションからExcelを特定し書き込むこともできる。