Die Community zu .NET und Classic VB.
Menü

Addin-Programmierung - Seite 4

 von 

Manipulieren von Objekten
Nächste Seite >>
Die Entwicklungsumgebung
<< Vorherige Seite

Projekte und Komponenten  

Auslesen von Projekt und Komponent - Daten

Die meisten Benutzer wird aber interessieren, wie er Zugriff auf das aktuelle Projekt und dessen Komponenten bzw. auf die aktuell selektierte Komponente und dessen Codefenster erhält, um den Code auszulesen, zu manipulieren oder zu löschen. Das VBE-Objekt enthält ein Property mit dem Namen ActiveVBProject mit dem wir Zugriff auf das Objekt VBProject erhalten, also auf das aktive Projekt im Projektexplorer. Über das Objekt VBProject erhalten wir Zugriff auf verschiedene andere Objekte und Funktionen. So lassen sich über VBProject folgende Objekte ansprechen: VBComponents zum Auslesen aller geladenen Komponenten eines Projektes oder Laden einer Komponente, References zum Auslesen aller geladenen Verweise eines Projektes oder Laden eines Verweises. Darüber hinaus können Informationen zum aktuellen Projekt ausgelesen bzw. gesetzt werden, so z.B. der Name, der Typ und Dateiname des Projekts uvm. Sogar speichern und kompilieren lässt sich das Projekt über dieses Objekt. Hier ein Beispiel, wie man Projekt-Informationen auslesen kann.

Private Sub OKButton_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBProj As VBProject 'Projekt
    
    ' Variablen zu zwischenspeichern
    Dim VBProjName As String
    Dim VBProjFileName As String
    Dim VBProjType As String
    Dim VBProjStartMode As String
    
    ' Objekt instanzieren
    Set VBProj = VBInstance.ActiveVBProject 'aktives Projekt

    VBProjName = VBProj.Name 'Name des aktiven Projektes auslesen
   
    ' Typ des Projektes ermitteln
    Select Case VBProj.Type
        Case vbext_pt_StandardExe
            VBProjType = "StandardExe"
        Case vbext_pt_ActiveXExe
            VBProjType = "ActiveXExe"
        Case vbext_pt_ActiveXDll
            VBProjType = "ActiveXDll"
        Case vbext_pt_ActiveXControl
            VBProjType = "ActiveXControl"
    End Select
        
    ' StartMode des Projektes ermitteln
    Select Case VBProj.StartMode
        Case vbext_psm_StandAlone
            VBProjStartMode = "StandAlone"
        Case vbext_psm_OleServer
            VBProjStartMode = "OleServer"
    End Select
    
    ' Dateiname des Projektes ermitteln
    If VBProj.FileName = vbNullString Then
        VBProjFileName = VBProj.Name
    Else
        VBProjFileName = VBProj.FileName
    End If
    
    ' Anzeigen der Eigenschaften
    Text1.Text = "Aktives Projekt: " & VBProjName & vbNewLine & _
    "ProjektTyp: " & VBProjType & vbNewLine & _
    "ProjektFileName: " & VBProjFileName & vbNewLine & _
    "ProjektStartMode: " & VBProjStartMode
    
    Set VBProj = Nothing
    
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Wenn es aber darum geht, auf die selektierte Komponente zuzugreifen, dann steht uns das Property "SelectedVBComponent" in dem Objekt "VBE" zur Verfügung. Über dieses Property erhalten wir Zugriff auf das Objekt "VBComponent". Dieses Objekt enthält weitere Objekte z.B. zum Zugriff auf das Objekt "CodeModule". Auch lassen sich hier die Eigenschaften (Properties) der Komponente auslesen bzw. ändern. Über das Objekt "CodeModule" können wir auch den Code einer Komponente auslesen, ändern und löschen. Ein weiteres Objekt im Objekt "CodeModule" ist das Objekt "CodePane". Über diese Objekt lässt sich das Codefenster steuern und Code markieren bzw. markierten Code auslesen. Hier nur ein kleiner Ausschnitt der Möglichkeiten.

