[[20181021220517]] 『 パブリック変数が何かのタイミングで破棄される堰x(隠居じーさん) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『 パブリック変数が何かのタイミングで破棄される可能性がある事象の対策』(隠居じーさん)

 こんばんは ^^ いつもお世話になっております。
1. D:¥excel\フォルダ内にマクロブック、pvtest.xlmsを作成
2. 同フォルダに新規ブック(book1)を同名のxlamファイルを作成
3. pvtest.xlmsから参照設定でpvtest.xlamを参照設定
4. pvtest.xlamのプロジェクト名をpに
5. pvtest.xlamのthisworkbookのオブジェクト名をvに変更
6. 5.のvのコード表示で

 Option Explicit
 Public cnt As Long

 を記述
7. pvtest.xlamを上書き保存
pvtest.xlmsの標準モジュールに下記コードを記述

 Option Explicit
Sub test()
   v.cnt = 1000
   MsgBox v.cnt
End Sub
Sub test2()
    For v.cnt = 1 To 10
       MsgBox v.cnt
    Next
End Sub

 testは正常作動  test2  はコンパイルエラー! 
”変数が必要です”とエラーメッセージが出ます。

 ご教授戴きたい点は
以前この学校でこのような回避策をお取りになり、
完全とは言えないまでも実用に支障は出ていないような趣旨
をおききしたもので。この方法で良いのか。他に対策案があれば
教えていただきたいのと。
現状では、ループカウンターに使うとエラーになります。??
多分私が設定を間違えているのだと思いますが、
この辺の当たりもお詳しい方がおられれば、お教えいただけると幸甚
です。宜しくお願い致します。

< 使用 Excel:Excel2016、使用 OS:Windows10 >


パブリック変数が消える時ってENDステートメントを実行した時とかエラーで強制終了した時とかのことですよね?

この時、開いている全てのブックの変数が消失すると思うのですが、そうなると別のブックにデータを退避することで対策にはならないと思うのですが、私の思い違いでしょうか?

この手の対策はワークシートに書き出すとか、外部ファイル・レジストリに書き出すのが普通だと思うのですが。

一応、そのコードを試してみましたが此方でも再現できました。
変数として代入はできるのに、ループカウンタには出来ない。不思議な挙動です。

それならそれで、こうすればいいのでは?と思いますが。

 Sub test2()
    Dim i As Long
    For i = 1 To 10
        p.v.cnt = i
        MsgBox p.v.cnt
    Next
End Sub
そもそも、このような使い方をする状況が私には想像できません。
(名無し) 2018/10/22(月) 09:30

 名無し さん ご回答ありがとうございます。

 >>パブリック変数が消える時ってENDステートメントを
実行した時とかエラーで強制終了した時とかのことですよね?
=============================================
私もその辺が良く分かりません。
マイクロソフトさんのご説明だと。。。
メモリーの自動解放システムがエクセル様は独特で。。
たまに普通〜に使っていても突然消えて無くなる
事も有るかもしれないので使う時は気を付けましょう ^^ いやなら セルに書き出しましょうね。
って事かな、と想像しているのですが。
=============================================
>>そもそも、このような使い方をする状況が私には想像できません。
すみません。
何時消えるか分からない恐怖から解放されのびのびと、各処理を部品化したコードを
書きたくて、考えています。

 別BOOKを使用するのは参照しているだけなので(開いているよ〜なきがしないでもありません)
メモリーお掃除システム様の処理対象外なのかな〜とか想像するのですが。。。
それだとメモリリークなど心配は無いのか。。。疑問は深まるばかりで
諸先輩の皆様のご経験、お知恵等ご拝借できればと思います。

 上記コードをpvtest.xlamの標準モージュールに記述すればループカウンターでも使えます。
thisworkbookモジュールってクラスモジュールでしょうか?
でもクラス、作ればうまく動かないです。
頭の中かがグチャグチャになりかかっている老人(もちろん私の事)でした。
m(__)m

(隠居じーさん) 2018/10/22(月) 10:44


ブックをまたいで変数を共用するから問題が出るのだと思います。 v.cnt のように、他ブックに属する変数だと、それは参照元からすれば自分の管理外なので、変数をまだ利用している、という判定から外れるのでしょう。 ブック内だけで、cnt として使っているなら、勝手に消えたりしませんよ。

そして、確認のためとは思いますが、ループ変数に共通変数をつかうべきではないでしょう。 変数はなるべく小さいスコープで定義し使用するのが、余計な不具合を出さないためのセオリーですから。

更には、クラスと共通変数って、相反するものです。 共通変数のように引数宣言を無視して横槍を入れるようなものを排除し、Input,Outputを明確に宣言し、内部処理は独立していて外からは見えない、というのがクラスというものかと思います。
(???) 2018/10/22(月) 11:12


???さんコメントありがとうございます。
>>ブック内だけで、cnt として使っているなら、勝手に消えたりしませんよ。
はい。
いろいろ検索してみますと消えたと云うサイトも多数あるのですがマイクロソフトさんの
サポートページにリンクが貼ってあります。しかしそのリンクをクリックしてもマイクロソフト
でそのようなサイト無しのメxセージになっています。
http://support.microsoft.com/kb/408871/ja
昔は存在したと思います。私も見た事が有ります。
バージョンアップで
消えなくなったのでしょうか。^^;
(隠居じーさん) 2018/10/22(月) 14:28

あー、MSはよく過去情報を捨ててしまいますよ。 過去アプリでHELPボタンを押すと、先が無かったり。 そのページもよく参照されていたようですが、消えてますね。 似た情報だと、以下とか?
https://support.microsoft.com/ja-jp/help/141693/scope-of-variables-in-visual-basic-for-applications
(???) 2018/10/22(月) 14:45

