Live-Forum - Die aktuellen Beiträge
Anzeige
Archiv - Navigation
936to940
Aktuelles Verzeichnis
Verzeichnis Index
Übersicht Verzeichnisse
Vorheriger Thread
Rückwärts Blättern
Nächster Thread
Vorwärts blättern
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
936to940
936to940
Aktuelles Verzeichnis
Verzeichnis Index
Verzeichnis Index
Übersicht Verzeichnisse
Inhaltsverzeichnis

Code per VBA übertragen - bin verzweilfelt!

Code per VBA übertragen - bin verzweilfelt!
20.12.2007 14:50:00
Kolja
Hallo Zusammen,
dies ist mein erster Beitrag und ich möchte mich kurz vorstellen. Ich heiße Kolja Ludwig und bin z.Zt. studentischer Mitarbeiter bei einer Berliner Beratungsfima im Automobilsektor.
Ich arbeite dort gerade an einm Excel-basiertem Tool zur Verwaltung einer Menge von bestimmten, gleichartigen Excel-Dokumenten. Eine Funktion dieses Tools ist es, ein solches Dokument auf den Stand eines ständig gepflegten Templates zu bringen. Dies umfasst alle Arbeitsblätter der Datei sowie den zugehörigen Makrocode. Im Template und den besagten Dateien befinden sich dabei Module aller Typen (ct_MSForm, ct_Document, ct_StdModule usw.).
Diese Update-Funktion funktioniert im Prinzip hervorragend, bis zu dem Punkt, an dem zum Schluss der Aktualisierungs-Routine der VBA-Code vom Template in die jeweils zu aktualisierende Datei übertragen werden soll. Der entsprechende Code läuft innerhalb des besagten Tools, weitere geöffnete Dateien sind das Template und die zu akt. Datei.
Das Problem liegt nun darin, daß Excel IMMER komplett abstürzt, wenn der Code in unten aufgelisteter Funktion in das jweilige Modul der Ziel-Datei eingefügt wird.
Ich habe (so denke ich) ALLE Möglichkeiten der Code-Übertragung ausprobiert: Import/Export über die eingebauten Methoden, mit .AddFromFile und .AddFromString, Zeilenweise, zeichenweise, und prozedurweise Übertragung. Dies auch jeweils mit/ohne aktivierten Events und noch etliche weitere Kombinationen. Nichts funktioniert.
Die unten stehende Funktion nutzt String-basierte Übertragung des Codes. Dies habe ich so gemacht, da bei der Nutzung der Export/Import-Funktionen vorher alle Module entfernt werden sollten (nat. mit Ausnahme der ct_Document-Module). Ein Modul und eine UserForm blieben dabei immer übrig, was einen Fehler beim Import der entsprechenden Module des Templates zur Folge hatte.
Hat jemand eine Idee, woran das Einfügen des Codes scheitert?
Noch kurz zum Verständnis: Da in dem Tool ständig mit mehreren offenen, gleichartigen Dateien hantiert wird, habe ich eine Klasse geschrieben, die eine solche Datei repräsentiert und einige immer wiederkehrende Operationen auf diesen Dateien kapselt (z.B. Arbeitsblätter sperren, Zeilen in eine Liste enfügen usw.). Dies ist der Typ 'Masterlist', welcher in der Signatur zu sehen ist.
Hier nun die Funktion:

