1. pooshikin

    No comment

    pooshikin
Changes in body
Source | HTML | Preview
@@ -1,263 +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ではなくマルチバイトとします。)
+ ※ TCHARをcharサイズで取扱います。(UNICODEではなくマルチバイトとします。)
これをやると、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++に置き換えもできます。
これでこの翻訳プロジェクトの機能部分の主役が揃いました。次回から本格化か?