Private Sub Command5_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBComp As VBComponent 'Komponente
    
    ' Variablen zu zwischenspeichern
    Dim VBCompName As String
    Dim VBCompFileName As String
    Dim VBCompType As String
    Dim VBCompFiles As Long
    Dim VBCompFilesData As String
    
    ' Objekte instanzieren
    Set VBComp = VBInstance.SelectedVBComponent 'selektierte Komponente
    
    VBCompName = VBComp.Name 'Name der sel. Komponente auslesen
        
    ' Typ der Komponente ermitteln
    ' es gibt aber noch mehr Typen !
    Select Case VBComp.Type
        Case vbext_ct_VBForm
            VBCompType = "Form"
        Case vbext_ct_StdModule
            VBCompType = "Modul"
        Case vbext_ct_ClassModule
            VBCompType = "Klassenmodul"
    End Select
    
    ' Dateiname der selektierten Komponente ermitteln
    If VBComp.FileNames(1) = vbNullString Then
        VBCompFileName = VBComp.Name
    Else
        'Dateiname hat den Index 1
        VBCompFileName = VBComp.FileNames(1)
    End If
    
    ' Verbundene Dateien auslesen zB frx
    VBCompFilesData = "Verbundene Dateien:" & vbNewLine
    ' ab Index 2 beginnen die verb. Dateien
    If VBComp.FileCount > 1 Then
        For VBCompFiles = 2 To VBComp.FileCount
            VBCompFilesData = VBCompFilesData & VBComp.FileNames(VBCompFiles) & vbNewLine
        Next VBCompFiles
    End If
    
    ' Anzeigen der Eigenschaften
    Text1.Text = _
    "Selektierte Komponente: " & VBCompName & vbNewLine & _
    "KomponenteFileName: " & VBCompFileName & vbNewLine & _
    "KomponentenTyp: " & VBCompType & vbNewLine & _
    VBCompFilesData
    
    Set VBComp = Nothing
        
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Außer der Möglichkeit die Daten einer Komponente auszulesen, gibt es noch die Möglichkeit auf den Code einer Komponente zuzugreifen wie z.B. Hinzufügen von Code, Ändern von Code, Löschen von Code, Auslesen des Codes im Deklarationsabschnitt sowie im Codeabschnitt. Hier ein Beispiel zum Einfügen von Code in der selektierten Komponente.

Private Sub Command8_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBComp As VBComponent 'Komponente
    Dim VBCodeM As CodeModule 'CodeModul
    Dim VBCodeP As CodePane 'CodePane

    ' Objekte instanzieren
    Set VBComp = VBInstance.SelectedVBComponent 'selektierte Komponente
    Set VBCodeM = VBComp.CodeModule 'Codemodul von sel. Komponente
    Set VBCodeP = VBCodeM.CodePane 'Codefenster von sel. Komponente

    ' Codefenster anzeigen wenn noch nicht geöffnet
    VBCodeP.Show
    
    ' Einfügen von Text in Zeile 1 im Codemodul
    VBCodeM.InsertLines 1, "'Neue Zeile einfügen"

    Set VBComp = Nothing
    Set VBCodeM = Nothing
    Set VBCodeP = Nothing
        
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Ein Objekt darf hier natürlich nicht fehlen. Das Member-Objekt. Das Member-Objekt enthält alle öffentlichen Elemente einer Komponente wie Variablen, Subs, Funktionen, Properties, Konstanten und Events. Eine Möglichkeit, wie man Enums und Types direkt von der VBIDE auslesen kann, gibt es leider nicht. Auch die Variablen usw. innerhalb einer Sub oder Funktion können nicht direkt ausgelesen werden. Ebenso verhält es sich bei den Parametern einer Sub oder Funktion. Bei einer Methode wird auch nicht angezeigt, ob es sich um eine Sub, Function oder API handelt. Bei Properties kann es unter Umständen dazu kommen, dass dreimal der gleiche Propertyname angezeigt wird und zwar für "Property Let PropertyName", "Property Get PropertyName" und "Property Set PropertyName". Da bleibt einem nur das Durchforsten des Codes Zeile für Zeile um herauszufinden ob es sich um ein Let- ,Get- und/oder Set-Property handelt. Hier das Beispiel zum auslesen von Memberdaten aus der selektierten Komponente.

