VB.NETのCOM相互運用機能の登録をONにして作成したDLLで定義されている構造体を、VBAから使用するにはどのように記述すればよいのでしょうか。
例えば、下記のコードでは、Dim t_o As T1の部分で「コンパイルエラー:Visual Basicでサポートさけていないオートメーションが変数で使用されています」というエラーになります。Dim t_o As vb_o.T1と記述しても「コンパイルエラー:ユーザ定義型は定義されていません。」というエラーになります。どのように記述すれば、VB側で定義した構造体T1をVBA側で使えるのでしょうか。
---DLL側(プロジェクト名ClassLibrary1)---
Public Class Class1
Public Structure T1
Dim x As String
Dim y As Integer
End Structure
Public Sub fx(ByRef t1_o As T1)
MsgBox(t1_o.x & t1_o.y)
End Sub
End Class
---VBA側---
Option Explicit
Public vb_o As New ClassLibrary1.Class1
'Public Type T1
' x As String
' y As Integer
'End Type
Public Sub test()
' Dim t_o As T1
Dim t_o As vb_o.T1
t_o.x = "ABC"
t_o.y = 123
call vb_o.fx(t_o)
End Sub
-----------
よろしくお願いします。
(WindowsXP,Excel2004_VBA,VisualStudio2010)
詳しくみてませんが、
vb_o.T1
ではなく
ClassLibrary1.Class1.T1
ではどうでしょう?
Dim t_o As ClassLibrary1.Class1.T1
としてみましたが、t_o As ClassLibrary1.Class1.T1の部分で「コンパイルエラー:型が一致しません」というエラーが出ます。
なお、型が一致しない時は
Dim t_o As (型) ClassLibrary1.Class1.T1
というようにキャストする方法がありますが、(型)の部分の記述が分かりません。
よろしくお願いします。
> Dim t_o As ClassLibrary1.Class1.T1
> としてみましたが、t_o As ClassLibrary1.Class1.T1の部分で「コンパイルエラー:型が一致しません」というエラーが出ます。
だとするとVBAでInnerクラスを使うことが出来ないかVariant宣言にしてClass1のメソッドによりT1のインスタンスを作成する必要があるかも。
「Variant宣言にしてClass1のメソッドによりT1のインスタンスを作成する」の部分がよく分からなかったのですが、以下のようにしましたが、New ClassLibrary1.Class1.T1の部分で「コンパイルエラー:型が一致しません」というエラーが出ます。
---VBA側---
Option Explicit
Public Sub test()
Dim VariantObject As Variant
Set VariantObject = New ClassLibrary1.Class1.T1
t_o.x = "ABC"
t_o.y = 123
call vb_o.fx(t_o)
End Sub
-----------
よろしくお願いします。
こんな感じでどうでしょう?
これだとVariantなのでメンバアクセスが結局大変なので
T1の宣言をClass外にした方がVBA側からの処理は書きやすいと思います。
Public Class Class1
〜
Public Function CreateT1() as T1
Dim ret as T1
Return ret
End Function
〜
End Class
> Set VariantObject = New ClassLibrary1.Class1.T1
これだとInnerクラス(構造体)へアクセスしているので同じになってしまいます。
Set VariantObject = vb_o.CreateT1()
のようにClass1のメソッドの戻りとして取得する必要があると思います。
なるほど。VB側に実装したメソッドを呼び出して構造体T1を取得するということですね。
下記のようなコードで確認してみましたが、取得した構造体T1がVariant型の変数に入らないような感じです。
Set t_o = vb_o.CreateT1()の部分で「実行時エラー'5':プロシージャの呼び出し、または引数が不正です。」というエラーになります。戻り値が未定義になっていましたので、下記のコメントのようにダミーの値を入れてみましたが同じエラーになります。
---VB側(プロジェクト名ClassLibrary1)---
Public Class Class1
Public Structure T1
Dim x As String
Dim y As Integer
End Structure
Public Sub fx(ByRef t1_o As T1)
MsgBox(t1_o.x & t1_o.y)
End Sub
Public Function CreateT1() As T1
Dim ret As T1
'ret.x="XXX"
'ret.y=0
Return ret
End Function
End Class
---VBA側(標準モジュール名Module1)---
Option Explicit
Public vb_o As New ClassLibrary1.Class1
Public Sub test()
Dim t_o As Variant
Set t_o = vb_o.CreateT1()
t_o.x = "ABC"
t_o.y = 123
Call vb_o.fx(t_o)
End Sub
-------------------------------------
よろしくお願いします。
レス内容に間違いがあったようです。
Inner宣言が駄目なのではなく構造体がCom公開されていないのが
原因のようです。ComClassによる公開が必要そうです。
Structure宣言をCom公開することが出来ないようでした。
Class1 クラスの下に、T1 構造体を配置しているようですが、
COM 側の名前空間は階層構造に出来ないと思います。
この場合タイプライブラリ上では、T1 ユーザー定義型の位置は
Class1 の下層ではなく、Class1 と同等の階層となるでしょう。
VBA 側から見える型名が As ClassLibrary1.T1 になるのか、
それとも、As ClassLibrary1.Class1_T1 になるのかは、
タイプライブラリ次第ですが、いずれにせよ、
ClassLibrary1.Class1.T1 のようにはなれないはずです。
> 「コンパイルエラー:Visual Basicでサポートさけていない
> オートメーションが変数で使用されています」
VBA で使われる String 型は、COM 的には「BSTR」と呼ばれる型です。
ユーザー定義型で String 型を用いたい場合には、String 型を
BSTR 型にマーシャリングする必要があります。
'----------------------------------------------
Imports System.Runtime.InteropServices
Public Class Class1
Public Structure T1
'MarshalAs を指定していない場合は、
<MarshalAs(UnmanagedType.BStr)> Public x As String
Public y As Integer
End Structure
Public Sub fx(ByRef t1_o As T1)
MsgBox(t1_o.x & t1_o.y)
End Sub
End Class
'----------------------------------------------
Public vb_o As New ClassLibrary1.Class1
Public Sub test()
Dim t_o As T1
t_o.x = "Hello," & ChrW(&H4F60) & "好"
t_o.y = 123
Call vb_o.fx(t_o)
End Sub
'----------------------------------------------
> call vb_o.fx(t_o)
現状のコードだと、VBA 側で「fx」がコード補完されない気がします。
レイトバインドにて呼び出せるので、今のコードでも問題は無いですが、
VBA 側の IntelliSense を使えるようにしたいのであれば、
インターフェイスを明示実装した上で、ClassInterfaceType.None を
指定しておくとよいでしょう。
Imports System.Runtime.InteropServices
<ClassInterface(ClassInterfaceType.None)> _
Public Class Class1
Implements IClass1
Public Structure T1
<MarshalAs(UnmanagedType.BStr)> _
Public x As String
Public y As Integer
End Structure
Public Sub fx(ByRef t1_o As T1) Implements IClass1.fx
MsgBox(t1_o.x & t1_o.y)
End Sub
Public Interface IClass1
Sub fx(ByRef t1_o As T1)
End Interface
End Class
余力があれば、GuidAttribute も指定しておくと安全です。
shuさん。
いろいろ調べていただきましてありがとうごさいました。
魔界の仮面弁士さん。
まさに教えて欲しかったことそのものです。
サンプルコードがありましたのでとてもよかったです。
問題が解決しただけでなく
・COM側の名前空間は階層構造に出来ない。
・VBAのString 型は、COMではBSTR型になる。
・マーシャリングの記述の仕方
・インターフェイスの記述の仕方
などのノウハウも理解できて、
初心者の私にとってはたいへん参考になりました。
ありがとうございました。
またよろしくお願いします。
ツイート | ![]() |