2017-04-20 16 views
1

プロシージャに引数を渡すことに慣れています。代わりに、ParamArrayは、0個以上の引数を手続きに参照として渡す柔軟性を私に許しています。しかし、このアプローチでは、プロシージャの範囲を超えて1つ以上の変数への参照を保持する方法があるかどうか疑問に思っていました。VBA - 安全に変数参照を格納する

Array(ParamArray ArgList() As Variant)

だから、私は一緒に次のテストコード置く:私はそれがこのように宣言して見たときの希望の私の最初の兆しがVBA Array機能でした残念ながら

Private Sub Test() 

    Dim a As Object 
    Dim b() As Variant 

    ParamArrayTest a 
    Debug.Print TypeName(a) ' Output is 'Dictionary' 
    b = Array(a)   ' b should be like ParamArray ArgList() 
    Set b(0) = Nothing  ' This should clear a 
    Debug.Print TypeName(a) ' Output is still 'Dictionary' 

End Sub 

Private Sub ParamArrayTest(ParamArray ArgList() As Variant) 

    Set ArgList(0) = CreateObject("Scripting.Dictionary") 

End Sub 

を、これはしませんでした期待どおりの仕事。引数がParamArray経由でArray関数に渡されたにもかかわらず、返された配列は参照ではなく値であるように見えます。

更なる研究により、私は文書化されていないVBA /StrPtr/ObjPtrの機能に導かれました。 API RtlMoveMemoryの機能と組み合わせて使用​​する例が多数見つかりました。しかし、私が読んだすべての記事は、アプリケーションを非常に簡単にクラッシュさせる可能性があるため、このアプローチを使用することを強く奨励しました。私のテストの中には、実際にアクセスがクラッシュしたものもあります。

私が持っていたもう一つのアイデアは、私が直接、別の変数の参照を割り当てることができるかどうかを確認することでした。

Private Sub Test() 

    Dim a As Object 
    Dim b As Variant 

    b = ByRef a ' Throws a compiler error 

End Sub 

を言えば十分で、コンパイラは、単にそれを許可しないでしょう。私の質問は、可変参照は、プロシージャの範囲(好ましくは別の変数)の範囲を超えて安全に保存/保存できるかどうかです。私は私が構築しようとしているものにいくつかの光を当てる場合、それは、より参考になることを決めた

EDIT

私は現在、すべてのフォーム/コントロールイベントを自分のモジュールのプロシージャに渡すラッパークラスを作成中です。これは、同じ制御構造を持ち、異なるソース表に接続する2つの形式で使用されます。コードは不完全ですが、私が克服しようとしている問題を説明するのに十分であるはずです。また、Databaseは私のVBAプロジェクト名です。