Private Sub Command13_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBComp As VBComponent 'Komponente
    Dim VBCodeM As CodeModule 'Codemodule
    Dim VBMember As Member 'Member

    ' Variable zum zwischenspeichern
    Dim MemberData As String
    Dim MemberType As String
    Dim MemberScope As String
    
    ' Objekte instanzieren
    Set VBComp = VBInstance.SelectedVBComponent 'selektierte Komponente
    Set VBCodeM = VBComp.CodeModule 'Codemodul von sel. Komponente
    
    ' wenn keine Members vorhanden
    If VBCodeM.Members.Count < 1 Then Exit Sub
    
    MemberData = vbNullString
    For Each VBMember In VBCodeM.Members
        'Membertyp ermitteln
        Select Case VBMember.Type
            Case vbext_mt_Method
            MemberType = "Methode"
            Case vbext_mt_Property
            MemberType = "Property"
            Case vbext_mt_Variable
            MemberType = "Variable"
            Case vbext_mt_Event
            MemberType = "Event"
            Case vbext_mt_Const
            MemberType = "Const"
        End Select
    
        'Memberscopetyp ermitteln
        Select Case VBMember.Scope
            Case vbext_Private
            MemberScope = "Private"
            Case vbext_Public
            MemberScope = "Public"
            Case vbext_Friend
            MemberScope = "Friend"
        End Select
    
        MemberData = MemberData & "Membername: " & VBMember.Name & vbNewLine & _
        "Membertyp: " & MemberType & vbNewLine & _
        "MemberScope: " & MemberScope & vbNewLine & _
        "In Zeile: " & CStr(VBMember.CodeLocation) & vbNewLine & _
        "Zeile: " & VBCodeM.Lines(VBMember.CodeLocation, 1) & vbNewLine & _
        vbNewLine
    Next VBMember
    
    Text1.Text = MemberData
    
    Set VBComp = Nothing
    Set VBCodeM = Nothing
    
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Eine Eigenart der VB-Entwicklungsumgebung ist, dass Codezeilen wie

'Die API ist hier in vier Zeilen aufgeteilt, intern wird diese Zeile
'aber als eine Zeile behandelt
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, _
ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, _
ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, _
ByVal ySrc As Long, ByVal dwRop As Long) As Long

'Die Variablen stehen hier in einer Zeilen, intern wird diese Zeile
'aber wie drei Zeilen behandelt
Dim Variable6 As Long: Dim Variable7 As Long: Dim Variable8 As Long

intern anders behandelt werden als im Codefenster, so dass über "VBMember.CodeLocation" die falsche Zeilennummer ausgelesen wird. Wenn der Unterstrich und der Doppelpunkt weggelassen werden, werden die korrekten Zeilennummern ausgelesen.

Das Finden von Membern im Code gestaltet sich recht umständlich da man ja nur den Namen des Members hat. Bei Prozeduren ist das noch recht einfach, da es dafür eine entsprechende Funktion gibt. Bei Properties ist es schon etwas schwieriger. Da man nur den Namen hat, aber nicht weiß, ob es sich um eine Get-, Set- oder Let-Property handelt. Hier ein Beispiel zum Finden von Membern im Code. (einfach die Mitglieder über den Button "Memberdaten von Komponente" auflisten und einen Membernamen in das Textfeld kopieren und dann den Button "Suchen und Markieren von Member" drücken.)

Private Sub Command15_Click()
    ' Variablen zum zwischenspeichern
    Dim sRow As Long 'Startzeile
    Dim eRow As Long 'Endzeile
    Dim sCol As Long 'Startsplate
    Dim eCol As Long 'Endspalte
    Dim lngPosition As Long 'gefundene Zeile
    
    ' in selektierter Komponente suchen
    With VBInstance.SelectedVBComponent
        With .CodeModule
            ' Temporarily defeat error trapping
            On Error Resume Next
            
            ' für Prozeduren (Sub, Function, API´s)
            lngPosition = .ProcBodyLine(Text2.Text, vbext_pk_Proc)
            ' für Property Let
            lngPosition = .ProcBodyLine(Text2.Text, vbext_pk_Let)
            ' für Property Get
            lngPosition = .ProcBodyLine(Text2.Text, vbext_pk_Get)
            ' für Property Set
            lngPosition = .ProcBodyLine(Text2.Text, vbext_pk_Set)

            ' für sonstige Member und Code
            If .Find(Text2.Text, sRow, sCol, eRow, eCol, False, False, False) Then
                lngPosition = sRow
            End If
            
            With .CodePane
                ' Codefenster anzeigen
                .Window.Visible = True
                
                ' setzt den Cursor vor dem Member
                .SetSelection lngPosition, 1, lngPosition, 1
                
                ' markiert die ganze Zeile vom Member
                '.SetSelection lngPosition, 1000, lngPosition, 1
                
                ' Focus auf Codefenster setzen
                .Window.SetFocus
            End With
        End With
    End With
