VisualBasic dll Klasse einbinden welche noch verändert werden kann wenn Programm offen ist

R O G E R

Vice Admiral
Registriert
März 2008
Beiträge
6.496
Hallo zusammen,

ich habe folgendes Problem:

Ich habe ein Visual Basic Programm geschrieben in dem ich eine dll Klasse einbinden möchte.
Unter Verweise habe ich diese hinzugefügt.
Das Funktioniert auch alles.
Dieses Programm wird auf mehreren Rechnern ausgeführt und wird laufen gelassen.
Dann möchte ich Änderungen in der dll machen, die beim nächsten Programmstart dann wirksam werden sollen.
Doch wenn ich die dll durch die neue dll ersetzten möchte, welche gerade von den Rechnern geöffnet ist, kann ich diese nicht ersetzten, da diese dll natürlich gerade von einem anderen Rechner in Benutzung ist.

Wie kann ich dieses Problem am besten lösen?
 
Schwierig bis kaum möglich ohne die Anwendung zu schließen. Muss die Anwendung zwingend laufen? Immer?

Ich habe es bei uns mit einem Updater gelöst. Der Updater informiert über Updates und schließt nach Bestätigung durch den Benutzer alle firmeneigenen Anwendungen. Dann aktualisiert er die Dateien und startet die zuvor geöffneten Anwendungen neu.
 
Das ist das Problem.
Die Anwendung wird von mehreren Benutzer genutzt und laufen gelassen. Und bei Aktualisierungen müssten alle diese Anwendung schließen.
Und das ist ja beinnah unmöglich.
Kann man nicht irgenwie eine gecachte dll einbinden?
Das beim Start eines Programms die dll gecacht wird und die eigentliche dll wieder freigegeben wird?
 
Wird nicht funktionieren, gibt auch keinen Workaround. Einzige Möglichkeit: Alle müssen die Anwendung beenden. Alternativ eben einen Auto-Updater, aber damit der geht, müssen alle die Anwendungen auch mal geschlossen haben, sonst wird der neue Code oder (sofern extern) Updater nicht gestartet.
 
Mein Updater verteilt die Dateien direkt beim Rechnerstart bzw. wenn ein neues Update verfügbar ist und der Benutzer es bestätigt.

Gibt es einen Fehler, wird der Benutzer das Update freiwillig einspielen, damit er weiterarbeiten kann. Ist das Update ein NiceToHave-Feature, hat es auch Zeit bis zum nächsten Tag.


Um den Benutzern das Update zu erleichtern, könntest du den aktuellen Status deiner Anwendung zwischenspeichern, damit sie an der selben Stelle weiterarbeiten können wenn das Update eingespielt ist.

Es ist ein Klick und je nach Updategröße nur ein Augenblick Wartezeit. Danach kann der Benutzer direkt weiterarbeiten.

Die Vorgehensweise klappt bei uns recht gut.

Andere Möglichkeiten sehe ich da nicht.
 
