Excel (VBA)

Excel VBAに関するフォーラムです。
  • 掲示板への投稿には会員登録(無料)が必要です。会員登録がまだの方はこちら
  • 掲示板ご利用上のお願い」に反するご記入はご遠慮ください。
  • Q&A掲示板の使い方はこちらをご覧ください
トピックに返信
質問

 
(Windows 10 Pro : Excel 2016)
ClassモジュールをCallByNameでセットする方法
投稿日時: 21/04/21 15:51:59
投稿者: sakamoto_

お世話になります。
 
ちょっとマニアックなやり方になると思いますがご存じの方ご教示お願いします。
 
 
Classモジュールを変数で使用するとき、通常は
 

Dim cls As Class1

 
のようにすると思いますが、この部分を「CallByNameで代用できないか」という質問です。
このような感じのイメージです。
 
Sub TEST()

    Dim obj As Object
    Dim str As String
    
    str = "Class1"
    Set obj = CallByName(Application, str, VbSet)
    
End Sub

 
上記だとApplicationの部分がNG(「オブジェクトは名前付き引数をサポートしていません」)にように思うのですが、、よくわかっておりません。
 
(背景)
いくつかのClassモジュールがあるのですが、任意のクラスモジュールを都度指定して使いたいというのが理由です。
 
分岐などを使い今あるClassモジュールの数だけSet文を記述しておくのもありですが、変数化しておくと追加のたびにいちいちSetの記述をする必要がないのでこのCallByNameの手法を取りたく思っています。
 
ご存じの方お願いいたします。
 
 
 
 
 

回答
投稿日時: 21/04/21 17:45:02
投稿者: sk

引用:
Classモジュールを変数で使用するとき、通常は
 
Dim cls As Class1
  
のようにすると思いますが、この部分を「CallByNameで代用できないか」という質問です。

引用:
str = "Class1"
Set obj = CallByName(Application, str, VbSet)

引用:
オブジェクトは名前付き引数をサポートしていません

・Excel.Application オブジェクトに Class1 という名前の
 プロパティ/メソッドは存在しない。
 
・CallByName 関数によって呼び出されたプロパティが返すオブジェクトを
 任意の変数に参照渡ししたいのであれば、第 3 引数 calltype には
 定数 VbSet ではなく定数 VbGet を渡すべきである。
 
---------------------------------------------------------------
Sub Test()
    Dim obj As Object
    Set obj = CallByName(Application, "ActiveSheet", VbGet)
    Debug.Print obj.Name
    Set obj = Nothing
End Sub
---------------------------------------------------------------
 
例えば上記のようなコードであればエラーは起こりませんが、
これはまず Excel.Application オブジェクトに ActiveSheet というプロパティが存在し、
そしてこのプロパティがアクティブなシート( Excel.Worksheet オブジェクト
または Excel.Chart オブジェクト)への参照を返してくれるものだからです。
 
引用:
いくつかのClassモジュールがあるのですが、
任意のクラスモジュールを都度指定して使いたい

具体的に誰が/どういうシチュエーションで/どのような操作によって
それを指定するのでしょうか。
 
仮に「任意の条件(例えば String 型の引数に渡された値)に応じて
新規生成するオブジェクトの型を切り替えるようにしたい」
ということであれば、根本的には以下の方法しかないのでは。
 
引用:
分岐などを使い今あるClassモジュールの数だけ
Set文を記述しておく

回答
投稿日時: 21/04/21 17:47:04
投稿者: simple

補足情報です。
https://www.moug.net/tech/exvba/0100027.html
が参考になるでしょう。(通常の使用例です)
 
ご指摘のとおり、そうしたことはできないので、分岐処理をいくつか作っておくことだと思います。

回答
投稿日時: 21/04/22 16:26:44
投稿者: Suzu

教えて頂きたいのですが、
 
仮に ご希望の事が実現できたとして、
そのクラスのメンバーやメソッドの名称をどの様に指定なさるおつもりなのでしょうか。
 
