[[20100731000846]] 『モジュール内での変数について』(ゆき夫) ページの最後に飛ぶ

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

 

『モジュール内での変数について』(ゆき夫)

 いつも参考にさせていただいています。
 変数について2つ質問です。OSはXPでExcel2007を使用しています。

 一つの標準モジュール内にSub1()とSub2()の2つのプロシージャを記載し、毎回必ずSub1()→Sub2()の順で処理をします。

 @その際、自分のPC(会社のですが)ではSub1()で取得し変数に入れた値がSub2()に引き渡されたのですが、他の人のPCだと
 「オブジェクト変数またはwithブロック変数が設定されてません」となってしまいます。確かに変数にポインタを当ててみると取得できていません。
 どこが原因なのでしょうか。

 ASub1()とSub2()の間に作業を行うのですが、@ではその作業を手入力していました。それをダブルクリックイベントプロシージャで行なうようにすると、
 自分のPCでもSub2()ではSub1()の変数が引き渡されませんでした。イベントプロシージャを使用すると引き継がれなくなってしまうものなのでしょうか?

 この2つをどうぞよろしくお願いします。

 Sub1()の処理内容
 加工する顧客データファイル(Excel)を選んで開き(ここでは選んだファイル名とそのファイルが入っているフォルダ名を取得)、シートを移動してき
 、関数を張り付け(ここでは最終行の取得)、フィルタをかけて手動で処理する対象データを抽出。
 Sub2()の処理内容
 手入力(手動で処理)したデータを反映させて(ここで最終行の値が必要になります)他の条件でフィルタをかけ直し、必要な列を抜き出して
 新しいブックを開いて貼り付け、取得しておいたフォルダ名の記載をし、更にデータをコピーしてきて、移動してきたシートを元の顧客データファイルに戻す。

 下記はコードです。長くて申し訳ございませんがどうぞよろしくお願いいたします。

 ---ThisWorkbook---Aで使用するためのものです

 Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)

    If Target.Column = 1 And Target.Value = "" Then
        Target.Value = Range("A1")
        Target.Offset(0, 1).Select

    ElseIf Target.Column = 1 And Target.Value = Range("A1") Then
        Target.Value = ""
        Target.Offset(0, 1).Select

    End If

 End Sub

 ---Module1---

 Option Explicit

    Private Wbook1 As Workbook      '---このファイル
    Private Wbook2 As Workbook      '---加工ファイル
    Private MyWb1RwMax As Long      '---候補リストの最終行
    Private FolderName As String    '---選択フォルダパス名

 Sub 目検対象抽出()

    Dim ans As Integer
    Dim OpenFileName As String
    Dim i As Boolean
    Dim MyRange As Range
    Dim n As Integer
    Dim FolderPath As String

    Set Wbook1 = ThisWorkbook

 'ファイルの選択告知
    ans = MsgBox("次の 『ファイルを開く』 から加工する候補リストを選択してください。", _
                vbOKCancel + vbInformation, "ファイルの選択")
        If ans = vbCancel Then
            MsgBox "キャンセルされました。もう一度  1.作業開始  から始めてください。", _
                vbOKOnly + vbInformation, "通知"
        Exit Sub
    End If

 '対象ブックを選択して開く
    OpenFileName = Application.GetOpenFilename("Microsof Excelマクロ有効ブック もしくは Excelバイナリブック,*.xlsm;*.xlsb")

    i = 0
    If OpenFileName <> "False" Then
        Set Wbook2 = Workbooks.Open(OpenFileName)
        FolderPath = ActiveWorkbook.Path
        n = InStrRev(FolderPath, "\")
        FolderName = Right(FolderPath, Len(FolderPath) - n)
        i = 1
    End If
    If i = 0 Then
        MsgBox "キャンセルされました。もう一度  1.作業開始  から始めてください。", _
                vbOKOnly + vbInformation, "通知"
    Exit Sub
    End If

 'シート"名寄せ候補template"を移動してくる
    Wbook2.Activate
    Worksheets("名寄せ候補template").Move After:=Wbook1.Worksheets(1)

    MsgBox "選択された加工用の候補リストファイルは、このファイルとは別に開いています。作業終了通知が出るまで閉じないでください。", _
            vbOKOnly + vbInformation, "作業通知"

 'B列の最終行取得
    Worksheets("名寄せ候補template").Select
    MyWb1RwMax = Range("C" & Rows.Count).End(xlUp).Row

 'コピー元のデータを名寄せ候補Templateへ貼り付け
    Worksheets("コピー元(TEST6用)").Range("DT1:EP4").Copy
    Worksheets("名寄せ候補template").Range("DT1").PasteSpecial Paste:=xlPasteAll
    Worksheets("名寄せ候補template").Range("EP1").Value = MyWb1RwMax - 3

    Worksheets("コピー元(TEST6用)").Application.CutCopyMode = False

    '各4行目の式を最終行まで貼り付ける

    Worksheets("名寄せ候補template").Select
    'DU4〜DU列をセット
    Set MyRange = Range("DU4:DU" & MyWb1RwMax)
    'DU4のデータをセットした範囲までフィルコピー
    Range("DU4").AutoFill Destination:=MyRange

    'DV4〜DV列をセット
    Set MyRange = Range("DV4:DV" & MyWb1RwMax)
    Range("DV4").AutoFill Destination:=MyRange

    'DW4〜DW列をセット
    Set MyRange = Range("DW4:DW" & MyWb1RwMax)
    Range("DW4").AutoFill Destination:=MyRange

    'DX4〜DX列をセット
    Set MyRange = Range("DX4:DX" & MyWb1RwMax)
    Range("DX4").AutoFill Destination:=MyRange

    'DY4〜DY列をセット
    Set MyRange = Range("DY4:DY" & MyWb1RwMax)
    Range("DY4").AutoFill Destination:=MyRange
    'DY列を値として貼り付け
    Range("DY4:DY" & MyWb1RwMax).Copy
    Range("DY4:DY" & MyWb1RwMax).PasteSpecial Paste:=xlPasteValues
    'DY列のGCIを排除
    Selection.Replace What:="00GCI", Replacement:=""

    'DZ4〜DZ列をセット
    Set MyRange = Range("DZ4:DZ" & MyWb1RwMax)
    Range("DZ4").AutoFill Destination:=MyRange

    'EA4〜EA列をセット
    Set MyRange = Range("EA4:EA" & MyWb1RwMax)
    Range("EA4").AutoFill Destination:=MyRange
    'EA列を値として貼り付け
    Range("EA4:EA" & MyWb1RwMax).Copy
    Range("EA4:EA" & MyWb1RwMax).PasteSpecial Paste:=xlPasteValues
    'EA列のGCIを排除
    Selection.Replace What:="00GCI", Replacement:=""

    'EB4〜EB列をセット
    Set MyRange = Range("EB4:EB" & MyWb1RwMax)
    Range("EB4").AutoFill Destination:=MyRange

    'EC4〜EC列をセット
    Set MyRange = Range("EC4:EC" & MyWb1RwMax)
    Range("EC4").AutoFill Destination:=MyRange
    'EC列を値として貼り付け
    Range("EC4:EC" & MyWb1RwMax).Copy
    Range("EC4:EC" & MyWb1RwMax).PasteSpecial Paste:=xlPasteValues
    'EC列のGCIを排除
    Selection.Replace What:="00GCI", Replacement:=""

    'ED4〜ED列をセット
    Set MyRange = Range("ED4:ED" & MyWb1RwMax)
    Range("ED4").AutoFill Destination:=MyRange

    'EE4〜EE列をセット
    Set MyRange = Range("EE4:EE" & MyWb1RwMax)
    Range("EE4").AutoFill Destination:=MyRange

    'EF4〜EF列をセット
    Set MyRange = Range("EF4:EF" & MyWb1RwMax)
    Range("EF4").AutoFill Destination:=MyRange

    'EG4〜EG列をセット
    Set MyRange = Range("EG4:EG" & MyWb1RwMax)
    Range("EG4").AutoFill Destination:=MyRange

    'EH4〜EH列をセット
    Set MyRange = Range("EH4:EH" & MyWb1RwMax)
    Range("EH4").AutoFill Destination:=MyRange

    'EI4〜EI列をセット
    Set MyRange = Range("EI4:EI" & MyWb1RwMax)
    Range("EI4").AutoFill Destination:=MyRange

    'EJ4〜EL列をセット
    Set MyRange = Range("EJ4:EL" & MyWb1RwMax)
    Range("EJ4:EL4").AutoFill Destination:=MyRange

 '目検のため機械チェックマージ対象(EI列=1)でフィルタの設定
    Rows("3:3").AutoFilter
    Rows("3:3").AutoFilter Field:=140, Criteria1:="1"
    Range("DU:EL,DS:DS,DQ:DQ,DO:DO,DM:DM,DK:DK,W:DI,U:U,Q:S,G:O,D:E").EntireColumn.Hidden = True
    Range("F:F").ColumnWidth = 35
    Application.ScreenUpdating = True
    Range("F3").Select

    MsgBox "顧客名・法人格等の目視チェックを行なってください。", vbOKOnly + vbInformation, "作業中間通知"

 End Sub

 Sub 新規ファイル作成()

    Dim i As Long
    Dim Wbook3 As Workbook
    Dim MyWb3RwMax As Long
    Dim MyRange As Range
    Dim Wb As Workbook
    Dim flag As Boolean

 '全て再表示させ、A列のチェック行をEG列のチェック行に1を入力
    Worksheets("名寄せ候補template").Select
    Rows("3:3").AutoFilter
    Columns("C:EQ").EntireColumn.Hidden = False
    ActiveWindow.ScrollRow = 4
    Range("D2").Select

    ActiveSheet.EnableCalculation = False '---再計算停止

    For i = 4 To MyWb1RwMax ←←←←←←←←←←←←←←←←←←ここが引き継がれていませんので値が0のままです。
        If Range("EI" & i).Value = 1 And _
            Range("A" & i).Value = Range("A1").Value Then
            Range("EJ" & i).Value = 1
        End If
    Next i

    ActiveSheet.EnableCalculation = True '---再計算再開

 '最終一括更新マージ対象(EK列=1)を抽出
    Rows("3:3").AutoFilter Field:=142, Criteria1:="1"

 '新ブックへの貼り付け
    Range("C:C,E:E").Copy
    Range("D1").Select
    Workbooks.Add
    Set Wbook3 = ActiveWorkbook
    ActiveSheet.Paste
    Worksheets("Sheet1").Name = "一括更新マージ対象"

    Wbook1.Worksheets("名寄せ候補template").Range("EN1:EP3").Copy ←←←←←←←←←←←←←←←←←←ここで「オブジェクト変数または〜」の表示。

    Wbook3.Activate
    Worksheets("一括更新マージ対象").Range("F5").PasteSpecial Paste:=xlPasteValues
    Worksheets("一括更新マージ対象").Range("F:F").EntireColumn.AutoFit
    Worksheets("一括更新マージ対象").Range("G4").Value = "セット数"
    Worksheets("一括更新マージ対象").Range("H4").Value = "件数"
    Worksheets("一括更新マージ対象").Range("G5:H7").Style = "Comma [0]"
    With Range("F5:F7,G4:H7")
        .BorderAround LineStyle:=xlContinuous
        .Borders(xlInsideVertical).LineStyle = xlContinuous
        .Borders(xlInsideHorizontal).LineStyle = xlContinuous
    End With
    With Range("F4")
        .HorizontalAlignment = xlRight
        .Value = FolderName
    End With
    Rows("3:3").RowHeight = 31.5
    Worksheets("一括更新マージ対象").Range("A1").Select

 '名寄せ結果情報確認の準備(顧客統合コードをVALUE関数にて表示)
    Worksheets("一括更新マージ対象").Copy After:=Sheets(1)
    Worksheets("一括更新マージ対象 (2)").Name = "名寄せ結果情報確認用"
    Columns("C:C").Insert Shift:=xlToRight
    Range("B3").Copy Destination:=Range("C3")
    With Range("C3")
        .Font.Size = 8
        .HorizontalAlignment = xlCenter
        .Value = "顧客統合" & Chr(10) & "コード" & Chr(10) & "(数値化)"
    End With
    Range("C4").ClearFormats
    With Range("C4")
        .FormulaR1C1 = "=VALUE(RC[-1])"
        .BorderAround LineStyle:=xlContinuous
    End With
    MyWb3RwMax = Range("A" & Rows.Count).End(xlUp).Row
    Set MyRange = Range("C4:C" & MyWb3RwMax)
    Range("C4").AutoFill Destination:=MyRange
    Columns("C:C").EntireColumn.AutoFit
    Range("A1").Select

 '一括更新編集用の準備作成(顧客統合コードをTEXT関数にて表示)
    Worksheets("一括更新マージ対象").Copy Before:=Sheets(2)
    Worksheets("一括更新マージ対象 (2)").Name = "一括更新編集用の準備"
    Worksheets("一括更新編集用の準備").Select
    Columns("B:B").Cut
    Columns("A:A").Insert Shift:=xlToRight
    Columns("B:B").Insert Shift:=xlToRight
    Range("A3").Copy Destination:=Range("B3")
    With Range("B3")
        .Font.Size = 8
        .HorizontalAlignment = xlCenter
        .Value = "顧客統合" & Chr(10) & "コード" & Chr(10) & "文字列化"
    End With
    Range("B4").ClearFormats
    With Range("B4")
        .FormulaR1C1 = "=TEXT(RC[-1],0)"
        .Borders(xlEdgeTop).LineStyle = xlContinuous
    End With

 'M4の式を最終行まで貼り付ける
    Set MyRange = Range("B4:B" & MyWb3RwMax)
    Range("B4").AutoFill Destination:=MyRange
    Range("A1").Select

 '不要なシートを削除
    Worksheets(Array("Sheet2", "Sheet3")).Select
    Application.DisplayAlerts = False
    ActiveWindow.SelectedSheets.Delete
    Application.DisplayAlerts = True
    Worksheets("一括更新マージ対象").Select

 '名寄せ候補templateを元のファイルに戻す
    Wbook1.Activate

    For Each Wb In Workbooks
            If Wb.Name = Wbook2.Name Then flag = True
        Next Wb
        If flag = True Then
            Worksheets("名寄せ候補template").Copy After:=Wbook2.Worksheets(1)
            MsgBox "これで作業は終了です。候補リストは上書保存、新しいファイルにはExcle 97-2003 ブックにて名前を付けて保存してください。作業用ファイルは保存せずに閉じてください。", _
            vbOKOnly + vbInformation, "作業終了通知"
        Else
            MsgBox "最初に選択した加工用の候補リストファイルが見つかりません。ファイルを開き、この作業用ファイルのシート『名寄せ候補template』をコピーして上書き保存をしてください。新しいファイルにはExcle 97-2003 ブックにて名前を付けて保存してください。作業を終了します。", _
                vbOKOnly + vbExclamation, "ファイルが見つかりません"
        End If

 End Sub


 目検対象抽出 というプロシジャーを実行する。
 ↑このプロシジャー内でモジュールレベルの変数にデータを設定

 手動操作でセルをダブルクリック(イベントが発生)

 新規ファイル作成 というプロシジャーを実行する
 ↑このプロシジャー内でモジュールレベルの変数を参照しようとすると、
 モジュールレベルの変数に設定したはずのデータが設定されていない

 という現象ですよね!!
 VBAでは、モジュールレベルの変数が何らかの原因で初期化されてしまうという現象は、
 今までにも報告されています。ただ、原因は一つではないようです。
 私の知っているのは、ActiveXcontrolをシートに貼り付けると初期化される ですが・・・。

 まず、何が起因しているか? 簡単なコードで探ってみては?

 thisworkbookに

  Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)

    If Target.Column = 1 And Target.Value = "" Then
        Target.Value = Range("A1")
        Target.Offset(0, 1).Select

    ElseIf Target.Column = 1 And Target.Value = Range("A1") Then
        Target.Value = ""
        Target.Offset(0, 1).Select

    End If

 End Sub

 標準モジュールに
 private aaa as long
 sub test1()
    aaa=5
 end sub

 sub test2()
    msgbox aaa
 end sub

 こんなコードで

 test1の実行-----ダブルクリックでイベント発生-----test2の実行

 これでtest2で変数(aaa)の値は どうなのか?

 というように原因に近いづいていく ということです。

 試してみてください。

 ichinose

 


 それってこれだと思うのですが、、(もし無関係でしたら済みません)
      ↓
 [VBA] Public 宣言された変数の有効期間
