Laden...

Erzeugte Klasse von externer Dll wird beim nächsten Aufruf nicht mehr erkannt.

Erstellt von Gerry100 vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.158 Views
G
Gerry100 Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren
Erzeugte Klasse von externer Dll wird beim nächsten Aufruf nicht mehr erkannt.

Hallo

Ich rufe über einen Multimedia Timer Thread einen externe Dll Funktion auf.
Die dll wird dynamisch gepuffert geladen alls byte array. Normalerweise nur einmal und dann wird zyklisch die Funktion aufgerufen.
An die Funktion wird eine Ilist übergeben in dem die Funktion Klassen ablegen kann um sie beim nächsten aufruf wieder verwenden zu können. In diesem fall eine Comport Verbindung.

Dann beende ich denn Timer und starte den Timer neu und ich bekomme eine exeption das die gespeicherte Klasse von einer anderen dll erzeugt wurde! Obwohl die dll gleich geblieben ist. Sie wurde auch nich neu geladen.. Nur der timer hat warscheinlich einen neuen thread gestartet für die neuen cyclischen aufrufe. Danke .was kann ich ändern um das problem zu umgehen

Die

16.807 Beiträge seit 2008
vor 6 Jahren

Es hilft potentiellen Helfern ungemein, wenn Du ein wenig Code zur Verfügung stellst.
Die Wahrheit liegt nun mal im Code und weniger in der Beschreibung, was Du denkst, was Du tust 😉

G
Gerry100 Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Hier der gekürzte Code. Die Timerklasse habe ich als Datei angehängt.

static byte[] loadFile(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Open);
            byte[] buffer = new byte[(int)fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();

            return buffer;
        }

        private void LoadAssembly(string EntryPointName)
        {

            Assembly asm = Assembly.Load(loadFile(@"C:\Users\User\Documents\RoslynPad\SPS.dll"));//LoadFrom(@"C:\Users\User\Documents\RoslynPad\SPS.dll");
            if (asm == null) { throw new Exception("Can't load SPS.Dll"); }

            Type t = asm.GetType("Program");
            if (t == null) { throw new Exception("No Program Class exist in SPS.Dll"); }

            if (EntryPointName == "") { EntryPointName = "EntryPointSPS"; }

            methodInfoStatic = t.GetMethod(EntryPointName);
            if (methodInfoStatic == null) { throw new Exception("No EntryPoint method '" + EntryPointName + "'exist in SPS.Dll"); }

        }

        private void StartTimer(string EntryPointName)
        {

    
                LoadAssembly(EntryPointName);
         

            _SPSTimer = new StyroCut.CNC.Timer();
            _SPSTimer.Started += _SPSTimer_Started;
            _SPSTimer.Stopped += _SPSTimer_Stopped;

            this._SPSTimer.Mode = TimerMode.Periodic;
            this._SPSTimer.Period = Settings.Default.SPSCycleTime;
            this._SPSTimer.Resolution = 5;
            this._SPSTimer.SynchronizingObject = null;
            this._SPSTimer.Tick += new EventHandler(TimerTick);


            this._SPSTimer.Start();

        }

        private void TimerTick(object sender, System.EventArgs e)
        {
            SPSCycle(this._ICNC);
        }

        private void SPSCycle(ICNC CNC)
        {

    
                object[] staticParameters = new object[2];
                staticParameters[0] = (ISPSInt)CNC.SPS;
                staticParameters[1] = CNC;

     
                methodInfoStatic.Invoke(null, staticParameters);




        }

code der externen dll

public static void EntryPointSPS(ISPSInt SPS, ICNC CNC)
{


    try
    {


        SPSModbus Modbus = (SPSModbus)SPS.DataObject.GetOrAdd("Modbus", (a) => new SPSModbus(SPS));

        if (Modbus.connected == false) { Modbus.Connect(115200, 1); }


    //    Modbus.ReadRegister();






                SPS.OutDataInt.SetValue("PINPWM3",0);
                SPS.OutDataInt.SetValue("PINPWM5",0);
                SPS.OutDataInt.SetValue("PINPWM6",0);
                SPS.OutDataInt.SetValue("PINPWM9",0);
                SPS.OutDataInt.SetValue("PINPWM10",0);
                SPS.OutDataInt.SetValue("PINPWM11",0);





   //     Modbus.WriteRegister();
        
    }
    catch (Exception ex)
    {

        SPS.RaiseEventandAbort(new SPSExeption("SPS Main Error", ex));
    }
}