???さんリンクありがとうございます。
【たまに普通〜に使っていても突然消えて無くなる
事も有るかもしれないので使う時は気を付けましょう ^^ いやなら セルに書き出しましょうね。】
のような意味合いの文言は見当たりませんね。
以前は確かにあったよ〜な気が。
^^;
普通にに使へば良いのでしょうかね。

(隠居じーさん) 2018/10/22(月) 15:03


マクロのブックまたぎをしなければ良いだけだと思いますよ。 マクロはどれか1つのブック上で動き、他はシート参照するために使うだけ。

「たまに消える」なんて、もし使っていて不正動作があってもMSは保証しませ〜ん!、と言っているようなものです。 そんな曖昧な機能なんて、使っちゃ駄目ですね。(この手の不具合をMSが認めても、修正したパッチを出した事が無いように思います)
(???) 2018/10/22(月) 15:33


???さん 名無しさん 貴重なお時間、ご意見を賜り、
ありがとうございました。
m(_ _)m

(隠居じーさん) 2018/10/22(月) 16:47


 MSの前の文言はこうでした。メモってくれてた人がいました。

 >ある Office ドキュメントが VBA のマクロを含む場合、
 >その中のモジュールに記述された Public 変数の値が有効である期間は、
 >あるプロシージャの実行を開始し、そのプロシージャが “End Sub” で終了するまでの間のみです。
 >
 >ほとんどの場合、プロシージャ終了後も値は保持されますが、
 >意図しないタイミングで保持されていた Public 変数の値が破棄され、
 >使えていた変数の値が突然使えなくなる場合があります。 
 >
 >そのため、Public 変数がアプリケーション終了時まで有効であることを期待する 
 >VBA マクロの実装は、推奨されません。
 >
 >なお、モジュールの編集、プロジェクトの構造の変更、コンパイルエラーの発生、
 >参照設定の変更、デザインモードへの切り替え、コントロールを削除して [元に戻す] 
 >を実行するなどのタイミングで変数が破棄される場合があります。 
 >
 >これは、そのタイミングでメモリ上に確保されているVBA の情報が破棄され、
 >初期化を行ってから、その後の処理が実行されるためです。
 >
 >補足 : この文書で説明する Public 変数とは、
 >すべてのモジュール レベル変数および静的変数を指します。 

 ichinoseさんが言っていたのは、普通のデータはどっかに書込・保存が利くが、
 オブジェクトはそうもいかないよねってことで、
 アドインファイルに格納している、との事だったかも知れません。

 「意図しないタイミング」で起きるといっているので、再現性のあるテストは難しい。

 けど、ググると、実行途中にEndステートメントを書くと、破棄が起きるらしい。
 なので、それでもichinose方式は無事かチェックしてみた。

 隠居じーさんのアイデアに従って、test3実行後にtest4を実行

 アドインファイル(pvtest.xlam) の ThisWorkbook(=v)
  Public cnt As Long
  Public ws1 As Worksheet

 テストファイル(pvtest.xlsm) の 標準モジュール

 Sub test3()
     Set ws1 = ThisWorkbook.Worksheets(1)
     Set v.ws2 = ThisWorkbook.Worksheets(1)

     Debug.Print "Test3 実行"
     Call test5(ws1, "test3-xlsm")
     Call test5(v.ws2, "test3-xlam")

     End '強制破棄を試みる

 End Sub

 Sub text4() 'test3終了後に実行
     Debug.Print "Test4 実行"
     Call test5(ws1, "test4-xlsm")
     Call test5(v.ws2, "test4-xlam")
 End Sub

 Sub test5(ws As Worksheet, oya As String)
     If ws Is Nothing Then
         Debug.Print oya & "のオブジェクトは破棄された"
     Else
         Debug.Print oya & "のオブジェクトは生きている"
     End If
 End Sub

 <結果>
 Test3 実行
 test3-xlsmのオブジェクトは生きている
 test3-xlamのオブジェクトは生きている

 Test4 実行
 test4-xlsmのオブジェクトは破棄された
 test4-xlamのオブジェクトは生きている

 アドインの方は生きていましたー。

 >ループカウンターに使うとエラーになります。??

 ループカウンターに使う変数には制限があるんじゃないですか?

 ヘルプを見ると

 >counter 必ず指定します。カウンタに使う数値変数を指定します。
 >配列変数およびブール型 (Boolean) に含まれる変数は指定できません 

 とか書いてありますので、書き漏れじゃないですかねぇ。
 (そんなことをする人がいると思っていないでしょうし・・

(半平太) 2018/10/22(月) 17:46


半平太さん ありがとうございます。
この学校のichinoseさんのレスを拝見した特に、オブジェクトモジュールをお使いだと記述しておられたように記憶していましたのでアドイン側のThisWorkbookに記述いたしました。
実験、有難う御座います。
ループカウンターにつきましては使えないとの理解で良いのでしょうか。
それとも何か私の記述ミスが有りましたでしょうか。
あと
アドイン側の標準モジュールに記述すればループカウンターも正常作動なのですが
標準モジュールを使うと意味が無いのでしょうか。
当方でももう一度実験してみます。
取り急ぎ御礼まで
m(__)m

(隠居じーさん) 2018/10/22(月) 18:19


 >アドイン側の標準モジュールに記述すればループカウンターも正常作動なのですが 
 >標準モジュールを使うと意味が無いのでしょうか。

 すみません 何の意味か分かりません。

 >ループカウンターにつきましては使えないとの理解で良いのでしょうか。

 そう思ったのですが、何か別の切り口があるんでしょうか?

 ループカウンタに使う変数は何でもいいって訳じゃない、と言うだけの認識なんですけども。

(半平太) 2018/10/22(月) 19:13


 エラー内容を書いてるのに、誰もそれに触れてないのは気のせいでしょうか

 コンパイルエラー”変数が必要です”
 なので、変数が破棄されたという問題ではなく、
 そもそも実行すら出来てないということではないのでしょうか

 そこで、
 Sub test()
    v.cnt = 1000
    MsgBox v.cnt
    Stop
 End Sub
 とし、ローカルウインドウを見てみると、
 - : Module1 :  : Module1/Module1
    :  : <変数なし> : 

 エラーどおり、変数がないようです

 では、cntはどこにあるのかというと、

 Sub test()
    Dim chk
    Set chk = v
    v.cnt = 1000
    MsgBox v.cnt
    Stop
 End Sub

 として、再度ローカルウインドウを見てみると、
 - : chk :  : Variant/Object/v
  (略)
     : CheckCompatibility : False : Boolean
     : cnt : 1000 : Long
     : CodeName : "v" : String
  (略)
 プロパティに混じってます

 なら、プロパティを使うと同じエラーが出るか試してみます

 Sub test3()
    For Range("A1").Value = 1 To 10
       MsgBox Range("A1").Value
    Next
 End Sub

 同じエラーが出ますね 型が違うだろとも言われそうなので、

    : RevisionNumber : 0 : Long

 確認用で、
 Sub test4()
    v.RevisionNumber = 1
    MsgBox v.RevisionNumber
 End Sub
 これは「コンパイルエラー:値の取得のみ可能なプロパティに値を設定することはできません。」

 Sub test3()
    For v.RevisionNumber = 1 To 10
       MsgBox v.RevisionNumber
    Next
 End Sub
 こちらは「コンパイルエラー:変数が必要です。」となります

 プロパティはプロパティであって、変数とはみなされないのでしょう
 もっとも、自分の考え方が根本的に間違えていたら、話になりませんが
(2u) 2018/10/22(月) 19:48

 > コンパイルエラー”変数が必要です”
 > なので、変数が破棄されたという問題ではなく、

 問題は2つなんです。1は大した問題じゃないですけど。

 1.なぜコンパイルエラーになるのか?

 2.上記1とは関係なく、意図しないタイミングで参照が破棄される対策として、
   アドインブックの変数に保存する方法がワークするかどうか?

 > プロパティはプロパティであって、変数とはみなされないのでしょう

 私には理解不能です。

 オブジェクトがメンバーとして持っている普通のスカラー変数は、
 そのオブジェクトのプロパティですよ。
 相容れないものじゃないです。

(半平太) 2018/10/22(月) 20:14


  > プロパティはプロパティであって、変数とはみなされないのでしょう

  これは正しい様な気がしてきました。

   Dim v.cnt as ?

   なんて書き方できないですから、「変数」と呼ぶのはおかしい。

  2uさん 済みませんでした。 m(__)m

(半平太) 2018/10/22(月) 20:57


http://support.microsoft.com/kb/408871/ja
はいつの間にか消されていたのですね。
↓を参照してください。
http://web.archive.org/web/20090909235324/http://support.microsoft.com/kb/408871/ja
(γ) 2018/10/22(月) 21:50

 半平太さん
>>すみません 何の意味か分かりません。
いえ、こちらこそ説明不足ですみません。

 アドインを参照する方式だとpvtest.xlamの変数の定義場所は
thisworkbookモジュールでも標準モジュールでも
意図しないタイミングで変数が破棄される対策に有効かどうかをお尋ね致しました。
2uさん詳しい解析ありがとうございます。
プロパティだとそうなるのですね、勉強になります。
γさんサイトのリンクありがとうございます。m(__)m

(隠居じーさん) 2018/10/22(月) 22:03


 半平太さんのコードを拝借して、pvtest.xlamのthisworkbookモジュールの
 変数定義をpvtest.xlamの標準 モジュールに書き換えて
 オブジェクト名を同じくvに変え実行した結果です。

 pvtest.xlam (p) の標準モジュール (v) に

 Option Explicit

 Public cnt As Long
 Public ws2 As Worksheet

 pvtest.xlsm の 標準モジュールに 

 Option Explicit
 Public ws1 As Worksheet
 Sub test3()
     Set ws1 = ThisWorkbook.Worksheets(1)
     Set v.ws2 = ThisWorkbook.Worksheets(1)
     Debug.Print "Test3 実行"
     Call test5(ws1, "test3-xlsm")
     Call test5(v.ws2, "test3-xlam")
     End '強制破棄を試みる
 End Sub
 Sub text4() 'test3終了後に実行
     Debug.Print "Test4 実行"
     Call test5(ws1, "test4-xlsm")
     Call test5(v.ws2, "test4-xlam")
     Call counter_test
 End Sub
 Sub test5(ws As Worksheet, oya As String)
     If ws Is Nothing Then
         Debug.Print oya & "のオブジェクトは破棄された"
     Else
         Debug.Print oya & "のオブジェクトは生きている"
     End If
 End Sub
 Sub counter_test()
    For v.cnt = 1 To 3
        Debug.Print v.cnt
    Next
 End Sub

 結果です。
Test3 実行
test3-xlsmのオブジェクトは生きている
test3-xlamのオブジェクトは生きている
Test4 実行
test4-xlsmのオブジェクトは破棄された
test4-xlamのオブジェクトは生きている
 1 
 2 
 3 

 となりましたです。

(隠居じーさん) 2018/10/22(月) 22:33


 あれー、なんで counter_test()の For v.cnt = 1 To 3 が旨くいっちゃうのー。

 標準モジュールの場合、クラスモジュールじゃないので、v.cnt ってプロパティじゃないんですかね?

 2uさんの見解待ちですね。

 いずれにしても「test4-xlamのオブジェクトは生きている」だから、対策の有効性が濃厚ですね。

 ところで、隠居じーさんは何故Counter変数に拘るんですか?

 そんなに重要じゃないような気がするんですけども。

 ループの途中でトラブって、再実行がシームレスで出来る方法、なんてのを考えているんでしょうか?

(半平太) 2018/10/22(月) 22:45


あ、@@いえ。。。単にcounter変数も使えると便利な場面も多々あるかと
例えばメインルーチンでループ回しあとはループの中から各サブルーチン呼び出しスタイルにすると
コード全体の流れが一目でわかりやすいとか。と思っています。
でもあまり推奨されたロジックではないような気がします。
おっしゃるとおり
>>そんなに重要じゃないような気がするんですけども。
だと思います。
出来ないと。。。悩む(すご〜く興味がわく ^^;)タイプなので恐縮です m(_ _)m

(隠居じーさん) 2018/10/22(月) 23:15


追伸
半平太さん ありがとうございました。使えそうなのでつかってみます。
名無しさん、???さん、2uさん有難うございました。

(隠居じーさん) 2018/10/22(月) 23:35


 あれ? もう終わっちゃうんですか?

 通常、ループ処理が途中で終わるなんてことは無いですよね?

 そうなると、ループのCounter変数が

  「グローバルである必要性」かつ
  「破棄が心配になる事情」

 がよく解からないんですけど・・

 企業秘密なら無理にはお尋ねしませんが。

(半平太) 2018/10/23(火) 00:03


 > 「グローバルである必要性」かつ

 すみません。こっちについては、これが答えだったんですね。
                 ↓
 >メインルーチンでループ回しあとはループの中から各サブルーチン呼び出しスタイルにすると 

 その時 、Counterは引数で受渡ししない、と理解しました。

(半平太) 2018/10/23(火) 09:13


おはようございます ^^

>>あれ? もう終わっちゃうんですか?
いえ、とても勉強になりますので、使用上の留意点とか、
モジュールの違いとかお教えいただくことの方が多いとは思いますが
ご意見、ご感想等。。。大歓迎です。お礼が言いたくて(隠居じーさん) 2018/10/22(月) 23:35
となりました。
(半平太)さん  2018/10/23(火) 09:13につきましては
ご指摘の通りです。

(隠居じーさん) 2018/10/23(火) 09:30


 pvtest.xlam (標準モジュール)変数定義
 Option Explicit

 Public cnt2 As Long
 Public ws2 As Worksheet

 pvtest.xlsm 変数の定義とコード

 Option Explicit
 Public ws1 As Worksheet
 Public cnt1
 Sub test6()
    For cnt2 = 1 To 10
        cnt1 = cnt1 + 1
    Next
    End
 End Sub
 Sub test7()
    Debug.Print "xlsm = " & cnt1
    Debug.Print "xlam = " & cnt2
 End Sub

 結果

 xlsm = 
 xlam = 11

 でした。xlamの方は消えていません。

 昨夜のループカウンタテストではENDをかけていませんでした
(使えるかどうかのみテストでしたので)
 モジュールの指定無しでもといううか、普通に変数として使えました。^^

(隠居じーさん) 2018/10/23(火) 10:29


 いや〜長い間エクセル使っていましたけど知りませんでした
別BOOKじゃなくても。
thisworkbookモージュールにパブリック変数を定義して
標準モジュールから参照すると変数が有りません。としかられます。wえ!
同じパターンの様ですu2さんがご指摘のプロパティなのでしょうか。
フォームモジュールではテストしていませんが、不思議ですね。
私が知らなかっただけでしょうか。A^^;

(隠居じーさん) 2018/10/23(火) 12:15


 うん ?  ↑ なにか混乱していますね ^^;
すみません。 変数、スコープ、モジュール
もう一度おさらいしてみます。
(隠居じーさん) 2018/10/23(火) 12:51

 知らなかったです。

 ループカウンターにその種の変数(プロパティ?)を使った経験自体ないです。

 通常、定義したモジュール内にあるプロシージャのループで使うだけですからねぇ。

(半平太) 2018/10/23(火) 12:55


 わたしも普段はループカウンタにオブジェクトモジュールのパブリック変数
を使ったことは有りませんでした。
そういえば思い出しましたが
何故かモジュール名から指定しなくては使えなかったですね。

 オブジェクトモジュールのパブリック変数とは実は変数ではなくてオブジェクトモジュールの
プロパティ?なのでしょうか。
「読込み、設定は出来るけど変数と同じようには(例、ループカウンタ)使えない。」なのでしょうか。
クラスモジュールでもなく標準モジュールでもないですね。
イベントプロシジャー以外は標準モジュールに書いた方が良い理由なのでしょうか
全て推測ですみません。

(隠居じーさん) 2018/10/23(火) 13:35


 >オブジェクトモジュールのパブリック変数とは実は変数ではなくてオブジェクトモジュールの
 >プロパティ?なのでしょうか

 なんか私には理解できない切り口なんですけど、変数とプロパティは相反する概念じゃないです。

 当該オブジェクトがプロパティとして変数を持っているので、変数です。

 ただ、ループカウンターとして使う変数には制限があるというだけです。

 上で書きましたけど、「配列変数」は使えないとヘルプに明示されていましたよね?
 他にも制限があるのだけども、MSが厳密に書かなかっただけだと思います。

(半平太) 2018/10/23(火) 14:01


 >>当該オブジェクトがプロパティとして変数を持っているので、変数です。
 >>ただ、ループカウンターとして使う変数には制限があるというだけです。
 >>他にも制限があるのだけども、MSが厳密に書かなかっただけだと思います。
 確かに。そぉかもしれませんね。
HELP見ました。
あと
アドインブック使用の際、excel終了後
タスクマネージャで確認してもエクセルが残っている事はなかったです。
使用後のアドインBOOKでの定義変数はエクセル様がメモリ解放して下さっている
との理解でよければ。どんどん使わせていただきたいと思っております。
今一度、レンジ、ディクショナリ等、オブジェクト変数でも試してみますです。
(隠居じーさん) 2018/10/23(火) 14:25

 結構増えてるなぁ

 今の問題は、標準モジュールオブジェクト名をv、その中に

 Public cnt As Long

 と書き、次のSubを実行した場合にv.cntがエラーにならない理由ですよね

 Sub counter_test()
    For v.cnt = 1 To 3
        Debug.Print v.cnt
    Next
 End Sub

 それは、「モジュール名.変数名」という形式で
 モジュールで宣言された変数を呼び出しているからでしょう

 ・・・前回といってることが違う?

 それは「変数の有効範囲」の話になります
 以下、自分自身の確認とメモ書きもかねてまとめてみます
 間違っていたらきっと指摘や訂正してくれるでしょう

 第108回.変数の適用範囲(スコープ,Private,Public)
 https://excel-ubara.com/excelvba1/EXCELVBA408.html

 この中の簡単なまとめには

 ・プロシージャー内で宣言した変数と引数は、そのプロシージャー内のみ使用可能
 ・モジュールの先頭でDimまたはPrivateで宣言した変数は、そのモジュール内でのみ使用可能
 ・モジュールの先頭でPublicで宣言した変数は、全てのモジュールで使用可能

 とありますが、

 標準モジュールとシートモジュールの違い
 https://excel-ubara.com/excelvba4/EXCEL251.html

 の「■モジュールレベル変数」には以下のように書かれています

 ・標準モジュール:全て使用可能
 ・ブックモジュール:Public変数は無効となりPrivate扱いとなる
 ・シートモジュール:Public変数は無効となりPrivate扱いとなる

 ということは、ブックモジュールであるThisWorkbookモジュールに

 Option Explicit
 Public cnt As Long

 と書いても実際のところは

 Option Explicit
 Private cnt As Long

 として扱われるため、2018/10/23(火) 12:15の隠居じーさんさんのような結果になるのでしょう

 ではcounter_test()の v.cnt がエラーにならない理由ですが、
 第108回.変数の適用範囲(スコープ,Private,Public) の「変数の重複について」にこう書かれています
 (必要と思う部分だけ抜き出してますので、必要な方は上のアドレスからお読みください)

 何も指定しなければ、自身の中で定義した変数が使われます。
 Module1.i
 このように、モジュール名で修飾することで、「モジュールレベル変数」を使う事が出来ます。

 複数のモジュールにある「モジュールレベル変数」が重複している場合は、
 上記のように、モジュール名で修飾して使います。

 これは、別の見方をすれば
 「モジュール名で修飾することで、そのモジュールのPublic変数を呼び出すことが出来る」
 と言うことなのでしょう

 ここまで、簡単にまとめてみました
(2u) 2018/10/23(火) 20:08

 > これは、別の見方をすれば
 > 「モジュール名で修飾することで、そのモジュールのPublic変数を呼び出すことが出来る」
 > と言うことなのでしょう

                 と言うことは、これは厳密な正確性を持った表現ではないと解釈されますね?
                         ↓
 > ・ブックモジュール:Public変数は無効となりPrivate扱いとなる

(半平太) 2018/10/23(火) 20:49


 こういう記事↓を読んでも「何となく分かった様な気はするけどさぁ...」レベルの者が失礼します ^^;

連載! とことん VB: 第 14 回 Visual Basic 固有の「モジュール」(Module) の役割 in VB.NET
https://code.msdn.microsoft.com/windowsdesktop/14-Visual-Basic-Module-2c407099

VBのモジュールはオブジェクト指向に不要? - @IT
http://www.atmarkit.co.jp/fdotnet/vbcheer/vbcheer07/vbcheer07.html

 きっと本格的な開発環境に慣れている人々にとって、
 単に「モジュール」って表現している所は「標準モジュール」を意味しているのでしょうね。
 標準モジュールだけは、クラスとオブジェクトの関係から切り離された存在に見えます。
 「みんなのひろば」みたいな「場所」でしかない存在というか。

 無いアタマで自分なりに解釈してみると、
 ・クラスモジュール            ← インスタンス作らないと使えない
 ・オブジェクトモジュール 
  ・シート、ブックモジュール  ← 既存のインスタンス扱い?
  ・ユーザーフォーム          ← クラスとしても使えるインスタンス? (逆か?)
 ・標準モジュール              ← インスタンス作れないし、インスタンスでもない。静的クラス(なんじゃそりゃ)とも違うらしい

 という感じに受け止めてます。

 「モジュール名.変数名」という書き方も
 標準モジュール上では「モジュールのメンバー」(「プロパティ」という表現もちょっと違うのかな? と。)である時点で
 「共有メンバー」であり、「(特定の)オブジェクトのメンバー」とは意味合いが違うのかも知れません。

 ・・・というタダの感想でした。失礼しました。

 ちなみに「まさかそれは無いよね?」と思って念の為やってみましたが、
 標準モジュール上にPublic Property Get/Letでプロパティを作っておいて、
 別モジュールからループカウンタとしてそのプロパティを使うことは、やはり出来ませんでした。
 (コンパイルエラー:変数が必要です)

(白茶) 2018/10/23(火) 23:11


 2uさん、白茶さん、リンク並びにご解説ありがとうございます
変数か。制限付き変数か。はたまたプロパティなのか。。。^^;
何故ループカウンターでは変数が有りませんエラーなのか。。。
季節は秋が深まる中エクセル様の謎も深まるよ〜な(何バカな事言ってる!)
議論は深まるなか、とりあえずオブジェクトが残らないか?テスト
先輩先生が既にお使いの様なので残るはずはないと思いましたが。せっかく作ってみましたので
諸先輩の前に出せるようなコードでは有りませんが、アップいたします。
このよ〜な組み方の是非はともかくパブリック変数テストみたいな感じです。
シート名Sheet1に適当に結合セルを作成後お試しください、突っ込み大歓迎 ^^v
 実行後タスクマネージャで見てもexcel様の残骸は見当たりませんでした。

 pvtest.xlam 標準モジュール v

 Public rr As Range
 Public r As Range
 Public D, buf()
 Public sh01 As Worksheet

 pvtest.xlsm 標準モジュール

 Option Explicit
Sub main()
'マージセル調査再構築
    On Error GoTo stperr
        Call start_main
        Call set_range
        For Each r In rr
            Call data_make
        Next
        Call write_data
        Call dest
Exit Sub
stperr:
    On Error GoTo 0
        Call dest
End Sub
Private Sub start_main()
    Set D = CreateObject("Scripting.Dictionary")
End Sub
Private Sub set_range()
    Set sh01 = Worksheets("Sheet1")
    Set rr = sh01.UsedRange
    ReDim buf(1 To rr.Count, 1 To 2)
End Sub
Private Sub data_make()
    Dim n As Long
    If r.MergeCells Then
        If Not D.exists(r.MergeArea.Address) Then
            n = D.Count + 1
            buf(n, 1) = r.MergeArea.Address(0, 0)
            D(r.MergeArea.Address) = n
        Else
            n = D(r.MergeArea.Address)
        End If
        buf(n, 2) = r.MergeArea.Interior.Color
    End If
End Sub
Private Sub write_data()
    Dim cnt As Long, y As Long: y = 1
    sh01.Copy
    With ActiveSheet
        .UsedRange.Clear
        .Cells(y, 1) = "Sub xxx()"
        If D.Count <> 0 Then
            For cnt = 1 To D.Count
                y = y + 1
                .Cells(y, 1) = "range(""" & buf(cnt, 1) & """).Merge"
                y = y + 1
                .Cells(y, 1) = "range(""" & buf(cnt, 1) & """).Interior.Color=" & buf(cnt, 2) & ""
            Next
        End If
        .Cells(y + 1, 1) = "End Sub"
    End With
End Sub
Private Sub dest()
    D.RemoveAll
    Erase buf
    Set rr = Nothing
    Set r = Nothing
    Set sh01 = Nothing
End Sub
(隠居じーさん) 2018/10/24(水) 07:50

 すこしきになりましたので ^^ 気弱な老人のいいわけです
隠居じーさん) 2018/10/24(水) 07:50の
>>季節は秋が深まる中エクセル様の謎も深まるよ〜な(何バカな事言ってる!)
は私自身のことを私自身が叱っています。決して、貴重なお時間をおさき戴き、
様々なご意見、ご感想をお寄せいただいている諸先生の事ではありません。
誤解を招くような表現で申し訳ありませんでした。
原因はどうであれ、
ループカウンターとしては使えない事実を知ることが出来た事はすごく私自身の勉強になりました。
このスレにお書込み頂いた皆様には、深く深く感謝致しております。
今後ともよろしくお願いいたします。

(隠居じーさん) 2018/10/24(水) 13:18


 https://excel-ubara.com/excelvba4/EXCEL251.html って「エクセルの神髄」さんでしたか。

 以前、その人の書いた内容について、この板で質問があり、関連個所を読みに行ったことがあるんですが、
 でたらめな事やる人だなぁと思ったことがあり、余りいい印象は持っていないです。

 そうなると、これは嘘と思う。それが本当なら、コンパイル時点でエラーになってしかるべきだと思います。
         ↓
 >■モジュールレベル変数
 >・標準ジュール:全て使用可能
 >・ブックモジュール:Public変数は無効となりPrivate扱いとなる
 >・シートモジュール:Public変数は無効となりPrivate扱いとなる

 「用語的には、若干の独自補正を加えています」と言っているので、「嘘」は言い過ぎかも知れないけど。

 Public、Privateの判定以前の問題、つまり、そもそも外部から相手にされていない。
 v.(ヴィ、ドット)と打って初めて、Public か Privateの判定に入る。

 隠居じーさんのコードをみましたけど、一気通貫に終わっちゃうプロシージャですよね。

 何かのタイミングで破棄される心配は無いと思うんですけどねぇ・・
 企業秘密なら無理に事情はお聞きしないですけども。

(半平太) 2018/10/24(水) 16:20


 こんばんは ^^ 感想。有難うございます。
>>企業秘密なら無理に事情はお聞きしないですけども。
ハンドルの通りで。。。隠居の身でして
決して秘密など御座いませんです。はい。
ただ、危険性が有るのなら少しでも回避したいな〜と。^^;
ループカウンターは今回パブリック変数としては使いませんでしたが。
コード書きながら、便利なのは便利なのですが
扱いには気を付けないと見つけにくいバグ発生の原因になりかねないなぁとの実感は有りました。
オブジェクトも同じですが複数回使い回すのは避けた方が良いのでしょうね。
サンプルコードは以前、半平太さんが作っておられたシート解析プログラムを拝見し、感嘆しておりました。
(難しくて理解できていません。「バラバラのアドレスを纏める」処理が凄いですね)
自分でも作成出来ないかと挑戦中です。とりあえずマージセルのみつくってみました。。。
お恥ずかしい限りです。私のスキルではまぁこんな程度です。^^

 private扱いでもモジュール名指定でアクセス出来る、出来ないと
ループカウンタで使える、使えないは別の様な気がするのですが
どうなんでしょうかね。
。。。m(__)m
でわ
(隠居じーさん) 2018/10/24(水) 17:36

 >ただ、危険性が有るのなら少しでも回避したいな〜と。^^;

 何か勘違いされているんじゃないかという気がしてきたんですけど・・

 プログラムが完全に終了したあと、次にオペレーターが処理を再開するようなプログラムにおいて
 前回使ったモジュールレベルの変数がまだ有効に残っている保証がないですよ、っていう事であって、
 一気通貫に走り終わるプログラムにそんな危険性はないです。

 >Private扱いでもモジュール名指定でアクセス出来る、出来ないと

  問題になっている状況がちょっと分からないです。

 「Private扱いでもモジュール名指定でアクセス出来る」と言う具体例を挙げて頂けませんか?

 >ループカウンタで使える、使えないは別の様な気がするのですが

 ループカウンタは、わざわざ「ループカウンタ変数」という用語を使っているくらいで、
 普通の変数と違う制約があることが伺い知れるので、ループカウンタに割り当てて
 エクセルに怒られたら素直に従うだけだと、私としては思っています。
 (理屈を考える気がしないです。ヘルプもそんなに充実した書き方をしていないので)

(半平太) 2018/10/24(水) 19:24


 > 以前、その人の書いた内容について、この板で質問があり、関連個所を読みに行ったことがあるんですが、
 > でたらめな事やる人だなぁと思ったことがあり、余りいい印象は持っていないです。
 > そうなると、これは嘘と思う。それが本当なら、コンパイル時点でエラーになってしかるべきだと思います。

 なんでかなー、と思っていたらそこのサイトは信用したくないってことでしょうか
 ちょっと気になったので検索してみたのですが、ここも信用できないって言われそうですね・・・

 https://excelwork.info/excel/varscope/

 でもまあ、シートモジュール・ブックモジュールの Public 宣言が有効だったら
 使い物にならないような気がしますね

(2u) 2018/10/24(水) 19:29


  https://excelwork.info/excel/varscope/

 こんなこと言っている箇所は無さそうですけど?
  ↓
  >■モジュールレベル変数
  >・標準ジュール:全て使用可能
  >・ブックモジュール:Public変数は無効となりPrivate扱いとなる
  >・シートモジュール:Public変数は無効となりPrivate扱いとなる

(半平太) 2018/10/24(水) 19:42


 半平太さん
 >「Private扱いでもモジュール名指定でアクセス出来る」と言う具体例を挙げて頂けませんか?

 たぶんこれは単に
 >>・ブックモジュール:Public変数は無効となりPrivate扱いとなる
 >>・シートモジュール:Public変数は無効となりPrivate扱いとなる

 を受けての表現だと思いますよ^^;

 (エクセルの神髄さんの言う)Private扱い(となってしまったPublic変数)でもモジュール名指定でアクセス出来る、
 (ホントにPrivate扱いならそもそもアクセス)出来ない(ハズだよね)と
 ループカウンタで使える、使えないは別(の問題)

 と読み替えてみました ^^;

 まぁ、真意は隠居じーさんさんに聞いてみないと分かりませんが、
 文脈的にはそんな感じに聞こえます。

(白茶) 2018/10/24(水) 20:23


 白茶さん 代弁、有難う御座います。その通りです。

 >>何か勘違いされているんじゃないかという気がしてきたんですけど・・
うう、^^ その通りだと思います。そぉだったのですね。
私が書いているコードくらいだと問題は無いのですね。
おっしゃっておられるようなコードを書く時の対処方法を教えていただき、
有難うございました。

(隠居じーさん) 2018/10/24(水) 20:49


 白茶さん 

 ありがとうございます。納得です。

 議論が繋がっていたんですね。

 半分くらいしか分かっていないので、私の頭の中では全然別の事になっちゃっていました。

 私としては、親元(?)を書かない限り、変数の存在すら無視された状態であり、
 Public性・Private性 を云々する以前の状態だと思っています。

 なので、こう言う表現は語弊があると思っています。
      ↓
  >・ブックモジュール:Public変数は無効となりPrivate扱いとなる
  >・シートモジュール:Public変数は無効となりPrivate扱いとなる

(半平太) 2018/10/24(水) 21:18


 >私が書いているコードくらいだと問題は無いのですね。

 サンプルに上がったものは問題ないですが、

 イベントが絡むコードで、モジュールレベルの変数を設定している様な時は、
 大抵(発生頻度は低いものの)この危険が発生していると思います

 初回、ボタンをクリックすると、(モジュールレベルのRange変数がNothingなら)
 アクティブセルに色を付ける、と言うコードを作ったとします。

 初回クリックで、アクティブセルに色を付けて、プログラムは一旦終了します。

 次に同じボタンをクリックすると、前回色を付けたセルの下に色を付けると言う仕掛けだと、
 前回色を付けたセルを覚えておく必要があります。
 多分、モジュールレベルの変数にセットすることになると思います。

 通常、モジュールレベルの変数はプログラムの実行が終わっても初期化されないので、
 下方へ色づけを続けることが出来ます。

 でも、予期しないタイミングで前回セルの情報が消えていたら、
 またアクティブセルに色を付けに行っちゃいます。

 (なら、色付けしたセルをアクティブにしておけよ、と言う話は無しで。あくまで例なので)

 でも滅多に起きないので、Q&Aではそんなこと気にする回答者は居ないと思います。
 (自分の仕事でもないですし、仮にそこまでやっても正統な評価を受けられるかどうかも怪しい)

(半平太) 2018/10/24(水) 23:20


 今更ながら「当たり前だろ」って事を確かめてみました。
 Module1、Module2、ThisWorkbook、Sheet1、Sheet2それぞれに

 Public v As Long
 を宣言し、
 Module3で

    Sub test()
        Debug.Print Module1.v, Module2.v, ThisWorkbook.v, Sheet1.v, Sheet2.v
    End Sub

 こんなのを書いてみても、とりあえずコンパイルエラーは出ませんね。

 但し「Module1.」を付けなければ「コンパイルエラー:名前が適切ではありません v」が出ます。
 「v」だけだと、どのPublic変数なのかを一意に特定できなくなってしまったからでしょうね。
 経験からこれは恐らく「Module1.vなのかModule2.vなのかどっち?」という事だと予想します。
 では
 Module2のPublic v As Longを削除したらどうか。

    Sub test()
        Debug.Print v, Module1.v, ThisWorkbook.v, Sheet1.v, Sheet2.v
        v = 1
        Debug.Print v, Module1.v, ThisWorkbook.v, Sheet1.v, Sheet2.v
    End Sub

 やはり「v」だけでも、コンパイルエラーは出ません。Module1.vに1が入りました。
 では、
 Module1のPublic v As Longも削除したらどうか。

    Sub test()
        Debug.Print v, ThisWorkbook.v, Sheet1.v, Sheet2.v
    End Sub

 「コンパイルエラー:変数が定義されていません」が出ます。

 いずれのモジュールでも変数の宣言部分は同じですが、
 WorkbookとSheetは「モジュール名.変数名」という書き方じゃないとアクセスできない
  →変数の持ち主から呼び出さないと見えてこない
 正に
 >親元(?)を書かない限り、変数の存在すら無視された状態
 ですね。
 対して
 標準モジュールは、標準モジュール全体で変数名が重複してなければ「モジュール名.」を省略できる。
 そしてループカウンタに「モジュール名.変数名」と書いても何だか通っちゃう。

 「オブジェクトに属するPublic変数をループカウンタとして使うことは出来ない」というのが本来の仕様なのであれば、
 標準モジュールは「オブジェクトではない」あるいは「ただのオブジェクトではない」と言えるのかも。
 やはり「標準モジュール」という「場所」の特性なのでしょうね。

 あれ?
 >Public変数は無効となりPrivate扱いとなる
 この表現をうまく釈明できる言葉が出て来ないものかとやってたハズだったのですが、結局出なかった... orz

(白茶) 2018/10/24(水) 23:32


 おはようございます ^^
白茶さん 詳細なテスト、ご説明、本当に有難うございます。
標準、シート、BOOK、各シートの特徴が良くわかります。
半平太さん、貴重なご教授、解りやすいご解説
ありがとうございます。
>>でも滅多に起きないので、Q&Aではそんなこと気にする回答者は居ないと思います。
>> (自分の仕事でもないですし、仮にそこまでやっても正統な評価を受けられるかどうかも怪しい)

 かもしれませんね ^^;
シートのイベントプロシージャはよく使います。
良く簡単なデータベースもどきは作るのですが、
 モジュールレベルの変数使用の場合
 入力待ち受け画面 → 分岐(新規登録、修正登録、削除、取消)戻る→、入力待ち受け画面
 の様な流れを繰り返す処理の場合も「危険あり」に該当しますでしょうか。

(隠居じーさん) 2018/10/25(木) 07:38


 > モジュールレベルの変数使用の場合
 > 入力待ち受け画面 → 分岐(新規登録、修正登録、削除、取消)戻る→、入力待ち受け画面
 > の様な流れを繰り返す処理の場合も「危険あり」に該当しますでしょうか。

 そう言われましても単純に判断つきません。

 待ち受け画面からスタートする時、
  前回処理した時に設定した変数の値が初期化されていないことを期待して
  プログラムを組んでいるなら危険ありです、MSの説明と同じです。m(__)m

  変数の値が何になっていようとも、スタートしてから新規に値を設定しながら
   進める作りなら何も危険は無いです。
  (同じ器を使っているだけであり、前のカスが残っていてもそれを捨て去って器だけ使うプログラム)

(半平太) 2018/10/25(木) 10:23


 >>そう言われましても単純に判断つきません。
ごもっともです。分かりやすいご説明、有難う御座います。
何となく解ってきたような気がいたします。イベント処理も含めますと。。。
コードの再点検してみます。^^;

(隠居じーさん) 2018/10/25(木) 11:04


 現在使用中のものはざ〜と点検しました。案外モジュールレベル変数は使っていませんでした。
ここでまた何かのおり、私がアップしたコードでそのようなものを見つけられ、めにあまるようでしたら
突っ込んでやってください ^^;。。。
本当にお世話になり有難うございました。とても勉強になりました。
お書込み頂いた皆様、有難うございました。
m(__)m
(隠居じーさん) 2018/10/25(木) 15:45

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.