Danke für eure Infos.
Das mit dem Updater ist leider bei mir in dem Fall so realisierbar :(
Da muss ich mir was anderes Einfallen lassen.
 
Hallo,

ich hatte ein ähnliches Problem, ich wollte eine DLL als Resource in einem Programm einbinden,
gelöst habe ich es mit diesem Code, er erzeugt im Temp-Verzeichnis ein Unterverzeichnis und schreibt die DLL dort hin und lädt sie dann per Kernel32->LoadLibrary.
Du musst den Code nur so modifizieren das die DLL eben aus dem Programm-Pfad gelesen und dann in Temp geschrieben wird, dann hat jede der lokalen Programme eine eigene lokale Kopie der DLL, so das dass Orginal jederzeit überschrieben werden kann.
Der Code ist C#, aber sollte in VisualBasic sehr ähnlich aussehen.
Code:
...
   LoadLib("BASSMOD.dll", global::Intro.Properties.Resources.BASSMOD);
...

        [DllImport("kernel32", EntryPoint = "LoadLibrary", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern IntPtr Kernel32LoadLibrary(string lpFileName);

        private static void LoadLib(String strDLLName, byte[] data) {
            AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();
            String dirName = Path.Combine(Path.GetTempPath(), assemblyName.Name + assemblyName.Version.ToString());
            if (!Directory.Exists(dirName))
                Directory.CreateDirectory(dirName);
            String dllPath = Path.Combine(dirName, strDLLName);

            using (Stream stm = new MemoryStream(data)) {
                try {
                    using (Stream outFile = File.Create(dllPath)) {
                        const int sz = 4096;
                        byte[] buf = new byte[sz];
                        while (true) {
                            int nRead = stm.Read(buf, 0, sz);
                            if (nRead < 1)
                                break;
                            outFile.Write(buf, 0, nRead);
                        }
                    }
                }
                catch {
                }
            }

            IntPtr h = Kernel32LoadLibrary(dllPath);
            Debug.Assert(h != IntPtr.Zero, "Unable to load library " + dllPath);
        }
 
Das ändert aber auch nichts. Die von der Anwendung geladene DLL wird trotzdem nicht zur Laufzeit geändert.
 
SomeDifferent schrieb:
Das ändert aber auch nichts. Die von der Anwendung geladene DLL wird trotzdem nicht zur Laufzeit geändert.
Das nicht, sein Problem war ja das er das Orginal nie überschreiben kann, wenn das aktualisieren auch noch automatisch passieren soll kann er z.B einen Datei "NeuXXX.dll" erzeugen und alle laufenden Instanzen prüfen den Programm-Pfad z.B jede Minute ob die Datei "NeuXXX.dll" existiert, in dem Fall wird das Programm neugestartet (und dann die erste Prüfung ob eine "NeuXXX.dll" existiert erst nach 10 Minuten anfangen) oder per Kernel32.FreeLibrary freigeben und dann wieder LoadLib - und nach erfolgtem Neuladen sollte das Automatisch prüfen für 10 Minuten oder so ausgesetzt werden, damit das aktualisieren nicht mehrfach passiert.
Dann muss man nach der Akualisierung von der XXX.dll nur die Datei "NeuXXX.dll" erstellen, 2 Minuten warten und die "NeuXXX.dll" wieder löschen.
 
Danke für die Antworten :)
das mit dem LoadLib habe ich eben auch schon gefunden.
Werde es ausprobieren.

Also die dlls und exe sind nicht sehr groß. Da diese eigentlich nur Unterprogramme mit bestimmten Parametern ausführen und die Daten eh aus einer Datenbank kommen.

Also mein aktuelles Workaround ist:
exe + dll auf den Windows Tempordner kopieren und dann die exe ausführen.
So kann ich auch die exe ändern wenn die von anderen Benutzern ausgeführt werden :)
Das habe ich mit ner Mini Konsolenanwendung gemacht, ich wieß ne Batch hätte auch gereicht ;)
 
lynxx schrieb:
Hallo,

ich hatte ein ähnliches Problem, ich wollte eine DLL als Resource in einem Programm einbinden,
gelöst habe ich es mit diesem Code, er erzeugt im Temp-Verzeichnis ein Unterverzeichnis und schreibt die DLL dort hin und lädt sie dann per Kernel32->LoadLibrary.
Du musst den Code nur so modifizieren das die DLL eben aus dem Programm-Pfad gelesen und dann in Temp geschrieben wird, dann hat jede der lokalen Programme eine eigene lokale Kopie der DLL, so das dass Orginal jederzeit überschrieben werden kann.
Der Code ist C#, aber sollte in VisualBasic sehr ähnlich aussehen.
Code:
...
   LoadLib("BASSMOD.dll", global::Intro.Properties.Resources.BASSMOD);
