1. pooshikin

    Posted

    pooshikin
Changes in title
+Excel VBAをJavaScriptに翻訳 その7
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,263 @@
+# はじめに
+[**前回**](https://qiita.com/pooshikin/items/adc5644fe43ee7c3360c)は、VBA翻訳結果のJavaScriptをコーディングしました。(サンプルとしてください)
+今回は、WindowsのCOMを利用したExcelのブックを更新するプログラムを作成します。
+Excelのブックを更新する手段として、GoogleのV8エンジン、npmのWin32ole、前回使用したExcelJSなどがありますが、あえてVC++(Visual Studio C++)で直接、Miscrosoft OfficeのCOMに対してブックを更新するプログラムを作ります。MicrosoftのOLEを利用するのが一番手っ取り早いのと、決して枯れた技術ではないので、改めて使用してみます。最終的には、このプログラムをnode.jsで呼ぶように再利用します。
+# 前提
+* OS : Windows7以上
+* PoweShellのターミナルで実行
+* VSCodeでコード編集
+* node.js環境構築済み
+* Visual Studio Comminnuty 2017のインストール済み
+ + VC++のCLRをインストールしておくこと
+
+![2019-02-18-14-34-53.png](https://qiita-image-store.s3.amazonaws.com/0/239597/804d82c6-7a40-0cd8-4058-91f0a82b8241.png)
+
+# ブックを更新するプログラムを作る
+
+|項目|内容|説明|
+|:--|:--|:--|
+|プログラム名|TomCRL.exe|ブックを更新するアプリ|
+|引数1|セル1|"A2"|
+|引数2|値1|問題箇所を□で伏字した文字列|
+|引数3|セル2|"A3"|
+|引数4|値2|解答の文字列|
+## 1. プロジェクト作成
+ファイル(F) → 新規作成(N) → プロジェクト(P)
+![2019-02-18-14-40-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/4e8dd744-c7ee-8bfc-ace9-79fae066c9ee.png)
+### プロジェクト名とソリューション名
+Visual C++ → CLR → CLRコンソールアプリケーション → 名前 (TomCLR入力) → 場所(任意のディレクトリ) → ソリューション名(TomCLR(自動で入力される)) → ソリューションのディレクトリ作成のチェックをはずす。
+![2019-02-18-14-47-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/8f103b73-0e49-9c64-df54-8ca454d5c629.png)
+### 参照の追加
+元のIDE画面 → ソリューションエクスプローラー → 参照にて右クリック →参照の追加を選択
+![2019-02-18-14-55-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/066483d8-3e20-1a5e-21b5-47b64dae9063.png)
+### Microsoft Excel 14.0 Object Library 1.7参照追加
+COM → タイプライブラリ → Microsoft Excel 14.0 Object Library 1.7 にチェック → OK
+![2019-02-18-15-01-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/8ab13f84-4819-e5a7-beba-0aaba317129f.png)
+#### 参照の確認
+参照にタイプライブラリ[Interrop.Microsoft.Office.Interop.Excel.1.7]が追加されます。
+![2019-02-18-15-06-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/2b2d3efa-23be-a15d-9e8f-a3f23ba9f4a9.png)
+これで、C++でExcelのタイプライブラリが使用可能となります。
+## 2. TomCRL.cppコーディング
+### ウィザードで作成されたTomCLR.cpp
+
+```C++
+#include "stdafx.h"
+
+using namespace System;
+
+int main(array<System::String ^> ^args)
+{
+ return 0;
+}
+```
+#### 1. Windowsの関数参照解決するため、インクルードにwindows.hを追加します。
+
+#### 2. Microsoft Office Interop参照解決するため、mainの前にusing namespace Microsoft::Office::Interop; を追加します。
+※ オブジェクトブラウザーからnamespaceを取得します。
+![2019-02-18-15-50-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/32e1de07-a735-7ac0-5fe2-cb0320e648f7.png)
+
+```C++
+#include "stdafx.h"
+#include "windows.h" // 1.
+
+using namespace System;
+using namespace Microsoft::Office::Interop; // 2.
+
+int main(array<System::String ^> ^args)
+{
+ return 0;
+}
+```
+#### 3.mainにExcelのインスタンスオブジェクト作成。
+```C++
+Excel::Application^ Xls = gcnew Excel::Application(); // 3.
+```
+#### 4. tomCRLの実行ディレクトリに配置するブック「temp.xlsx」のパスとブック名を編集します。
+ GetCurrentDirectoryで実行ディレクトリのパスを取得します。 パスの長さが不定なので、MAX_PATHのサイズを確保します。
+#### 5. ブック名「temp.xls」は固定、一応128バイトのサイズを確保します。
+ ※ TCHARをcharサイズで取扱います。(UNICODではなくマルチバイトとします。)
+ これをやると、128バイト以下のfile名の場合、後ろに空白が設定されるので空白をトリミングします。
+#### 6. パスとブック名を結合します。
+#### 7. ブックオープンのファイル名がString指定のため、TCHARをStringへ変換します。
+#### 8. 空白をトリミングします。
+```C++
+ // カレントパス
+ TCHAR path[MAX_PATH];
+ GetCurrentDirectory(MAX_PATH, path); // 4.
+
+ // ブック名
+ TCHAR file[128] = TEXT("\\temp.xlsx\n"); // 5.
+
+ // カレントパス + ブック名
+ lstrcat(path, file); // 6.
+
+ // TCHAR → String変換s
+ String^ pathname = gcnew String(path); // 7.
+
+ // 空白トリミング
+ String^ book = pathname->Trim(); // 8.
+```
+#### 9. ブックをオープンします。
+ ※ 引数はオブジェクトブラウザーでコピーできます。
+
+![2019-02-18-15-55-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/5a683c14-211f-1bee-6e45-00952f7a3dda.png)
+![2019-02-18-15-57-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/5ff07dba-7f8f-6f8c-fa7c-766ced127398.png)
+
+```C++
+// ブックオープン
+Excel::Workbook^ wbook = Xls->Workbooks->Open(book, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing); // 9.
+```
+#### 10. Sheet1のハンドルを作成します。
+```C++
+// シートオープン
+Excel::Worksheet^ wsheet = (Excel::Worksheet^)wbook->Worksheets[1]; // 10.
+```
+#### 11. 引数で取得したRange(A2とA3)と値を設定します。
+```C++
+ // セル->Range(args[0]) 値->Value2(args[1])
+ wsheet->Range[args[0], Type::Missing]->Value2 = args[1]; // 11.
+ // セル->Range(args[2]) 値->Value2(args[3])
+ wsheet->Range[args[2], Type::Missing]->Value2 = args[3]; // 11.
+```
+#### 12. ブックを上書きで保存します。
+```C++
+ // ブックの上書き保存
+ bool flag = true;
+ wbook->Close(flag, \
+ System::Type::Missing, \
+ System::Type::Missing); // 12.
+```
+#### 13. Excel Applicationの終了
+```C++
+ // Excel Application 終了
+ Xls->Quit(); // 13.
+```
+#### 14. COMハンドルのリリース
+wsheet/wbook/Xlsをリリースします。
+※ オブジェクトブラウザでReleaseComObjectを検索する。
+
+```C++
+System::Runtime::InteropServices::Marshal::ReleaseComObject(wsheet); // 14.
+System::Runtime::InteropServices::Marshal::ReleaseComObject(wbook); // 14.
+System::Runtime::InteropServices::Marshal::ReleaseComObject(Xls); // 14.
+```
+
+<details><summary>上記の全ソースです。</summary><div>
+
+```C++
+#include "stdafx.h"
+#include "windows.h" // 1.
+
+using namespace System;
+using namespace Microsoft::Office::Interop; // 2.
+
+int main(array<System::String ^> ^args)
+{
+ Excel::Application^ Xls = gcnew Excel::Application(); // 3.
+
+ // カレントパス
+ TCHAR path[MAX_PATH];
+ GetCurrentDirectory(MAX_PATH, path); // 4.
+
+ // ブック名
+ TCHAR file[128] = TEXT("\\temp.xlsx\n"); // 5.
+
+ // カレントパス + ブック名
+ lstrcat(path, file); // 6.
+
+ // TCHAR → String変換s
+ String^ pathname = gcnew String(path); // 7.
+
+ // 空白トリミング
+ String^ book = pathname->Trim(); // 8.
+
+ // ブックオープン
+ Excel::Workbook^ wbook = Xls->Workbooks->Open(book, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing, \
+ Type::Missing); // 9.
+
+ // シートオープン
+ Excel::Worksheet^ wsheet = (Excel::Worksheet^)wbook->Worksheets[1]; // 10.
+
+ // セル->Range(args[0]) 値->Value2(args[1])
+ wsheet->Range[args[0], Type::Missing]->Value2 = args[1]; // 11.
+ // セル->Range(args[2]) 値->Value2(args[3])
+ wsheet->Range[args[2], Type::Missing]->Value2 = args[3]; // 11.
+
+ // ブックの上書き保存
+ bool flag = true;
+ wbook->Close(flag, \
+ System::Type::Missing, \
+ System::Type::Missing); // 12.
+
+ // Excel Application 終了
+ Xls->Quit(); // 13.
+
+ // COMハンドルリリース
+ System::Runtime::InteropServices::Marshal::ReleaseComObject(wsheet); // 14.
+ System::Runtime::InteropServices::Marshal::ReleaseComObject(wbook); // 14.
+ System::Runtime::InteropServices::Marshal::ReleaseComObject(Xls); // 14.
+
+ return 0;
+}
+```
+</div></details>
+## 3. ビルド
+ビルド(B) → ソリューションのビルド(B)
+![2019-02-19-09-52-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/a3e087dd-d9e0-55df-edef-370fd21ae50f.png)
+
+ビルドが成功すると、「tomCRL.exe」が作成されます。
+
+```Shell
+C:\~\TomCLR\Debug\TomCLR.exe
+```
+## 4. 実行
+C:\~\TomCLR\Debug\に前回準備したブック「temp.xlsx」を配置します。
+ ※ 今回はJavaScriptから直接呼び出しをおこなわないで、前回のnode.jsの結果を、「temp.xlsx」のセル(A2)とセル(A3)に設定します。
+
+```Shell
+C:\~\TomCLR\Debug>TomCLR.exe "A2" "徳川
+家康(とくがわ いえやす、旧字体: 德川家康)または□□ □□(□□□□□□□□□
+)は、戦国時代から安土桃山時代にかけての武将・戦国大名[1]。安祥松平家九代当主。
+江戸幕府の初代征夷大将軍[1]。三英傑の一人。「海道一の□□□」の異名を持つ。 " "A
+3" "松平 元康,まつだいらもとやす,弓取り"
+
+C:\~\TomCLR\Debug>
+
+```
+## 5.結果
+![2019-02-19-10-14-00.png](https://qiita-image-store.s3.amazonaws.com/0/239597/ae289154-5c4c-99e1-6b0f-051c63d9422d.png)
+
+# まとめ
+C++でExcelのCOMを利用したプログラムを作成しました。
+COMを利用するとブックのI/Oのオーバーヘッドが発生します。限られたリソースを使用するので、必ずCOMで使用したハンドルはリリースします。Windowsのお約束です。
+前回、ExcelJSでブックの読みとりをしたコードもC++に置き換えもできます。
+これでこの翻訳プロジェクトの機能部分の主役が揃いました。次回から本格化か?
+
+