End Sub

Dann haben wir noch das Designer-Objekt. Der Zugriff auf das Designerfenster einer Komponente erfolgt über das "VBComponent"-Objekt. Ob eine Komponente ein Designerfenster besitzt, hängt vom Typ der Komponente ab. Ein Modul besitzt z.B. kein Designerfenster aber eine Form schon. Über das "Designer"-Objekt kann man auf die Entwurfsmerkmale einer Komponente zugreifen z.B. die Controls auf einer Form auslesen, hinzufügen und manipulieren. Hier ein Beispiel zum auslesen der Control-Informationen.

Private Sub Command14_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBComp As VBComponent 'Komponente
    Dim VBCtrl As VBControl  'Control-Objekt

    ' Variable zum zwischenspeichern
    Dim CtrlData As String
    Dim CtrlName As String
    Dim CtrlIndex As String
    Dim CtrlType As String

    ' Objekte instanzieren
    Set VBComp = VBInstance.SelectedVBComponent 'selektierte Komponente

    ' Komponenttyp ermitteln
    Select Case VBComp.Type
        Case vbext_ct_VBForm, vbext_ct_VBMDIForm, vbext_ct_MSForm, _
        vbext_ct_DocObject, vbext_ct_PropPage, vbext_ct_UserControl
        
        ' Designerfenster anzeigen
        VBComp.DesignerWindow.Visible = True
        VBComp.DesignerWindow.SetFocus
        
        ' wenn keine Controls vorhanden sind
        If VBComp.Designer.VBControls.Count < 1 Then Goto ende
    
        ' Controldaten auslesen
        CtrlData = vbNullString
        For Each VBCtrl In VBComp.Designer.VBControls
            'Name des Controls ermitteln
            CtrlName = VBCtrl.Properties.Item("Name").Value
            'Index des Controls ermitteln
            CtrlIndex = VBCtrl.Properties.Item("Index").Value
            
            'wenn Index =>0, dann handelt es sich um ein Controlarray
            If CLng(CtrlIndex) >= 0 Then CtrlName = CtrlName & "(" & CtrlIndex & ")"
            
            'Typ des Controls auslesen
            Select Case VBCtrl.ControlType
                Case vbext_ct_Light
                CtrlType = "Light"
                Case vbext_ct_Standard
                CtrlType = "Standard"
                Case vbext_ct_Container
                CtrlType = "Container"
            End Select
            
            'restliche Daten auslesen
            CtrlData = CtrlData & "ControlName: " & CtrlName & vbNewLine & _
            "ControlClassName: " & VBCtrl.ClassName & " (" & VBCtrl.ProgId & ")" & vbNewLine & _
            "ControlTyp: " & CtrlType & vbNewLine & _
            "ControlSelected: " & CStr(VBCtrl.InSelection) & vbNewLine & vbNewLine
        Next VBCtrl
        
        ' Daten anzeigen
        Text1.Text = CtrlData
        
        ' Designerfenster schließen
        VBComp.DesignerWindow.Close
        
        Case Else ' Komponente hat kein Designerfenster
        Goto ende
    End Select
        
ende:
    Set VBComp = Nothing
    Set VBCtrl = Nothing
    
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Bisher wurden immer das aktuelle Projekt und die aktuell selektierte Komponente im Code verwendet. Manchmal aber muss man auch auf die nicht aktuell selektierte Komponente oder auf das zweite Projekt, das gerade nicht das aktuelle Projekt ist, usw. zugreifen. Auch das ist möglich. Dazu werden die Collection-Objekte verwendet wie z.B. das VBProjects-Objekt oder das VBComponents-Objekt. Diese Objekte enthalten eine Funktion mit dem Namen "Item". Dieser Funktion kann der Index eines Objektes übergeben werden oder der Name des Objektes. Den Index eines Objektes wird man kaum verwenden. Hier nur einige Beispiele, wie man auf nicht selektierte bzw. nicht aktuelle Objekte zugreifen kann. Kombinationen sind möglich.

