VBSと連携するElectronアプリを作る

electronは大変便利なプラットフォームですが、Windows環境で使う場合に於いて、特に企業ユースの場合「あれは駄目、これは駄目」そういったケースが多いと思います。その1つがメール送信。(SMTP駄目やらWeb APIの使用駄目などなど)。

そうすると、メール1つ送るにしても、electronからはこのままでは送る事ができません。しかし、ほとんどの企業で穴になっている「VBA」や「VBS」については使える事が多いので、これを利用してVBScriptにそれら代理をしてもらう手段を考えました。VBScriptに引数を渡し、指定の条件でOutlookからのメール送信をやってもらうのが今回の目的。

VBScriptが使えると、Windows環境ではoffice製品のコントロールをelectronからも行えるようになるので、活用の幅が広がるのではないかと。

今回使用するプロジェクトファイル

※今回はjQuery以外に特に外部モジュールをnpm installしていません。

※中に入ってるmailer.vbsは、今回はデスクトップに配置してください。

図:こんな感じのウィンドウから作業開始になります。

ソースコード

VBSのみを実行するケース

electron側

レンダラプロセス側
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <script>
      var $ = jQuery = require("jquery")
    </script>
    <script type="text/javascript" src="js/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="css/jquery-ui.css">
    <link rel="stylesheet" href="css/setting.css">
    <script>
      // IPC通信を行う
      var ipcRenderer = require( 'electron' ).ipcRenderer;
      
      //セッティング項目を保存する
      function savesetting(){
        //メールアドレスを取得
        var mail = document.getElementById("mail").value;

        //メインプロセスに処理を送る
        ipcRenderer.send('mailer', mail);

      }

    </script>

    <title>VBSでメール送信</title>
  </head>
  <body>
    <!-- セッティング項目を表示 -->
    <form class="contact_form" action="#" method="post" name="contact_form">
        <ul>
            <li>
                 <h2>VBSでメール送信</h2>
                 <span class="required_notification">*印は、必須入力項目です</span>
            </li>
            <li>
                <label for="name">メールアドレス</label>
                <input type="text"  placeholder="kinoko@test.com" style="width:150px" id="mail" required />
                <span class="form_hint">メールアドレスを入力</span>
            </li>
        </ul>
    </form>

    <p>
      <center>
        <button onClick='savesetting()' id="saveman" class="action" title='設定を保存する'>送信</button>
      </center>
    </p>

  </body>
</html>
  • IPC通信にて、メインプロセスにテキストボックスのメアドを送り込んでいます。
メインプロセス側
'use strict';

//標準モジュールの宣言
const electron = require('electron');
const { app, dialog } = require('electron');
var fs = require('fs');
const BrowserWindow = electron.BrowserWindow;

//vbs実行用(同期的にexe実行を行う)
var spawnSync = require('child_process').spawnSync;

// メインウィンドウはグローバル宣言
let mainWindow = null;

//Node.js側とHTML側で通信をするモジュール
const ipcMain = require('electron').ipcMain;

//初期化
app.on('ready', function() {
  // メイン画面の表示。ウィンドウの幅、高さを指定できる
  mainWindow = new BrowserWindow({
  	'width': 550,
  	'height': 400,
  	'autoHideMenuBar':true,
  	//nodeIntegrationを有効にしないとrenderProcessでrequireを使えない。v5.0.0ではデフォルトで廃止
  	webPreferences: {
        nodeIntegration: true
    },
    'resizable':false,
    'fullscreenable':false,
    'fullscreen':false
  });

  //初期ページの表示
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  //mainWindow.webContents.openDevTools();


  //閉じたときの挙動
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});

