Laden...

Problem bei DLLImport mit events

Erstellt von thaukko vor 19 Jahren Letzter Beitrag vor 19 Jahren 2.463 Views
T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren
Problem bei DLLImport mit events

Hallo,

ich möchte eine DLL(LibPl.dll) welche in C++ geschrieben ist, in C# benutzen. Ich habe die Methode mit DLLImport eingebunden.



[DllImport(DllFileName)]
internal static extern uint PL_new_term_refs(int n);


Wenn ich diese Methode (PL_new_term_refs) in meine Hauptprogramm benutze, funktioniert alles wunderbar.
Aber wenn ich genau den selben Code mittels einem Event/Delegate aufrufe funktioniert dies nicht.
Dann bekomme ich nämlich folgenden Error:


An unhandled exception of type 'System.NullReferenceException' occurred
in aflib.dll

Additional information: Object reference not set to an instance of an
object.

Unhandled Exception: System.NullReferenceException: Object reference
not set to an instance of an object.
at ArteFACT.Rule.libpl.PL_new_term_refs(Int32 n)


Es scheint, dass die Method gar nicht gefunden wird.

Hat jemand eine Idee was das sein könnte? Hab so ziehmlich alles versucht zu debuggen was ging und im Netz hab ich auch nichts gefunden.

Würde mich wirklich über Hilfe freuen,

Chris

S
8.746 Beiträge seit 2005
vor 19 Jahren

Hast du mal versucht eine Funktion "vorzuschalten", die im Delegaten zu registrieren und von dort den DLL-Aufuruf zu machen?

Zeig uns doch mal den aufrufenden Code!

T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren

Es ist schon ein Methode vorgeschaltet. Um alle Aufrufe aus der DLL ist ein Wrapper drum gebaut. Ich rufe in beiden Fällen (einmal mittels delegate und einmal im normalen Main-Thread) die selbe Wrapper Methode auf.

Die Methody "query" ist die Wrapper Methode.


public AFReplyMessage query(AFQueryMessage query)
{		
...
	
    PlTermv av = new PlTermv(numberOfArgs);
    PlQuery q = new PlQuery(query.BodySimulated.Name, av);
...

}

Der Konstruktor PLTermv(int n) sieht folgendermaßen aus:



public PlTermv(int n)
		{
			int numberOfParameter = n;
			m_a0   = libpl.PL_new_term_refs(numberOfParameter);
			m_size = n;
		}



Also ruft der Konstruktor die native Methode PL_new_term_refs auf.

Und ich rufe sowohl mit dem delegate wie auch im Main Thread die Methode
query auf. Die Methode query ist eine Methode der Klasse SWIRuleEngine. Diese SWIRuleEngine ist eine static Attribut einer anderen Klasse (Artefact), so dass bei beiden Aufrufen sogar das gleich Objekt benutzt wird.
Das seltsame ist, dass ich mit diesem Objekt im MainThread keine Probleme habe, aber wenn ich es mittels delegate aufrufe, bekomme ich eine Nullpointer-Exception. Wenn ich dann das ganze direkt nochmal im MainThread aufrufe geht es allerdings wieder. Sehr seltsam.

S
8.746 Beiträge seit 2005
vor 19 Jahren

Sieht gut aus. Hast du nochmal den Delegaten-Code? Da muss der Fehler liegen.

Hast du eine NullPointer-Prüfung vor dem Delegatenaufruf?

T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren

Das ist das Registrieren der Delegate Methode:


_commChannel.MessageReceived += new MessageReceivedHandler(HandleQueries);

Der _commChannel ruft dann das MessgeReceived auf, wenn er eine Nachricht empfängt.

Das ist die Definition des events:


public event MessageReceived
		{
			add
			{
				Debug.WriteLine("UdpParticleChannel.MessageReceived.add");
				Debug.Indent();
				//we do it that way  guarantee that we first register MessageReceivedHandlers
				bool registerPacketReceived = false;
				if (msgReceivedStorage == null)
				{
					registerPacketReceived = true;
				}
				msgReceivedStorage += value;
				if (registerPacketReceived)
				{
					ps.PacketReceived += packetReceivedHandler;
				}
				Debug.Unindent();
			}
			remove
			{
				Debug.WriteLine("UdpParticleChannel.MessageReceived.remove");
				Debug.Indent();
				lock (this)
				{
					msgReceivedStorage -= value;
					if (msgReceivedStorage == null)
					{
						ps.PacketReceived -= packetReceivedHandler;
					}
				}
				Debug.Unindent();
			}
		}