それらが 同じなのであれば、実現するメリットが出ると思いますが
同じなのであれば、別クラスにする必要がありませんよね。
 
という事は、指定する為のメンバーや、プロパティー名をも動的に取得するのでしょうか。
 
そうなると、逆に回りくどいコードになりませんでしょうか。

回答
投稿日時: 21/04/23 10:51:58
投稿者: simple

本質的な話のあとで恐縮ですが、少し余談めいたコメントを追加します。
 
ExcelVBAで、文字列を対象に、
なんらかの評価を行いたいという場面は、次のようなものがあげられます。
(以下の(3)が今回のケースに相当しますが、それ以外も含めて列挙します。)
 
(1)ワークブックもしくはワークシートの環境のもとで、特定文字列を評価する場合
 
   これは、例えばアプリケーションレベルであれば、
      Evaluate("A1").Value = 25
      v = Evaluate("SIN(45)")
   とかいったものですね。
   これは結構使われます(syntactic sugarとして[]記法も使われます。)
    
   Worksheet(や、その特殊形としてのChat(グラフシート))も、Evaluateメソッドを持ちます。
   (評価を行う場合の"環境"が、Worksheetであったり、グラフシートだったりするわけです。)
     
(2)CallByName関数の使用。
   これは、指定されたオブジェクトの
        ・メソッドの実行や、
        ・プロパティの値の取得/設定を、
   プロパティやメソッドの名前を、引数Procnameに文字列で指定して行います。
   これを使うことで、メソッドやプロパティの指定を、文字列変数で行えます。
   (複数のプロパティの取得といった場面では、これを使うことで、繰り返し構文に
   持ち込むことが可能です(コードの短縮化が可能))。
   
(3)【これは可能ではなく、こうしたくなるという話ですが】
   文字列をコードの一部と見て、それをVBAで評価したい、というパターン。
    
   よく引き合いに出されるのが、"vbRed"といった文字列をワークシートに書いておいて、
   それをコードで評価して、vbRedという定数の代わりにできないか、といった話です。
    
   特定の言語では、そういった評価が可能なものがあります。(Lispが代表的でしょうか)
   しかし、VBA(つまりはVB6)という言語仕様には、そういう仕組みはありません。
   したがって、残念ながらそういうことはできません。
 
今回のご質問は、
   str ="Class1"
   s = "Set obj = new " & str
   eval(s) '文字列 s を マクロとして評価・実行する、
このようなことができないか、ということでしょうから、上の(3)に相当するものと思います。
残念ながら、そうしたことはできません、というのが回答です。

投稿日時: 21/04/23 17:01:16
投稿者: sakamoto_

皆様
 
ご回答ありがとうございます。
「できない」とのこと受け入れたく思います。
 
Simpleさんのおっしゃる
 

引用:
よく引き合いに出されるのが、"vbRed"といった文字列をワークシートに書いておいて、
それをコードで評価して、vbRedという定数の代わりにできないか、といった話です。

イメージとしてはいつもこのようなことを考えています。(たとえば、「vbRed」といった情報をシート上に持たせておき(定数だと可読性もあがり)、変更が必要なときはシートに記載した文字列を「vbWhite」にすれば、コード上のメンテナンスが不要になり楽になるかな、という発想です。
今回の件でもEvalの利用も検討しました)
 
Suzuさんの
引用:
仮に ご希望の事が実現できたとして、
そのクラスのメンバーやメソッドの名称をどの様に指定なさるおつもりなのでしょうか。

 
こちらについて長くなってしまい恐縮ですが、経緯含めて説明しますと、
よく使う機能を束ねたClassのライブラリがいくつかあります(Funcs.xlsm)。
これを別のマクロブックから呼び出して使うことを想定しています。
(もともとこのような想定ではなかったのですが、「ライブラリ化して外から呼び出せるようにしよう」という話になりました)
 