Public Function transferVBACode(source As Masterlist, dest As Masterlist) As Boolean
Dim returnValue As Boolean
Dim FName As String
Dim VBComp As VBIDE.VBComponent
Dim VBCompInDest As VBIDE.VBComponent
Dim VBComps As VBIDE.VBComponents
Dim ws As Worksheet
Dim wsInDest As Worksheet
Dim wsName As String
Dim sfx As String
Dim restoreScreenUpdating As Boolean
Dim restoreEvents As Boolean
Dim destWorkbook As Workbook
Dim sourceWorkbook As Workbook
Dim destVBComponents As VBComponents
Dim sourceVBComponents As VBComponents
Dim sourceFolderPath As String
Dim currentComp_Name As String
Dim currentComp_Type As Integer
Dim currentComp_Code As String
Dim newComp As VBComponent
Dim comp_match As Boolean
Dim search_ws_ml As Boolean
Dim i As Integer
If Application.ScreenUpdating = False Then
Application.ScreenUpdating = True
restoreScreenUpdating = True
End If
If Application.EnableEvents = True Then
Application.EnableEvents = False
restoreEvents = True
End If
' Projekte wenn nötig entsperren
If source.VBAProjectProtected = True Then
returnValue = source.UnlockVBAProject
If returnValue = False Then
transferVBACode = False
Exit Function
End If
End If
If dest.VBAProjectProtected = True Then
returnValue = dest.UnlockVBAProject
If returnValue = False Then
transferVBACode = False
Exit Function
End If
End If
' Workbook- und Component-Objekte initialisieren
Set destWorkbook = dest.GetMLWorkbook
Set sourceWorkbook = source.GetMLWorkbook
Set destVBComponents = destWorkbook.VBProject.VBComponents
Set sourceVBComponents = sourceWorkbook.VBProject.VBComponents
' ### Code der Zieldatei löschen ###
For Each VBComp In destVBComponents
With VBComp.CodeModule
.DeleteLines 1, .CountOfLines
End With
Next VBComp
' ### Module kopieren ###
' Hier kommt es darauf an, den Code einer Komponente des Templates der richtigen
' Komponente der Zieldatei zuzuordnen. Diese Zuordnung kann eindeutig geschehen, wenn
' der Name und der Typ beider Komponenten übereinstimmen.
' Wird keine Entsprechung in der Zieltabelle gefunden, wird eine Komponente mit diesem
' Namen und Typ generiert und der Code dort eingefügt.
For Each VBComp In sourceVBComponents
' Namen (Worksheet-Name), Typ und Code der aktuellen Komponente des Templates auslesen
currentComp_Type = VBComp.Type
currentComp_Code = VBComp.CodeModule.Lines(1, VBComp.CodeModule.CountOfLines)
If currentComp_Type  vbext_ct_MSForm And VBComp.Name  "DieseArbeitsmappe" Then
currentComp_Name = VBComp.Properties.Item("Name")
Else
currentComp_Name = VBComp.Name
End If
' Flag setzen für den Fall, daß die Komponente des Masterlisten-Arbeitsblattes
' gesucht werden soll. Dises Arbeitsblatt ist u. U. in den beiden Dateien
' unterschiedlich benamt.
If currentComp_Name = source.GetTableName Then
search_ws_ml = True
Else
search_ws_ml = False
End If
comp_match = False
' Jeweils Komponenten der Zieldatei durchlaufen und schauen, ob dort eine
' Komponente gleichen Names und Typs vorhanden ist
For Each VBCompInDest In destVBComponents
If VBCompInDest.Type = currentComp_Type Then
' Die Namen der Komponenten sind nicht bei jedem
' Modultyp aus der gleichen Eigenschaft auslesbar:
If currentComp_Type = vbext_ct_MSForm Or currentComp_Name = "DieseArbeitsmappe"  _
_
Then
If VBCompInDest.Name = VBComp.Name Then
comp_match = True
End If
Else
If VBCompInDest.Properties.Item("Name") = currentComp_Name Or _
(search_ws_ml And VBCompInDest.Properties.Item("Name") = dest. _
GetTableName) Then
comp_match = True
End If
End If
End If
If comp_match Then
' Code einfügen (hierbei stürzt Excel komplett ab)
VBCompInDest.CodeModule.AddFromString String:=currentComp_Name
Exit For
End If
Next VBCompInDest
' Komponente ist in Zieldatei (noch) nicht vorhanden
If Not comp_match Then
Select Case VBComp.Type
' Für Klassen-Module und Standardmodule:
Case vbext_ct_StdModule, vbext_ct_ClassModule
' Neue Komponente des richtigen Typs erstellen
Set newComp = destVBComponents.Add(VBComp.Type)
' Code einfügen
newComp.CodeModule.AddFromString String:="Option Explicit"
' Nun müssen noch alle Eigenschafts-Werte der Komponente übertragen werden
'   For i = 1 To VBComp.Properties.Count
'       newComp.Properties.Item(i) = VBComp.Properties.Item(i)
'   Next i
' Für UserForms:
Case vbext_ct_MSForm
' Noch nicht vorhandene Formulare lassen sich
' mit angemessenen Aufwand nur durch Exportieren/Importieren
' in die Zieldatei übertragen
FName = sourceFolderPath & "\" & VBComp.Name & ".frm"
VBComp.Export FName
destVBComponents.Import FName
Kill FName
Kill sourceFolderPath & "\" & VBComp.Name & ".frx"
Case vbext_ct_Document
End Select
End If
Next VBComp     ' nächste Komponente
' Operation war erfolgreich, wenn wir bis hierher gekommen sind
transferVBACode = True
If restoreScreenUpdating Then
Application.ScreenUpdating = False
End If
If restoreEvents Then
Application.EnableEvents = True
End If
' Das aktive Projekt soll nun wieder dieses sein
Set Application.VBE.ActiveVBProject = ThisWorkbook.VBProject
Application.VBE.MainWindow.Visible = False
' Objekte freigeben
Set destWorkbook = Nothing
Set sourceWorkbook = Nothing
Set destVBComponents = Nothing
Set sourceVBComponents = Nothing
Set VBCompInDest = Nothing
Set newComp = Nothing
Set VBComp = Nothing
Set VBComps = Nothing
Set ws = Nothing
Set wsInDest = Nothing
End Function


