Hi,
ich will eine in VB6 erstellte DLL in C# benutzen. In VB6 habe ich dafür
eine ActiveXDLL erstellt und diese mittels TlbImp-Tool exportiert. Füge
ich die DLL als Verweis ein, kann ich ganz regulär auf den Namespace
zugreifen und meine Funktionen wie gewohnt nutzen.
Problem an dieser Methode ist allerdings, das die in der DLL aufgerufene
Funktion, abstürzen kann. In C# reagiere ich mittels eines Watchdogs
darauf, beende die Berechnung und zeige eine entsprechende Meldung an.
Will ich jedoch anschließend eine neue Berechnung ausführen, bekomme ich
nurnoch Timeouts und sehe die entsprechende Fehlermeldung. Das Problem
liegt darin, das das "killen", den Zustand der DLL nicht sauber beendet.
Ich brauche daher ein Verfahren, was die DLL komplett aus dem Speicher
löscht und für die nächste Berechnung neu lädt. Ich dachte, ich könnte
das Problem durch das dynamische Laden der DLL lösen. Dafür habe ich
eine AppDomain (newDomainName) erstellt, und darin über
newDomainName.Load("KMDLLCSHARP") die DLL zur Laufzeit geladen. In einem
finally{} Block entlade ich die Domain am Ende. Aber auch dieses
Verfahren schlägt fehl, da auch dabei die DLL nicht wirklich aus dem
Speicher entfernt wird.
Jetzt habe ich noch einen Ansatz über den Import mittels
"LoadLibrary"-Aufrufes aus der Kernel32.dll gefunden. Das Beispiel
schlägt dafür vor, das ganze zunächst in eine Klasse zu packen.
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule,
string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
Anschließend soll man die Sachen importieren können:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int info(ref string infostring);
...
IntPtr pDll = NativeMethods.LoadLibrary("KMDLLCSHARP");
IntPtr pAdressOfFunctionToCall = NativeMethods.GetProcAddress(pDll,
"info");
info infofunction =
(info)Marshal.GetDelegateForFunctionPointer(pAdressOfFunctionToCall,
typeof(info));
Das funktioniert so allerdings nicht. Ich sehe, das pDLL korrekt geladen
wird (der wert ist zumindest ungleich 0), GetProcAddress liefert
allerdings stets 0 zurück. Nun weiß ich allerdings nicht genau, warum.
Gebe ich den Namen der Funktion falsch an oder ist die CallingConvention
in VB6 falsch (kp. wie die genau ist, oder wie ich darauf Einfluss
nehmen könnte).
Weiß da wer weiter?
Der richtige Ansatz wäre, Deine VB6-DLL so zu korrigieren, dass sie nach
einem Absturz nicht in einem Zustand bleibt, so dass sie nicht mehr
verwendbar ist. Das Problem von P-Invoke (DllImport, ...) ist nämlich,
dass eine einmal geladene DLL solange im Speicher bleibt, bis das
aufrufende Programm beendet wird. Da gibt es NICHTS dran zu drehen. Nimm
also mal den VS-Debugger zur Hand und untersuche, warum Deine VB6-DLL
abstürzt, so dass sie nicht mehr funktioniert.
Wenn Du unbedingt mit der Holzhammer-Methode Programmierfehler umgehen
willst, dann gibt es noch eine Möglichkeit, native DLLs über LoadLibrary
und FreeLibrary zu laden und entladen. Wie das richtig geht, kannst Du
diesem englischsprachigen Artikel entnehmen:
http://www.codeproject.com/Articles/258270/How-to-release-non-managed-library-loaded-into-man.
Allerdings ist das Einbinden der DLL-Funktionen nicht ganz so
komfortabel, wie mit P-Invoke und mit vergleichsweise viel manuell zu
erstellendem Code verbunden.
Gruß
Markus
Hey, danke schonmal für die Hilfe.
Prinzipiell wäre mir ein Programm, das nicht oder nur "sauber" abstürzt,
lieber. Das Programm wird allerdings nicht in unserer Firma erstellt,
sondern extern zugekauft. Einflussnahme habe ich so nicht direkt. Ich
kann zwar einzelne Fallstricke (es geht um eine iterative Berechnung)
aufzeigen, die behoben werden, an die implementierung eines Watchdogs
oder ähnlichen Sicherheitsmaßnahmen, ist aber leider nicht zu denken.
Daher der Bedarf an die Holzhammermethode!
So, habe mittlerweile herausgefunden, das meine Funktionen lediglich als
"COM Method" exportiert wurden. Habe das ganze nun nochmal nach
folgender Anleitung umgestellt:
http://www.hermetic.ch/vbm2dll.htm#petrusha
Meine Funktionen werden nun korrekt als "Exported Function" exportiert,
ein aufruf funktioniert prinzipiell. Verzweige ich aber nun innerhalb
der Funktionen weiter, (also auf die ursprünglichen Funktionen, die ich
mit der ursprünglichen Methode aufrufen konnte), erscheint lediglich
folgende Exception:
"Attempted to read or write protected memory. This is often an
indication that other memory is corrupt."
Ich habe nun allerdings keine Ahnung, wie ich das umgehen koennte?
Hi Marcel,
ich hatte den Eindruck, die VB6 DLL wäre von Dir...
Ok. VB (6, .NET) muss ich leider passen. Die Exception riecht aber
irgendwie nach korruptem Stack. Das wiederum ist ein Indiz dafür, dass
beim DllImport Attribut das Property CallingConvention auf den richtigen
wert gesetzt werden sollte. Welcher das ist, kann ich leider nicht
sagen. Wahrscheinlich hilft dir aber eine Internetsuche weiter. Die
Calling Convention gibt an, wer den Stack nach einem Funktionsaufruf
aufräumt (Aufrufer oder Aufgerufene Funktion).
Gruß
Markus
Hi,
mit den Calling Conventions habe ich bereits rumgespielt. Prinzipiell
funktioniert ja auch der Aufruf, aber nicht die weiterführung. D.h. wenn
das Programm innerhalb der Funktion bleibt, die ich aufrufe, klappt es.
(Ich kann Rückgabe und Übergabewerte regulär benutzen.) Nur der Aufruf
einer weiteren Funktion/Sub führt zum Absturz/Wurf der Exception.
Irgendwie sehr dubios.
Ich denke, ich werde mir eine weitere, WrapperDLL schreiben, die den
Aufruf der VB6DLL für mich übernimmt und die ich dynamisch in C#
nachladen kann. Würde das so funktionieren? Irgendwie traue ich dieser
scheinbar einfachen Sache nichtmehr so ganz! =[