Private Sub Command15_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    Dim VBProj As VBProject
    Dim VBComp As VBComponent
    Dim VBProp As Property
    
    ' Zugriff auf ein nicht aktuelles Projekt
    Set VBProj = VBInstance.VBProjects("Project2")
    
    ' Zugriff auf eine nicht selektierte Komponente in einem nicht aktuelles Projekt
    Set VBComp = VBInstance.VBProjects("Project1").VBComponents("Module1")
    
    ' Zugriff auf eine nicht selektierte Komponente im aktuellen Projekt
    Set VBComp = VBInstance.ActiveVBProject.VBComponents("Module1")
    
    ' Zugriff auf die Eigenschaft "Name" einer nicht selektierter Komponente des aktuellen Projektes
    Set VBProp = VBInstance.ActiveVBProject.VBComponents("Form1").Properties("Name")
    
    '******************
    '* sonstiger Code *
    '******************
    
    Set VBProj = Nothing
    Set VBComp = Nothing
    Set VBProp = Nothing
    
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description
End Sub

Auslesen aller Projektinformationen

Hier noch mal ein grober Überblick, wie man alle Informationen von geladenen Projekten auslesen kann.

Private Sub AllData_Click()
    ' Bei Fehler zur Fehlerbehandlung
    On Error Goto error_handler
    
    ' Instanzieren der Objekte
    Dim VBProj As VBProject 'Projekt-Objekt
    Dim VBRef As Reference 'Referenz-Objekt
    Dim VBComp As VBComponent 'Komponent-Objekt
    Dim VBCtrl As VBControl  'Control-Objekt
    Dim VBMember As Member 'Member-Objekt

    ' Variable zum zwischenspeichern
    Dim CompFiles As Long

    ' wenn kein Projekt geladen, dann goto NoProj
    If VBInstance.VBProjects.Count < 1 Then Goto NoProjekt
    ' alle geladenen Projekte durchlaufen
    For Each VBProj In VBInstance.VBProjects
        ' Hier können alle Infos zu dem Projekt
        ' ausgelesen werden
        
        ' wenn kein Verweis geladen, dann goto NoReferenz
        If VBProj.References.Count < 1 Then Goto NoReferenz
        ' alle Verweise zum Projekt durchlaufen
        For Each VBRef In VBProj.References
        ' Hier können alle Infos zu den Verweisen
        ' ausgelesen werden
        Next VBRef
NoReferenz:
    
        ' wenn keine Komponente geladen, dann goto NoComponent
        If VBProj.VBComponents.Count < 1 Then Goto NoComponent
        ' alle Komponeten zum Projekt durchlaufen
        For Each VBComp In VBProj.VBComponents
        ' Hier können alle Infos zu der Komponente
        ' ausgelesen werden
        
                ' wenn keine verbundenen Datein (z.B. frx), dann goto NoCompFiles
                If VBComp.FileCount < 2 Then Goto NoCompFiles
                ' alle verbundenen Dateien zur Komponente durchlaufen
                For CompFiles = 2 To VBComp.FileCount
                ' Hier können alle Infos zu den verbundenen Dateien
                ' ausgelesen werden
                Next CompFiles
NoCompFiles:
                
                '***************************************************
                '* Hier muss noch die VBComp.Type Abfrage erfolgen *
                '* um die anderen Objekte der Komponente ohne      *
                '* Fehler auslesen zu können.                      *
                '* z.B. enthält eine Res-Komponente kein Designer-,*
                '* CodeModule-, CodePane- und Member-Objekt        *
                '***************************************************
                
                ' wenn kein Control auf Komponente, dann goto NoControl
                If VBComp.Designer.VBControls.Count < 1 Then Goto NoControl
                ' alle Controls zur Komponente durchlaufen
                For Each VBCtrl In VBComp.Designer.VBControls
                ' Hier können alle Infos zu den Controls
                ' ausgelesen werden
                Next VBCtrl
NoControl:

                ' wenn keine Members vorhanden, dann goto NoMember
                If VBComp.CodeModule.Members.Count < 1 Then Goto NoMember
                ' alle Member im Codemodule durchlaufen
                For Each VBMember In VBComp.CodeModule.Members
                ' Hier können alle Infos zu den Members
                ' ausgelesen werden
                Next VBMember
NoMember:

        
        Next VBComp
NoComponent:
    
    Next VBProj
        
NoProjekt:
    
    Set VBProj = Nothing
    Set VBRef = Nothing
    Set VBComp = Nothing
    Set VBCtrl = Nothing
    Set VBMember = Nothing
    
    ' Wenn alles ok, dann Methode verlassen
    Exit Sub

' Fehlerbehandlung
error_handler:
    
    ' Fehler anzeigen
    MsgBox Err.Description

End Sub
Nächste Seite >>
Manipulieren von Objekten
<< Vorherige Seite
Die Entwicklungsumgebung