hier die exeption> Fehlermeldung:

[A]SPSModbus kann nicht in **SPSModbus umgewandelt werden. Der Typ "A" stammt von der "SPS, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" im Kontext "LoadNeither" in einem Bytearray.. Der Typ "B" stammt von der "SPS, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" im Kontext "LoadNeither" in einem Bytearray..[/error]

Hinweis von Coffeebean vor 6 Jahren

Bitte benutze die richtigen Code-Tags [Hinweis] Wie poste ich richtig?

121 Beiträge seit 2016
vor 6 Jahren

⚠404 - Codetags nicht gefunden! ⚠

G
Gerry100 Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Ich habe noch rausgefunden das wenn ich das Assembly so lade

Assembly asm = Assembly.LoadFrom(@"C:\Users\user\Documents\RoslynPad\SPS.dll");

es funktioniert.

Ich würde aber gerne das Assembly so laden das ich die Datei zwischen den Timer Zyklen überschreiben kann und das geht so nicht da sie nur einmal in denn Speicher geladen wird.

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

nun - eine Assembly die geladen wurde kann man meines Wissens nicht mehr entladen - die einzige Variante ist die AppDomain komplett zu entladen.

Hier kannst du nachschauen wie das geht: https://stackoverflow.com/questions/4887847/hot-unload-reload-of-a-dll-used-by-an-application

LG

Edit: Meint - für diese Assembly eine eigene AppDomain erstellen - und diese dann eben ggf. entladen. (sofern das hinsichtlich der Performance ok ist)

D
985 Beiträge seit 2014
vor 6 Jahren

Und warum das mit Assembly.LoadFrom funktioniert ist sogar dokumentiert

If an assembly with the same identity is already loaded, LoadFrom returns the loaded assembly even if a different path was specified.

Was macht Assembly.Load anders?

Note that this method overload always creates a new Assembly object with its own mapping.

G
Gerry100 Themenstarter:in
5 Beiträge seit 2017
vor 6 Jahren

Noch eine Frage.

Ich habe dann die Möglichkeit eine Appdomain zu machen mit denn Nachteil das alle Objekte von MarshalByRefObject erben müssen.
Oder kann ich auch die Scripting Api von Roslyn verwenden? Weiß irgendeiner ob ich dort Objecte auf Referenz hin und herschieben kann?

301 Beiträge seit 2009
vor 6 Jahren

https://stackoverflow.com/questions/5728599/when-would-using-assembly-loadfrom-or-assembly-loadfile-be-appropriate

Da sollte eigentlich alles zu dem Thema stehen was du brauchst.

"LoadFile() doesn’t bind through Fusion at all – the loader just goes ahead and loads exactly* what the caller requested. It doesn’t use either the Load or the LoadFrom context."

1.029 Beiträge seit 2010
vor 6 Jahren

@Gerry:
Nein - dein AssemblyLoader soll von MarshalByRefObject erben - der eigentliche Typ muss das nicht.

Könnte dann z.B. so aussehen:


class Program
	{
		static void Main(string[] args)
		{
			for (var i = 0; i < 10; i++)
			{
				var domain = AppDomain.CreateDomain("my-temp-domain", new Evidence(), AppDomain.CurrentDomain.BaseDirectory,
					AppDomain.CurrentDomain.RelativeSearchPath, false);

				var loader = (SimpleAssemblyLoader) domain.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader).Assembly.FullName,
					typeof(SimpleAssemblyLoader).FullName);
				var assembly = loader.Load(path: @"C:\dev\BitBucket\Trash\Trash\ConsoleApp3\bin\Debug\ClassLibrary2.dll");
				var type = assembly.GetType(name: "ClassLibrary2.Test");
				var instance = Activator.CreateInstance(type);
				var methodInfo = type.GetMethod("TestMethod");
				var result = methodInfo.Invoke(instance, null);

				AppDomain.Unload(domain);
			}
		}
	}

	public class SimpleAssemblyLoader : MarshalByRefObject
	{
		public Assembly Load(string path)
		{
			ValidatePath(path);

			return Assembly.LoadFile(path);
		}

		private void ValidatePath(string path)
		{
			if (path == null) throw new ArgumentNullException(nameof(path));
			if (!System.IO.File.Exists(path))
				throw new ArgumentException($"path \"{path}\" does not exist");
		}
	}


namespace ClassLibrary2
{
    public class Test
    {
	    public string TestMethod()
	    {
		    return $"{GetType().FullName}";
	    }
    }
}