ライブラリの機能群をClassではなく、標準モジュールに移植すれば、「Application.Run」で呼び出せるのですが、
この場合、ライブラリ側のエラーが呼び出し元(別のマクロブック)のOn Errorで拾えないことが分かりました。
ここで、「ライブラリ側のコードに可能な限り修正を入れずに、エラーを取得するにはどうすればよいのか」という課題に突き当たりました。
 
そこで、CallByNameを使うことを思いつきました。
ただし、呼び出し元でCallByNameが使えない(と思っています→エラーになった)ので、
呼び出し元マクロからは、「必ずライブラリ内のMainのプロシージャを介してライブラリ内の機能を呼び出す」というルールにすることでエラーが検知できる仕組みが作れそうだ、、という流れに至ります。
※Mainだけはライブラリ(の標準モジュール)に作り込みます
 
以下はイメージです(手入力)。
 
呼び出し元マクロ(Runで機能を呼び出す)
戻り値 = Application.Run(Funcs.xlsm!Main,"ファイル一覧作成","C:\")

 
ライブラリ側
Function Main(Cls as Object,Arg1 as Variant,Arg2) as Variant
    
    Dim var as Variant
    On Error Goto ErrH

    var = CallByName(Cls, Arg1, VbGet,Arg2)

    Main = var  

    Exit Function

ErrH:  
Main = "エラーです"
End Function

 
ライブラリの構成を親子関係(Mainが親、Classの機能が子)にし、親側でOn Errorを入れる作戦です。
このようにすれば、仮にClass側で発生したエラーもMainで拾え、それを呼び出し元に返せるという仕組みを考えていました。
 
※この方法にこだわっているわけでもなく、別解でもよいのですが、何かあればと思います
 
よろしくお願い致します。

回答
投稿日時: 21/04/23 19:27:28
投稿者: simple

戻り値 = Application.Run(Funcs.xlsm!Main,"ファイル一覧作成","C:\")
の"ファイル一覧作成"は、
Mainの第1引数 Cls (as Object)と型が違うので、エラーになりませんか?
 
Runの引数として classオブジェクトを指定するなら、
今までとさして変わらないことになるのではないですか?
 
オブジェクトの代わりに、文字列で代替することは、
"vbRed"という文字列からvbRedという定数を作り出せないのと同じ理屈で、
難しいでしょう。

回答
投稿日時: 21/04/23 19:38:15
投稿者: kumatti
投稿者のウェブサイトに移動

表題の方はさっぱりなのですが、simpleさんの引き合いに出されてる方法は出来ます。
ただ、この時代のTypeLibって今手に入るのか?という懸念はあります。
http://web.archive.org/web/20040821153557/http://www2.moug.net/app/bbs/message.php?cat=acm_v&id=20040526-000002

回答
投稿日時: 21/04/23 20:03:22
投稿者: simple

いつも貴重な情報をありがとうございます。
そうなんですか、できるんですね、と思いました。
その反面で、
一般のユーザーに広く提供されているもの、
というレベルではなさそうな印象を持ってしまいます。
少なくとも言語仕様として備わっているとは言いにくいのでは、
というのが率直な感想です。

質問者さんのお役に立てればいいですね。ありがとうございます。

回答
投稿日時: 21/04/24 07:12:04
投稿者: MMYS

sakamoto_ さんの引用:

よく使う機能を束ねたClassのライブラリがいくつかあります(Funcs.xlsm)。
これを別のマクロブックから呼び出して使うことを想定しています。
  <中略>
この場合、ライブラリ側のエラーが呼び出し元(別のマクロブック)のOn Errorで拾えないことが分かりました。
ここで、「ライブラリ側のコードに可能な限り修正を入れずに、エラーを取得するにはどうすればよいのか」という課題に突き当たりました。

そのような用途なら、アドインにすれば良いでしょう。
別のマクロブックからアドイン内の自作クラスを直接、呼び出せますし、
クラス内のエラーも On Error で拾えます。
 
 
[VBA]作成したクラスをアドインとして利用する
https://qiita.com/Kamo123/items/020bd3f64ca15593a216
 
 

トピックに返信