ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

JScriptの配列とVBScriptの配列(SafeArray)を相互変換する方法(2次元編)

次は本題の2次元のSafeArrayを作り出す方法です。

JScriptVBScript両方使う方法

<?xml version="1.0" encoding="Shift_JIS" ?>
<package>
  <job>
    <script language="VBScript"><![CDATA[
      Function convertToSafeArray2d(jsArray)
        Dim l1, l2, result
        l1 = jsArray.length
        l2 = Eval("jsArray.[0].length")
        ReDim result(l1, l2)

        For i=0 to l1 - 1
          For j=0 to l2 - 1
            result(i, j) = Eval("jsArray.[" & i &"].[" & j &"]")
          Next
        Next

        convertToSafeArray2d = result
      End Function
    ]]></script>
    <script language="JScript"><![CDATA[
      // 2次元のJScript配列
      var arr2d = [[1,2,8],[4,7,6]];

      // SafeArrayに変換
      var safeArray2d = convertToSafeArray2d(arr2d);
      printVBArray(new VBArray(safeArray2d));

      // デバッグ用:VBArrayを表示
      function printVBArray(vbArray) {
        for(var i=0; i<vbArray.ubound(1); i++) {
          for(var j=0; j<vbArray.ubound(2); j++) {
            WScript.Echo("vbArray[" + i + "," + j + "] = " + vbArray.getItem(i, j));
          }
        }
      }
    ]]></script>
  </job>
</package>

VBScriptの関数(convertToSafeArray)にJScriptの2次元配列(正確には配列の配列)を渡すと、1次元の場合と同様"JScriptTypeInfo"という型になるんですが、残念ながら、中身は"JScriptTypeInfo"の配列ではなく、単なるカンマ区切りの文字列になります。
これを別のアプローチで読み込む方法はないかと探したところ、以下の記事が見つかりました。
a jscript accessor within vbscript - ASP Message Board
要はEvalを駆使すれば出来るということのようだったので、これを利用したところうまく動作しました。
一方、VBScriptの2次元配列を戻り値として受け取ったJScript側では、1次元の場合と同様VBArrayを利用して読み込みを行います。

JScriptのみ使う方法

<?xml version="1.0" encoding="Shift_JIS" ?>
<package>
  <job>
    <script language="JScript"><![CDATA[
      function convertToSafeArray2d(jsArray2d) {
        try {
          // Excelを起動する
          var excelObj = new ActiveXObject("Excel.Application");

          var workbook = excelObj.workbooks.Add();
          var sheets = workbook.Sheets;
          var enumSheet = new Enumerator(sheets);

          // 1シート目を取得
          var sheet = enumSheet.item();

          // 各セルに値を設定
          var i,j;
          for(i=0; i<jsArray2d.length; i++) {
            for(j=0; j<jsArray2d[i].length; j++) {
              var value = jsArray2d[i][j];
              sheet.Cells(i+1, j+1).Value = value;
            }
          }

          // SafeArrayを返す
          return sheet.Cells(1, 1).CurrentRegion.Value;
        } finally {
          // Excelを終了する
          workbook.Close(false);
          excelObj.Quit();
        }
      }

      // 2次元のJScript配列
      var arr2d = [[1,2,8],[4,7,6]];

      // SafeArrayに変換
      var safeArray2d = convertToSafeArray2d(arr2d);
      printVBArray(new VBArray(safeArray2d));

      // デバッグ用:VBArrayを表示
      function printVBArray(vbArray) {
        for(var i=1; i<=vbArray.ubound(1); i++) {
          for(var j=1; j<=vbArray.ubound(2); j++) {
            WScript.Echo("vbArray[" + i + "," + j + "] = " + vbArray.getItem(i, j));
          }
        }
      }
    ]]></script>
  </job>
</package>

ExcelのRange.Valueが2次元のSafeArrayを返すことを利用した方法です。VBScriptが不要になる反面、Excelが必要になります。
配列を受け取って、セルに一つ一つ値をセットするというオーソドックスな方法をとっていますが、場合によっては、配列をテキストファイルに書き出して、Workbooks#OpenTextで読み込んだ方が速いかも知れません。


このテクニックを利用して出来たのが、先のエントリCSVファイルを一瞬でExcelファイルに変換する方法です。