Vielen Dank schon mal für Eure Hilfe!
Gruß,
Kolja

5
Beiträge zum Forumthread
Beiträge zu diesem Forumthread

Betreff
Datum
Anwender
Anzeige
AW: Code per VBA übertragen - bin verzweilfelt!
20.12.2007 16:54:27
Alf
Hallo Kolja
Ich hab es mal bei mir getestet, und es läuft ohne Absturz.
Ich hab den Typ der Argumente von "Masterlist" auf "Workbook" geändert. Entsprechend habe ich die Codezeilen, die "Masterlist"-Eigenschaften beinhalten, deaktiviert.
Somit ist insbesondere die Protection-Problematik ausgeklammert, was evtl. schuld sein könnte.
Ich habe drei Punkte zu erwähnen, die aber offensichtlich nichts mit dem Absturz zu tun haben:
(A) Änderung der Bedingung
If currentComp_Type = vbext_ct_MSForm Or currentComp_Name = "DieseArbeitsmappe" Then
ändern in:
If currentComp_Type = vbext_ct_MSForm Or currentComp_Type = vbext_ct_Document Then
Damit sind auch die Tabellen-Komponenten eingeschlossen. Denn bei diesen versagt bei mir der Zugriff auf
die Name-Eigenschaft: VBCompInDest.Properties.Item("Name") (Fehlermeldung)
(B) Einsatz von currentComp
VBCompInDest.CodeModule.AddFromString String:=currentComp_Name
ändern in:
VBCompInDest.CodeModule.AddFromString String:=currentComp_Code
(das war vermutlich ein Vertipper)
(C) Option Explicit
Unter den Optionen im VBA-Editor kann eingestellt werden, dass bei der Generierung
einer neuen Komponente "Option Explicit" automatisch eingefügt wird. Bei Anwendung
des vorliegenden Codes kann somit "Option Explicit" schliesslich doppelt vorhanden sein, was
einen Syntaxfehler hervorruft.
Gruss Alf

Anzeige
AW: Code per VBA übertragen - bin verzweilfelt!
21.12.2007 11:13:00
Kolja
Hallo Alf,
danke zunächst für deine Antwort. Du schreibst:

Somit ist insbesondere die Protection-Problematik ausgeklammert, was evtl. schuld sein könnte.