http://support.microsoft.com/kb/408871/ja

 (半平太) 2010/07/31 15:31

 半平太さん、
 リンク参考になりました。
 スコープとして保障されているのはプロシージャ実行間だけだったんですね。
 今までもやもやしていたのがすっきりしました。
 (Mook)

 ichinoseさんのおっしゃる通りですね。簡単なコードで試してみます。
 ありがとうございました!

 半平太さんもありがとうございました!
 VBAの本を読んでも、ネットで調べても、変数の有効期限についての説明が全て同じで
 リンク先の存在を知らなかったので、なんだか少しだけ解消したような、
 でも、さんざん調べてきたのに・・・と少し複雑ですが、
 こんなこともあるんだと納得しました。

 以前は出来ていて少々コードの編集をした時からエラーが出てきてしまったので
 このことを踏まえて再度チャレンジしてみます。

 お二人の方、本当にありがとうございました。(ゆき夫)

 半平太さんご紹介の本社サイトの
 >意図しないタイミング
 このタイミングに再現性があるなら、一つでも多く知りたいなあ という趣旨で
 現象を調べてみることは良いことだと思います。

 で、結論としては、上のサイトではシートにデータを保持しなさい ということですよね?

 ゆき夫さんの事象の場合、モジュールレベルの変数は

 >   Private Wbook1 As Workbook      '---このファイル
 >   Private Wbook2 As Workbook      '---加工ファイル
 >   Private MyWb1RwMax As Long      '---候補リストの最終行
 >   Private FolderName As String    '---選択フォルダパス名

 これですね!! Wbook1 とWbook2は、オブジェクトではなく、ブック名として、セルに保存し、

 range("a1").value="ブック1.xls" 

 運用時は、 workbooks(range("a1").value) とすれば、ブックオブジェクトの参照が可能です。

 他のLong型やString型の変数もセルに保存することで解決できそうですよね?

 又は、仕様を変更して

  目検対象抽出 ----(イベントが発生)-----  新規ファイル作成 

 という流れをひとつのプロシジャーで実行する

 なんて方法も検討材料になるでしょうか?

 いずれにせよ、代替方法はありそうです。が、

オブジェクト変数が他に代替出来ないような事象はどうするのだろう? 

 (例えば、イベントを有しているオブジェクト等)

 こんなとき、私は、別ブック内に変数だけを配置しています
 Book1がメインプログラムがあるブックに対し、 Book2が変数だけを配置するブックだとすると、

 Book2のthisworkbookのモジュールに

 private bk as workbook

 Book1にて

 sub appset() 
    set workbooks("book2").bk=thisworkbook
    msgbox workbooks("book2").bk.name
 end sub

 いまところ、変数を別ブックに配置し、そのブックは、非表示又は、専用アドインブックにしておく
 ことで値を保持してくれています。

 これで大丈夫という結論ではありませんが、別ブックに変数を配置する方法も試してみてください。

 ichinose

 


コメント返信:

[ 一覧(最新更新順) ]


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