...

        [DllImport("kernel32", EntryPoint = "LoadLibrary", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern IntPtr Kernel32LoadLibrary(string lpFileName);

        private static void LoadLib(String strDLLName, byte[] data) {
            AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();
            String dirName = Path.Combine(Path.GetTempPath(), assemblyName.Name + assemblyName.Version.ToString());
            if (!Directory.Exists(dirName))
                Directory.CreateDirectory(dirName);
            String dllPath = Path.Combine(dirName, strDLLName);

            using (Stream stm = new MemoryStream(data)) {
                try {
                    using (Stream outFile = File.Create(dllPath)) {
                        const int sz = 4096;
                        byte[] buf = new byte[sz];
                        while (true) {
                            int nRead = stm.Read(buf, 0, sz);
                            if (nRead < 1)
                                break;
                            outFile.Write(buf, 0, nRead);
                        }
                    }
                }
                catch {
                }
            }

            IntPtr h = Kernel32LoadLibrary(dllPath);
            Debug.Assert(h != IntPtr.Zero, "Unable to load library " + dllPath);
        }

WTF? Wieso kopierst du die DLL so umständlich?
 
lynxx schrieb:
sein Problem war ja das er das Orginal nie überschreiben kann
Ups, ja das Ur-Problem hatte ich schon wieder verdrängt :D
 
Die Update-Prüfung kann man noch vereinfachen, das Programm merkt sich nach dem kopieren einfach das Änderungsdatum/Uhrzeit der Source-DLL und prüft ob sich das ändert alle X Minuten. :)
 
Ein anderer Ansatz wäre, dass Du deine DLL-Versionen durchnummerierst. Beim Programmstart Überprüfst Du die vorhandenen DLLs im Programmpfad und lädst mit LoadLibrary die mit der höchsten Nummer.
Aktuell ist dann z.B. die MeineDLL005.dll am Laufen, Du erstellst eine neue Version unter dem Namen MeineDLL006.dll - der nächste der das Programm neu startet lädt dann schon die 006er DLL.
Vielleicht ist es sogar möglich einenAutomatismus ins Programm einzubauen, der in regelmäßigen Abständen prüft, ob es eine neue Version der DLL gibt, wenn ja die alte Version frei gibt und die neue Verion lädt. Dann bräuchte es nicht mal einen Neustart.
 
Zuletzt bearbeitet: (Typo)
Der einfachste Weg, sowas zu realisieren, ist, die bewußten Funktionen(-gruppen) in Module auszulagern, die zur Laufzeit nicht geladen, sondern ausgeführt werden (in einem neuen Thread). Allerdings muß man dazu ggf. sein Layout komplett überarbeiten, denn die Module müssen notwendigerweise autonom sein.

Code, der zur Laufzeit *benötigt* wird, kann nicht zur Laufzeit erneuert werden. Das wäre so, als würde man sein Haus abreißen und neu bauen, während man drein fernsieht.

Wenn man aber weiß, daß ein Modul grad nicht gebraucht wird, und/oder man dafür sorgen kann, daß es nicht gebraucht wird ("bitte schließen Sie zunächst das Dokument" oder "bitte stellen Sie sicher, daß XYZ erledigt ist, bevor Sie weitermachen") dann kann man es auch zur Laufzeit ersetzen. Geladen ist es nicht (da es bei Bedarf *ausgeführt* wird), aber man braucht natürlich Schreibrechte.

Wenn das eine Serverkomponente ist, auf die von verschiedener Stelle gleichzeitig zugegriffen wird, muß man davon ausgehen, daß sämtliche Module zu jeder Zeit geladen sind. In dem Fall *könnte* man über eine zur Laufzeit änderbare Konfigdatei angeben, welches Modul (.dll) für welche Funktionsgruppe zuständig ist, und dann dafür sorgen, daß für jeden Modulstart diese Zuordnung aus der Konfigdatei geholt und angewendet wird.

Haken daran ist aber, daß man dann zusätzlichen Aufwand betreiben muß - zB ein standardisiertes Interface definieren muß -- um zu erfahren, wer denn jetzt welche Version eines Moduls tatsächlich ausführt. Wenn es irgendwo hakt, weiß man sonst ggf. nicht, wo denn nun der Fehler zu suchen ist, weil fünf Modulversionen im Arbeitsspeicher stehen (für fünf Anwender jeweils eine) und es bei vieren tut und beim fünften aber nicht.
 
Eine AppDomain erstellen, DLL-Datei dazuladen und die Aktionen entsprechend ausführen. Wenn es ein Update gibt, die AppDomain entladen und neue DLL-Datei holen.
 
Zurück
Oben