COMで配列の受け渡しがどのよう行なわれているか調べたメモ
ATLでCOMサーバ作ってみて、配列の受け渡しが難しかったので調べた。
テスト用に渡されたパラメータの型に関する情報を文字列で返す簡単なCOMサーバを作って試してみた。
テスト用のCOMサーバのソースは後述する。
※なお、Method() と書くべきところを Mthod() となってしまってるのは単なるTypoで、あとで気づいたけど直すのめんどうだったのでそのままにしてる。
VBScriptの場合
テストコード
function format_table(title, result) format_table = join(Array("", title, result, ""), "|") end function set v = CreateObject("VariantTest.Tester") WScript.echo format_table("hoge", v.Mthod("hoge")) WScript.echo format_table("Array(""xxx"")", v.Mthod(Array("hoge", "fuga"))) WScript.echo format_table("Array(123)", v.Mthod(Array(123, 456))) dim arr2(1) arr2(0) = "hoge" arr2(1) = "fuga" WScript.echo format_table("Dim arr()", v.Mthod(arr2)) dim arr3(1) arr3(0) = 123 arr3(1) = 456 WScript.echo format_table("Dim arr() - 2", v.Mthod(arr3)) arr4 = Array("hoge", "moge") WScript.echo format_table("arr = Array()", v.Mthod(arr4)) WScript.echo format_table("(Array())", v.Mthod((Array("hoge", "moge")))) set v = nothing
結果
"hoge" | [BSTR] |
Array("xxx") | [ARRAY][VARIANT] |
Array(123) | [ARRAY][VARIANT] |
Dim arr() | [BYREF][VARIANT] |
Dim arr() - 2 | [BYREF][VARIANT] |
arr = Array() | [BYREF][VARIANT] |
(Array()) | [ARRAY][VARIANT] |
上記結果から、VBScriptからCOMサーバへSAFEARRAYを渡すためにはArray()で配列を生成するか、Array()への参照をもつ変数をカッコで括って (arr)
のようにして、渡す必要がある。
実験してみて、以外だったのは、 Dim で宣言した配列は、そのままではARRAYとして認識されないこと
また、Array()で生成した配列を変数に格納したとたんそれもARRAYとして認識されず、VARIANTの参照となってしまうこと
その場合の対処として、()で括って参照先の値を評価することでARRAYとして認識させることができること
JScriptの場合
テストコード
function format_table(title, result) { return ["", title, result, ""].join("|"); } v = new ActiveXObject('VariantTest.Tester'); WScript.echo("'hoge'", v.Mthod('hoge')); WScript.echo("['xx']", v.Mthod(["hoge", "fuga"])); WScript.echo("Array('xxx')",v.Mthod(Array("hoge", "fuga"))); sa = new Array(); sa[0] = "hoge"; sa[1] = "fuga"; WScript.echo("new Array()", v.Mthod(sa)); vbs = new ActiveXObject("ScriptControl"); vbs.language = "VBScript" WScript.echo("ScriptControl", v.Mthod(vbs.eval('Array("hoge", "moge")'))); dic = new ActiveXObject("Scripting.Dictionary"); dic.add(0, "hoge"); dic.add(1, "fuga"); WScript.echo("Dictionary", v.Mthod(dic.Items()));
結果
'hoge' | [BSTR] |
['xx'] | [DISPATCH] |
Array('xxx') | [DISPATCH] |
new Array() | [DISPATCH] |
ScriptControl | [ARRAY][VARIANT] |
Dictionary | [ARRAY][VARIANT] |
上記の結果から、JScriptからCOMサーバに対してSAFEARRAYを渡すには、Scripting.Dictionary オブジェクトのItems()メソッドからSAFEARRAYを得る方法がある。(コメントでおしえてもらいました)
VB6の場合
テストコード
Sub Main() Dim v As New VariantTestLib.Tester Debug.Print format_table("""hoge""", v.Mthod("hoge")) Debug.Print format_table("array()", v.Mthod(Array("abc", "def"))) Debug.Print format_table("array()", v.Mthod(Array(123, 456))) Dim arr2(1) As String arr2(0) = "hoge" arr2(1) = "fuga" Debug.Print format_table("arr2()", v.Mthod(arr2)) End Sub Function format_table(title, result) format_table = Join(Array("", title, result, ""), "|") End Function
結果
"hoge" | [BSTR] |
array() | [ARRAY][VARIANT] |
array() | [ARRAY][VARIANT] |
arr2() | [ARRAY][BSTR] |
COMがVB用作られただけあって、いちばんわかり易い結果になっている。
テスト用COMサーバのソースコード
参考までに上記のテストで使用したCOMサーバのソースコードを掲載しておく
// Tester.cpp : CTester の実装 #include "stdafx.h" #include "Tester.h" #include <iostream> // CTester STDMETHODIMP CTester::Mthod(VARIANT param, BSTR* ret) { CComBSTR result; long vt = param.vt; if (vt & VT_ARRAY) { result.Append("[ARRAY]"); vt ^= VT_ARRAY; } if (vt & VT_BYREF) { result.Append("[BYREF]"); vt ^= VT_BYREF; } if (vt == VT_BSTR) result.Append("[BSTR]"); else if (vt == VT_VARIANT) result.Append("[VARIANT]"); else if (vt == VT_UI1) result.Append("[UI1]"); else if (vt == VT_UI2) result.Append("[UI2]"); else if (vt == VT_INT) result.Append("[INT]"); else if (vt == VT_I4) result.Append("[LONG]"); else if (vt == VT_R4) result.Append("[FLOAT]"); else if (vt == VT_R8) result.Append("[DOUBLE]"); else if (vt == VT_ERROR) result.Append("[ERROR]"); else if (vt == VT_UNKNOWN) result.Append("[UNKNOWN]"); else if (vt == VT_DISPATCH) result.Append("[DISPATCH]"); result.CopyTo(ret); return S_OK; }