Die add und remove Methode hab ich halt selber geschrieben wegen des ps.PacketReceived. Der value wird dem "private MessageReceivedHandler msgReceivedStorage" zugewiesen.

Das ist die Defintion des Delegates:


public delegate void MessageReceivedHandler(AFCommChannel channel,
MessageInfoEventArgs messageInfo);

Und das ist die HandleQueries, die weiter oben registriert worden ist:


private void HandleQueries(AFCommChannel channel, MessageInfoEventArgs messageInfo)
{
//build query to query kb 
AFQueryMessage query = new AFQueryMessage();
AFReplyMessage reply = Artefact.SendQueryToSimulatedArtefact(query);
} 

Die Methode SendQueryToSimulatedArtefact sieht folgendermaßen aus:


public static AFReplyMessage SendQueryToSimulatedArtefact(AFQueryMessage queryMsg)
		{
			//Artefact.engine.update();
			//Artefact.engine = new SWIRuleEngine();
			AFReplyMessage afReply;
			
			try
			{
				afReply = Artefact.engine.query(queryMsg);
			}
			
			catch
			{
				Console.WriteLine("Null Pointer by KB.");
				return null;
			}
			return afReply;
		}


Die Methode Artefact.engine.query sieht so aus:



public AFReplyMessage query(AFQueryMessage query)
		{			
			...

			PlTermv av = new PlTermv(numberOfArgs);			
                        
                        ...

                 }

Und die Termv dann so:



public class PlTermv
	{

public PlTermv(int n)
		{
			int numberOfParameter = n;
			m_a0   = libpl.PL_new_term_refs(numberOfParameter);
			m_size = n;
		}
}


Und da kracht es dann. Wie gesagt, wenn ich das alles genauso aufrufe aus dem Main-Thread, dann geht alles.
Der gleiche event mit einem ählnlichen delegate Methode verwende ich auch öfter und dann ohne Probleme. Also weiß ich nicht genau, ob es an dem Delegate selbst liegt.

4.221 Beiträge seit 2005
vor 19 Jahren

Ist es richtig dass dies im HauptThread abläuft ?

_commChannel.MessageReceived += new MessageReceivedHandler(HandleQueries);

Ich vermute, dass der Api-Function ein Delegate übergeben wurde, welches nicht im selben thread erstellt wurde wie der Aufruf....

müsste man da nicht für die api-function auch einen Delegate erstellen und diesen dann invoken, so dass der Aufruf der Api-Function in dem Thread erfolgen kann, in welchem der MessageReceived-Delegate erstellt wurde (im Hauptthread)

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren

>Ist es richtig dass dies im HauptThread abläuft ?
>_commChannel.MessageReceived += new MessageReceivedHandler(HandleQueries);

Jup, ich denke schon. Das wird mittels eines Buttons im Main GUI Thread gesetzt.

>Ich vermute, dass der Api-Function ein Delegate übergeben wurde, welches nicht im >selben thread erstellt wurde wie der Aufruf....
>müsste man da nicht für die api-function auch einen Delegate erstellen und diesen >dann invoken, so dass der Aufruf der Api-Function in dem Thread erfolgen kann, in >welchem der MessageReceived-Delegate erstellt wurde (im Hauptthread)

Mmmh, ich weiß nicht genau was du damit meinst, aber warum muss der Aufruf der Api-Funktion (du meinst damit doch libpl.PL_new_term_refs(numberOfParameter)) im selben Thread sein wie der MessageReceived-Delegate?
Und selbst wenn ich für die Api-Funktion einen weiteren delegate baue, dann wird die doch immer noch nicht sicher im Hauptthread ausgeführt. Oder verstehe ich da was falsch?

4.221 Beiträge seit 2005
vor 19 Jahren