コードへの4つの部分があります。

  1. Form_TEST_FORM - フォームモジュール

    Private Sub Form_Open(Cancel As Integer) 
    
        FormHub.InitForm Me, Cancel 
    
    End Sub 
    
  2. FormHub - モジュール

    Public Sub InitForm(_ 
        ByRef Form As Access.Form, _ 
        ByRef Cancel As Integer _ 
    ) 
    
        Dim Evt As Database.EventHandler 
    
        Set Evt = New Database.EventHandler 
        Evt.InitFormObject Form, Cancel 
        FormList.Add Evt, Form.Name 
    
    End Sub 
    
    Private Function FormList() As VBA.Collection 
    
        Static Init As Boolean 
        Static Coll As VBA.Collection 
    
        If Not Init Then 
    
         Set Coll = New VBA.Collection 
         Init = True 
    
        End If 
    
        Set FormList = Coll 
    
    End Function 
    
  3. FormControl - クラスモジュールは

    Public Ptr  As Variant ' Pointer to form control variable 
    Public acType As Access.AcControlType 
    
  4. EventHandler - クラスモジュール

    Private WithEvents Form  As Access.Form 
    Private WithEvents SForm As Access.SubForm 
    Private CtrlList   As VBA.Collection 
    
    Private Sub Class_Initialize() 
    
        InitCtrlList 
    
    End Sub 
    
    Public Sub InitFormObject(FormObj As Access.Form, ByRef Cancel As Integer) 
    
        Dim ErrFlag As Boolean 
        Dim Ctrl As Access.Control 
        Dim FCtrl As Database.FormControl 
    
        On Error GoTo Proc_Err 
    
        Set Form = FormObj 
    
        If Form.Controls.Count <> CtrlList.Count Then 
    
         Err.Raise 1, , _ 
         "Form has incorrect number of controls" 
    
        End If 
    
        ' This is where I want to validate the form controls 
        ' and also initialize my event variables. 
        For Each Ctrl In Form.Controls 
    
         If Not CtrlExists(FCtrl, Ctrl.Name) Then 
    
          Err.Raise 2, , _ 
          "Invalid control name" 
    
         ElseIf FCtrl.acType <> Ctrl.ControlType Then 
    
          Err.Raise 3, , _ 
          "Invalid control type" 
    
         Else 
    
          ' Initialize the correct variable with it's 
          ' pointer. This is the part I haven't been 
          ' able to figure out yet. 
          Set FCtrl.Ptr = Ctrl 
    
         End If 
    
        Next 
    
    Proc_End: 
    
        On Error Resume Next 
    
        If ErrFlag Then 
    
         ClearEventVariables 
    
        End If 
    
        Set Ctrl = Nothing 
        Set FCtrl = Nothing 
    
        Exit Sub 
    
    Proc_Err: 
    
        ErrFlag = True 
        Debug.Print "InitFormObject " & _ 
        "Error " & Err & ": " & Err.Description 
        Resume Proc_End 
    
    End Sub 
    
    Private Function CtrlExists(_ 
        ByRef FCtrl As Database.FormControl, _ 
        ByRef CtrlName As String _ 
    ) As Boolean 
    
        On Error Resume Next 
    
        Set FCtrl = CtrlList(CtrlName) 
        CtrlExists = Err = 0 
    
    End Function 
    
    Private Sub InitCtrlList() 
    
        Set CtrlList = New VBA.Collection 
        CtrlList.Add SetCtrlData(SForm, acSubform), "SForm" 
    
    End Sub 
    
    Private Function SetCtrlData(_ 
        ByRef Ctrl As Access.Control, _ 
        ByRef acType As Access.AcControlType _ 
    ) As Database.FormControl 
    
        Set SetCtrlData = New Database.FormControl 
    
        With SetCtrlData 
    
         ' This assignment is where I need to keep a reference 
         ' to the variable in the class. However, it doesn't 
         ' work. 
         Set .Ptr = Ctrl 
         .acType = acType 
    
        End With 
    
    End Function 
    
    Private Sub ClearEventVariables() 
    
        Dim FormCtrl As Database.FormControl 
    
        Set Form = Nothing 
    
        For Each FormCtrl In CtrlList 
    
         ' Assuming I was able to retain a reference to the 
         ' class variable, this would clear it. 
         Set FormCtrl.Ptr = Nothing 
    
        Next 
    
    End Sub 
    
    Private Sub Class_Terminate() 
    
        ClearEventVariables 
        Set CtrlList = Nothing 
    
    End Sub 
    

Iは、簡単にするためにコード例1つの制御を使用します。しかし、アイデアは、フォームデザインが変更された場合に、コントロールを追加/削除するために変更する必要のあるコードの量を簡単にすることです。あるいは、私はプロジェクトにさらにフォームを追加する必要があります。

+0

「*プロシージャの範囲外の1つ以上の変数への参照を保持する」ということはまったくどういう意味ですか?あなたの質問はむしろ不明です。 –

+0

@ Mat'sMugたとえば、変数「ByRef」に変数を渡すと、手続き引数を変更することでソース変数の値を変更できます。しかし、プロシージャが終了するとすぐに、私はソース変数へのポインタを失います。私は安全にソース変数にポインタを格納したいので、その値を1つのプロシージャの範囲外に更新できます。この説明が理にかなっているかどうか教えてください。 –

+0

"私はソース変数へのポインタが失われています"という部分は私には分かりません。プロシージャが終了すると、呼び出し側に戻ります。ここには、ByRefを渡していた変数があります。私はちょうどあなたが解決しようとしている問題を見ていない。 –

答えて

0

1つのモジュール内でのみ参照する必要がある場合は、モジュールヘッダーでPublicとして宣言します。任意のモジュールで参照する場合は、汎用モジュールヘッダーでグローバルとして宣言します。配列とレコードセットと接続オブジェクトでさえ、このように宣言することができます。実行時にコードが壊れた場合、これらの変数は値を失うことに注意してください。

または、TempVarオブジェクト変数を調べてください。コードが破損しても値は失われません。しかし、番号やテキストの値だけを格納することはできますが、オブジェクトは格納できません。

関連する問題