// 全てのウィンドウが閉じたときの処理
app.on('window-all-closed', () => {
  // macOSの時以外はアプリケーションを終了させます(osxだとドックに残る)
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

//メール送信コントロール
ipcMain.on('mailer', function( event, args ){

  //デスクトップのパスを指定
  var dir_home = process.env[process.platform == "win32" ? "USERPROFILE" : "HOME"];
  var dir_desktop = require("path").join(dir_home, "Desktop");

  //実行するVBSファイルの名前
  var filename = "\mailer.vbs";

  //実行パスを構築する
  var fullpath = dir_desktop + filename;

  //コマンドラインを構築
  var ret = vbsCommand(fullpath,args);

  //返り値により処理を分岐
  if(ret == 0){
    //メッセージを表示
    //メッセージオプション
    var options ={
      type:'info',
      title:"通知",
      button:['OK'],
      message:'メール送信完了',
      detail:'メールは無事に' + args + "へ送信されました。"
    }

    //表示する
    dialog.showMessageBox(null,options);
  }else{
  	//エラーが発生しているので、なにか処理をする
  	console.log("error:" + ret + "みたいですよ。");
  }

});

//外部コマンドを実行する
function vbsCommand(fullpath,address) {
  //コマンドを組み立てて実行
  var child = spawnSync('cscript.exe', [ fullpath, address ] );

  //返り値を取得する(status)
  var ret = child.status;

  //retを返す
  return ret;

}
  • 同期的にコマンドラインを実行する必要があるので(返り値を取得するため)、child_processでは、spawnSyncを利用しています。
  • デスクトップに配置したmailer.vbsが今回メールを送信するVBSスクリプトファイルです。
  • vbsCommandにはvbsファイルへのパスと、ダイアログから受け取ったメアドを引数で渡しています。
  • spawnSyncにてコマンドライン実行。cscript.exeがvbscriptの実行本体。コマンドライン引数にfullpathとaddressをつなげています。
  • vbsファイルからの返り値は、child.statusとして取得しています。

VBS側(mailer.vbs)

'変数を宣言し、引数(メアド)を取得する
Dim args : args = WScript.Arguments(0)
dim status : status = 0

'エラー発生時に処理を継続
On Error Resume Next

'引数確認
msgbox args & "さんへメール送信"

'Outlookオブジェクトを用意
Set appOutlook = CreateObject("Outlook.Application")
Set objMailItem = appOutlook.CreateItem(olMailItem)

with objMailItem
    .Recipients.Add args
    .Subject = "これはテストメールですよ"
    .Body = "メールの本文です。"
    '.Attachments.Add "c:test.csv"
    .Send
end with

On Error Goto 0

'終了処理
set appOutlook = nothing
set objMailItem = nothing

'ステータスを返す
WScript.Quit status
  • 無事に送信されると、electron側に0のstatusが返る
  • Outlookが起動していなくとも、APIで実行されているのでメールはデフォルトアカウントで送信されます。
  • 冒頭のargsにて、引数として指定されてるメアドを取得しています。
  • 今回は、Microsoft365のOutlook Version 16.0でテストしています。
  • メールが送れるので、APIなしでOutlookを使って、Microsoft Teamsのチームチャンネルに投稿が可能です。
  • Teams投稿時にはVBSファイルは文字コードがANSIでないとSubjectが文字化けします。メモ帳で保存時に文字コードをUTF-8からANSIに変更して保存しましょう。

VBAを実行させるケース

VBSを介してVBAを実行させる事が可能です。また、その場合に引数を渡し、返り値を受け取る事が可能ですが、VBAからの結果出力はShift-JISでありそのままだと文字化けしてしまいます。そこでこれをiconv-liteを使って変換する事で、Electron側で受け取る事が可能です。事前にnpmで追加しておきましょう。

npm install --save iconv-lite

electron側

var iconv = require('iconv-lite');

ipcMain.on('vbaexec', function (event) {
  //フルパスを取得して、ディレクトリを取得する
  let fullpath = store.get("fullpath");

  //VBSファイルを指定
  let vbsfile = __dirname + '/vbs/vbatest.vbs';

  //引数を構築
  var cmdargs = fullpath + "," + "サグラダ・ファミリア";

  //VBS実行
  var result = vbsCommand3(vbsfile,cmdargs);

  console.log(result)
});

//VBSコマンドを実行する関数
function vbsCommand3(fullpath, args) {
  //コマンドを組み立てて実行
  console.log(args)
  var child = spawnSync('cscript.exe', ['//nologo',fullpath, args]);

  //返り値を取得する(status)
  var ret = child.stdout;

  //stdoutの文字列がshift-jisなので変換して表示
  ret = iconv.decode(Buffer.from(ret), 'Shift_JIS');

  //retを返す
  return ret;
}
  • nologoオプションを指定しています。stdoutから結果にロゴが入ってしまうのを除外する為です。
  • 実行するVBAの入ったファイルは、argsにて指定していますが、このargsにはxlsmファイルへのフルパスと引数をカンマ区切りにした値が格納されています。
  • 返り値はテキストで帰ってくるのでchild.stdoutにて出力を受け取ります。
  • Buffer.fromでchild.stdoutを引き受けます
  • 受け取った出力はShift-JIS形式なので、iconv.decodeにてNode.jsで扱えるUTF-8に変換してretに格納しています。

VBS側

'変数を宣言し、引数(メアド)を取得する
dim status : status = 1
Dim args : args = WScript.Arguments(0)
Dim aryStrings
aryStrings = Split(args, ",")

'変数を宣言
Dim exo, wbo, strMsg, fullpath, ret

fullpath =  aryStrings(0)

'送信する引数の値をセット
strMsg = aryStrings(1)

'Excelを起動する
Set exo = CreateObject("Excel.Application")
exo.Application.Visible = false

'Excelファイルを開く(拡張子はxlsmである必要がある)
Set wbo = exo.Application.Workbooks.Open(fullpath)

'Module1のHelloWorldを実行する
ret = wbo.Application.Run("Module1.testman", strMsg)

'終了処理
wbo.Close
exo.Quit
Set exo = Nothing
Set wbo = Nothing

'返り値を表示する
WScript.Echo ret
  • 単純にVBA側に文字列を渡して返り値を受け取って表示するだけ。
  • VBA側のModule1の中にあるtestmanという関数を実行しています。引数としてstrMsgを渡しています。相手方はVariant型で受け取る必要があります。

VBA側

'メッセージ表示
Public Function testman(tword As Variant) As Variant
    testman = tword & "あぶさん"
End Function
  • 単純に引数に文字を足して返すだけの関数です
  • 引数も返り値もVariant型で指定します。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)