Ich bin mir auch nicht sicher.... aber ich kanns auch nicht ausprobieren ...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

S
8.746 Beiträge seit 2005
vor 19 Jahren

Sieht so aus, dass im Add das lock(this) fehlt. In jedem Fall sind die beiden Accesor-Methoden nicht ganz sauber synchronisiert.

T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren

Beim Added ist es ja auch nicht so schlimm, wenn dieser Teil von einem anderen Thread unterbrochen werden. Dann kann es höchstens passieren, dass ein event verpasst wird, aber sonst nichts.

Ich habe jetzt das einen das ganze mal mit einem anderem delegate getestet, den ich in einer Test Klasse geschrieben habe und da hat das dann auch alles funktioniert.

Ich hab langsam echt keine Ahnung mehr was das sein soll.

S
8.746 Beiträge seit 2005
vor 19 Jahren

Hier nochmal was zu Delegates und Multi-Threading. Der Abschnitt über die Accessoren ist interessant. Du benutzt ja den +=-Operator, anstelle von Combine(). Vielleicht liegt auch hier ein Problem.

Aber generell müßte ja dann der Delegaten-Aufruf bereits fehlschlagen. Bei dir wird ja wohl noch die unmanaged Methode erreicht, bzw. erst deren Aufruf schlägt fehl (hast du mal geprüft, ob die Methode nicht doch noch eingesprungen wird und erst nach Rückkehr der Fehler erfolgt?). Sehr rätselhaft.


Q What is the proper way to raise an event in C#? I've heard that I need to be concerned about multithreading issues when doing so.

A If an event in C# has no delegates registered with it, attempting to raise the event will cause a NullReferenceException. As a result, given an event declared as

public event EventHandler MyEvent;

you'll often see it raised with code such as:

if (MyEvent != null) MyEvent(this, EventArgs.Empty);

This works fine in a single-threaded environment, but consider the scenario in which multiple threads are accessing MyEvent simultaneously. In such a case, one thread could check whether MyEvent is null and determine that it isn't. Just after doing so, another thread could remove the last registered delegate from MyEvent. When the first thread attempts to raise MyEvent, an exception will be thrown. A better way to avoid this scenario is shown in the following code snippet:

void MyEventInvoke(object sender, EventArgs args) {
EventHandler ev = MyEvent;
if (ev != null) ev(sender, args);
}

Whenever a delegate is added to or removed from an event using the default implementations of the add and remove accessors, the Delegate.Combine and Delegate.Remove static methods are used. These methods return a new instance of a delegate, rather than modifying the ones passed to it. In addition, assignments of object references in .NET are thread-safe, and the default implementations of the add and remove event accessors are synchronized. As such, the previous code succeeds by first copying the multicast delegate from the event to a temporary variable. Any changes to MyEvent after this point will not affect the copy you've made and stored. You can now safely test whether any delegates were registered and subsequently invoke them.

As an aside, the RaiseEvent keyword in Visual Basic handles the null check for you and, as a result, developers using Visual Basic don't first need to test whether any delegates are registered with an event before raising it in a single-threaded scenario.

T
thaukko Themenstarter:in
6 Beiträge seit 2005
vor 19 Jahren

>Hier nochmal was zu Delegates und Multi-Threading. Der Abschnitt über die >Accessoren ist interessant. Du benutzt ja den +=-Operator, anstelle von Combine(). >Vielleicht liegt auch hier ein Problem.
>Aber generell müßte ja dann der Delegaten-Aufruf bereits fehlschlagen.
Genau das denke ich auch.

>Bei dir wird ja wohl noch die unmanaged Methode erreicht, bzw. erst deren Aufruf >schlägt fehl (hast du mal geprüft, ob die Methode nicht doch noch eingesprungen wird >und erst nach Rückkehr der Fehler erfolgt?). Sehr rätselhaft.

Ich denke nicht, dass es bei der Rückker ist, weil im Debugger der Error direkt auftritt und ich genau den gleichen Aufruf aus dem Main Thread ja machen und dort keinen Nullpointer erhalte.

Man man, bin ich hier am verzeifeln 🙂