Das verstehe ich nicht so ganz. Meinst Du den Schutz des VBA-Projekts? Dieser wird in diesem Teil ausgeschaltet:
If dest.VBAProjectProtected = True Then
returnValue = dest.UnlockVBAProject
If returnValue = False Then
transferVBACode = False
Exit Function
End If
End If
Dies funktioniert ja leider nur per SendKeys, was ich für sehr fehleranfällig halte. Ich habe lange dort das Problem gesucht, aber es funktioniert eben. Warum gibt es da nur keine eigene Methode für ? - Na ja, Microsoft eben :-(
Meine Hauptvermutung bzgl. des Absturzes geht in die Richtung, daß der VBE intern fehlerhaft ist. Anders lässt sich meiner Meinung nach nicht erklären, warum VBComponent.Remove nicht bei allen Modulen funktioniert und das Einfügen per .AddFromString zwar mit einer oder wenigen Zeilen funktioniert, der Editor aber aussteigt, wenn der gesamte Code eines Moduls eingetragen wird. Zumal er erst nach dem Einfügen aussteigt.
Zu deinen Anmerkungen:
(A) :


If currentComp_Type = vbext_ct_MSForm Or currentComp_Name = "DieseArbeitsmappe" Then
ändern in:
If currentComp_Type = vbext_ct_MSForm Or currentComp_Type = vbext_ct_Document Then
Damit sind auch die Tabellen-Komponenten eingeschlossen. Denn bei diesen versagt bei mir der  _
Zugriff auf
die Name-Eigenschaft: VBCompInDest.Properties.Item("Name") (Fehlermeldung)


Ich weiß nicht, ob Du an der Stelle etwas verändert hast, aber der Code holt sich den Namen nur über die .Properties-Eigenschaft, wenn das Modul keine UserForm ist. Bei den anderen Modultypen funktioniert es. Das Modul "DieseArbeitsmappe" scheint ein Sonderfall zu sein, da .Properties.Item("Name") hier nicht "DieseArbeitsmappe" liefert, sondern den Dateinamen der Arbeitsmappe. Hier muß zur identifizierung des Moduls auch die .Name-Eigenschaft benutzt werden. So, wie ich den Code gepostet habe, funktioniert das auch alles wunderbar.
Ich habe zum identifizieren des richtigen (Ziel-) Moduls dieses etwas komplizierte Konstrukt gewählt, da die Kombination CodeName/Name der einzelnen Arbeitsblattmodule im Template und in der Zieldatei nicht unbedingt immer identisch sein müssen.
(B): Ja, das war ein Vertipper. Das habe ich kurz nach meinem Posting auch geändert. Allerdings ist Excel beim Einfügen des Modulnamens in das CodeModule auch nicht ausgestiegen, sondern es kam später ein normaler Laufzeitfehler, als ein Makro ausgeführt werden sollte. Dies zeigt, daß mein Code prinzipiell richtig ist und funktioniert. Weißt Du, ob VB eingefügten Code gleich nach dem Einfügen kompiliert oder irgendwie evaluiert?
(C) Das müsste ich tatsächlich prüfen. Dies ist aber nur bei neu erstellten Modulen relevant, die restlichen werden ja zuvor "leergeräumt".
Ich werde nun mal probieren, ob der Zugriff, wie Du es probiert hast, ohne meine "vorgeschaltete" Klasse funktioniert. Die 'Workbook'-Objektreferenzen kommen ja auch aus dem jeweiligen 'Masterlist' -Objekt. Vielleicht hat VBA hier ja Probleme mit den Objektverweisen.
Ich melde mich dann wieder. Vielen Dank soweit für deine Hilfe.
Gruß,
Kolja

Anzeige
AW: Code per VBA übertragen - bin verzweilfelt!
22.12.2007 17:46:45
Alf
Hallo
Hier einige Tipps & Antworten:
Dies funktioniert ja leider nur per SendKeys, was ich für sehr fehleranfällig halte.
Ja, diese Ansicht teile ich mit dir! Mit Sendkeys kann man Aktionen auslösen, die einen anderen Thread
starten. Dann muss im Makro über ein Timer-Event in kurzen Zeitabständen zunächst geprüft werden, ober der gewünschte Zustand (hier: VBProject-Schutz aufgehoben) eingetreten ist, bevor die nächsten Schritte eingeleitet werden können...
Meine Hauptvermutung bzgl. des Absturzes geht in die Richtung, daß der VBE intern fehlerhaft ist. .
Ja, diese Fehler gibt es. Einige davon sind gutmütig und verursachen keinen Absturz. Die Fehlermeldung "Konstanter Ausdruck erforderlich" etwa, wie ich sie beim Arbeiten mit Enum-Konstanten kennengelernt habe.
Ein Laufzeitfehler tritt manchmal auf, wenn man mit Copy/Paste manuell im Deklarationsteil operiert. Ich habe mir jedoch nicht die Mühe gemacht, ihn zu reproduzieren. Ein Laufzeitfehler infolge automatischen Zugriffs ist bei mir allerding noch nie vorgekommen.
Anders lässt sich meiner Meinung nach nicht erklären, warum VBComponent.Remove nicht bei allen Modulen funktioniert.
Komponenten des Typs vbext_ct_Document (Arbeitsblatt-Modul und Tabellenmodule) können mit VBComponent.Remove nicht entfernt werden!
Ich habe zum identifizieren des richtigen (Ziel-) Moduls dieses etwas komplizierte Konstrukt gewählt, da die Kombination CodeName/Name der einzelnen Arbeitsblattmodule im Template und in der Zieldatei nicht unbedingt immer identisch sein müssen.
Streng genommen genügt das aber nicht. Denn in einem Tabellenmodul stehen gewöhlich Ereignis-Routinen der Tabelle oder von Schaltflächen, die über die Steuerelemente-Toolbox eingefügt worden sind. Also müsstest du konsequenter auch überprüfen, ob die eingefügten Ereignis-Routinen den Schaltflächen entsprechen...
Code, der nicht tabellenspezifisch ist, gehört in ein Standard- oder Klassenmodul! Wenn du Ereignisse von von mehreren Tabellen abfangen willst, erstelle ein separate Klasse als Eventhandler!
So behaupte ich mal, dass du für deine Aufgabe die Tabellen-Module ignorieren kannst.
Allerdings ist Excel beim Einfügen des Modulnamens in das CodeModule auch nicht ausgestiegen, sondern es kam später ein normaler Laufzeitfehler, als ein Makro ausgeführt werden sollte
Das ist eine wichtige Information! Schau weiter:
Weißt Du, ob VB eingefügten Code gleich nach dem Einfügen kompiliert oder irgendwie evaluiert?
Das ist mir auch nicht so ganz klar.
Versuch daher mal folgendes:
1. Gehe in den kopierten, neuen Code
2. Produzier einen Syntaxfehler
3. Lass die VBE den Code prüfen. Es wird auf den Fehler hingewiesen.
4. Mach den Syntaxfehler rückgängig.
5. Lass die VBE den Code neu prüfen (jetzt wird offensichtlich vollständig kompiliert bzw. evaluiert)
6. Starte das Makro
Es würde mich interessieren, ob dein Laufzeitfehler dann immer noch auftritt.
Dieses Vorgehen hat sich bei mir in gewissen Fällen bewährt.
Beste Grüsse
Alf

Anzeige
AW: Code per VBA übertragen - bin verzweilfelt!
20.12.2007 20:47:21
Yal
Hallo Kolja,
ich habe leider um diese Uhrzeit nicht mehr die Nerven, mich den Code in seine Feinheit anzuschauen. Sieht aber gut aus. Alles von Dir? Dann sollte die Firma schon überlegen, wie es mit Dir nach deinem Studium weitergehen soll! ;-)
Ich hatte ein ähnliche Problem als ich noch bei einer Beratungsfirma war (Der Kunde war auch im Automobilsektor): zig tausend Excel-Datei, die die gleiche Funktionen gebraucht hätten.
Ich habe Sie alle in einem xla (Excel-AddIn) gepackt und die Anwender per Mail "geschult", wie Sie diese AddIn dauerhaft in Excel anbinden könnten. Da der xla auf einem Netzlaufwerk war, und ich extra angewiesen hatte, keine lokale Kopie zu machen, war die Verteilung einer neue Version nur das Ersetzen des xla durch die neue Version.
Wenn das Dich helfen kann...
Viele Grüße
Yal

Anzeige
AW: Code per VBA übertragen - bin verzweilfelt!
21.12.2007 10:37:07
Kolja
Hallo Yal,
danke für dein Lob. Bewerbung läuft zur Zeit :-).
Auf deine Idee mnit dem Add-In bin ich noch nicht gekommen, damit habe ich mnich noch gar nicht beschäftigt. Kann ein Add-In den gesamten Code eines Projekts ersetzen, insbesondere den Code in den Arbeitsblatt-Modulen? Wenn ja, wäre das tatsächlich eine Möglichkeit. Das Problem ist, daß sich auch die Tabellenblätter öfter mal ändern, welche in allen im Umlauf befindlichen Dateien Anwendung finden müssen. Aber die Idee ist schon bestechend, nur ein neues Add-In online zu stellen. Danke für den Tip.
Gruß,
Kolja

307 Forumthreads zu ähnlichen Themen

Anzeige
Anzeige

Links zu Excel-Dialogen

Beliebteste Forumthreads (12 Monate)

Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige
Anzeige
Anzeige