Seite 2 von 2

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 11.01.2017, 14:51
von xq
Das Problem ist mir noch nicht untergekommen, scheinbar ist dieser Fall abgedeckt.

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 11.01.2017, 21:41
von Krishty
Du hast recht:
https://blogs.msdn.microsoft.com/cbrumme/2003/05/06/asynchronous-operations-pinning/ hat geschrieben:Clearly the unmanaged function pointer must refer to a fixed address. It would be a disaster if the GC were relocating that! This leads many applications to create a pinning handle for the delegate. This is completely unnecessary. The unmanaged function pointer actually refers to a native code stub that we dynamically generate to perform the transition & marshaling. This stub exists in fixed memory outside of the GC heap.
Dann verdränge ich den Gedanken mal ganz schnell wieder aus meinem Kopf, und aus meinem Code … :)

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 08:40
von joggel
Ich habe gerade gesehen, dass ich das ja schon mache.

Code: Alles auswählen

        if (mCallback == NULL)
        {
                mCallback = new MyCallBack();
                mCallback->setCallBack(funcPtr); // <= hier übergebe ich ja den Funktionspointer der Klasse MyCallback.
        }

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 09:15
von xq
ja und der funktionspointer wird WO gespeichert? Es geht nicht darum, dass du die klasse, die den funktionspointern nutzt, vorm aufräumen schützt, sondern den funktionspointer an sich!

also schön brav funcPtr in eine member-variable von mCallback legen, am besten in mCallback::setCallbackFunc

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 09:18
von joggel
Aber das mache ich doch schon!!!
Über die Funktion setCallBack übergebe ich den Funktionspointer der Klasse MyCallback. Und diese bleibt am leben...

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 10:14
von joggel
Nachtrag:

Anscheinend funktioniert das aufrufen des Funktionspointer nicht richtig.

Code: Alles auswählen

void OPCDAGroup::enableAsynch(CallbackFunc^ theCallback)
{
        auto funcPtr = static_cast<FunctionPointer>(Marshal::GetFunctionPointerForDelegate(theCallback).ToPointer());

        if (mCallback == NULL)
        {
                mCallback = new MyCallBack();
                mCallback->setCallBack(funcPtr);
        }

        mGroup->enableAsynch(*mCallback);
// Ich habe mal testweise hier den Funktionspointer aufgerufen.
	array<OPCDAItemData^>^ arr = gcnew array<OPCDAItemData^>(10);
	funcPtr(arr); // <= Hier knallt es. also scheint dieser Funktionspointer nicht richtig zu sein!!!
//*******************************
        // Sorgt dafür, dass das Delegate nicht vorzeitig vom Garbage Collector eingesammelt wird
        GC::KeepAlive(theCallback);
}


Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 11:10
von joggel
Ich habe irgendwie das Gefühl, dass das hier nicht richtig ist

Code: Alles auswählen

typedef void(*FunctionPointer)(array<OPCDAItemData^>^ arr);
Das aufrufen des Delegate funktioniert ja, nur eben nicht das Aufrufen über den Funktionspointer :?

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 11:51
von xq
Die Doku besagt, dass ein native function pointer keine managed-objekte enthalten darf. Von daher solltest du das Array wohl als native Datentypen übertragen. Da musst du eventuell umbauen.

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 12.01.2017, 13:42
von joggel
Na ganz toll!!!! PRIMA!!!
Ich überlege schon die ganze Zeit wie ich das Problem jetzt löse, also wie ich das umbauen soll....und mir fällt da nix ein.
Ich habe es bis jetzt so gemacht, das ich eine unmanaged Struktur habe, die alle geänderten Items speichert in eine List.
Nun frage ich mich, wie ich das anstelle, dass bei diesem Event eine funktion der managed Klasse aufgerufen werden kann.
Dieser Funktion würde ich gerne diese Liste der unmanaged Items übergeben, welche ich ja in einer unmanaged klasse generiert habe.
Nur ist das Problem, dass ich aus einer unmanaged Klasse keine Funktion aufrufen kann einer managed Klasse.!!!!!
FUUUU....

Nachtrag:
Aber ich glaube, das hier ist so etwas was ich bräuchte.
https://msdn.microsoft.com/de-de/librar ... x#Anchor_1

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 13.01.2017, 08:19
von joggel
MISSION ACCOMPLISHED
34477803.jpg

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 14.01.2017, 09:59
von xq
Scheint wohl endlich zu klappen?!

Gratulation!

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 16.01.2017, 08:01
von joggel
Japp!! Hörnse uff...das war ein rumgehacke^^
Mein größtes Problem war ja, wie bekomme ich die unmanaged Datentypen in managed Code.
Ist jetzt etwas kompliziert zu erklären, aber nach einiger Googlezeit fand ich dann das hier:
Mist, finde den Link nicht mehr...
Dann halt etwas Code:

Eine Wrapper-Klasse die die umwandlung der unmanaged Daten in managed Daten macht.
Ich habe meine ganze Kreativität rausgelassen, und diese Klasse "Wrapper" genannt...

Code: Alles auswählen

class Wrapper
{
private:
	IntPtr m_handle;

public:
	Wrapper()
	{}

	~Wrapper()
	{
		static_cast<GCHandle>(m_handle).Free();
	}

	void makeManagedArray(OPCDAItemDataUnmanaged list[], const unsigned int& countListEntry)
	{
		array<OPCDAItemData^>^ arr = gcnew array<OPCDAItemData^>(countListEntry);

		for (int index(0); index < countListEntry; ++index)
			arr[index] = gcnew OPCDAItemData(list[index].mData, list[index].mName);

		safe_cast< CallbackFunc ^ >(static_cast<GCHandle>(m_handle).Target)(arr); // <= Diese Stelle ist das entscheidende! Von hier aus wird das Delegate (also C#-Funktion) aufgerufen
	}

	void setDelegate(CallbackFunc^ delegateFunc)
	{
		m_handle = static_cast<IntPtr>(GCHandle::Alloc(delegateFunc));
	}
};
Dann static Wrapper wrapper; im Header (öffentlicher Namensraum) um die Instanz in der Klasse zu verwenden, die als Callbackklasse von der Bibliothek genutzt wird.
Ich weiß jetzt garnicht ob das static erforderlich ist...

Und dann in dieser besagten Callback-Klasse:

Code: Alles auswählen

class MyCallBack : public IAsynchDataCallback
{
public:
	void setDelegate(CallbackFunc^ delegateFunc)
	{
		wrapper.setDelegate(delegateFunc);
	}

	void OnDataChange(COPCGroup &group, CAtlMap<COPCItem*, OPCItemData*> &changes)
	{
		array<OPCDAItemData^>^ arr = gcnew array<OPCDAItemData^>(changes.GetCount());
		POSITION pos;
		COPCItem* key;
		OPCItemData* value;
		pos = changes.GetStartPosition();

		int index(0);

		OPCDAItemDataUnmanaged* list = new OPCDAItemDataUnmanaged[changes.GetCount()];

		while ( pos != NULL )
		{
			key = changes.GetKeyAt(pos);
			value = changes.GetNextValue(pos);
			list[index] = OPCDAItemDataUnmanaged(value, key->getName());
			++index;
		}
		wrapper.makeManagedArray(list, changes.GetCount());
	}
};
Und Voila. Jochen hat sich gefreut wie sonstwas....

Hintergrund sollte ja der sein, dass eine C#-Funktion immer dann aufgerufen werden muß wenn sich Werte auf dem OPC-Server ändern.
Die C++-Bibliothek biedet dazu ein Callback-Klassen-Interface; also davon erben, Methode implementieren, und instanz der Klasse an die Bibliothek übergeben. Ganz leicht.
Nun aber sollte aus dieser Callback-Klasse ja ein Delegate aufgerufen werden....und dies gestaltete sich echt als schwierig, da ja zB in einer unmanaged Klasse keine managed Klassenhandler oder Delegates erlaubt sind. Deswegen der Umweg über diesen....naja...."Pointer"(?)...und Wrappen.

Jau. Danke noch mal für die Hilfe...


Nachtrag:
Ich sehe gerade, dass ich mir wahrscheinlich diese statische Instanz der Wrapper-Klasse im header hätte sparen können.
Ich könnte sie evtl auch als Member in MyCallback packen.
nur frage ich mich, wieso ich das nicht gemacht habe.... :?:

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 17.01.2017, 08:39
von joggel
Nachtrag:
Oh man...ich habe es mir ja so umständlich gemacht, und vollkommen...blöd.
*peinlich*

Hier mal das vereinfachte Vorgehen:

Code: Alles auswählen

class MyCallBack : public IAsynchDataCallback
{
private:
	IntPtr mHandle;
public:
	void setDelegate(CallbackFunc^ delegateFunc)
	{
		mHandle = static_cast<IntPtr>(GCHandle::Alloc(delegateFunc));
	}

	void OnDataChange(COPCGroup &group, CAtlMap<COPCItem*, OPCItemData*> &changes)
	{
		array<OPCDAItemData^>^ arr = gcnew array<OPCDAItemData^>(changes.GetCount());
		POSITION pos;
		COPCItem* key;
		OPCItemData* value;
		pos = changes.GetStartPosition();

		int index(0);
		while ( pos != NULL )
		{
			key = changes.GetKeyAt(pos);
			value = changes.GetNextValue(pos);
			arr[index] = gcnew OPCDAItemData(value, key->getName());
			++index;
		}
		safe_cast<CallbackFunc^>(static_cast<GCHandle>(mHandle).Target)(arr);
	}
};
Ja...was soll ich sagen, manchmal baue ich ganz schön mist im code... :?

Mal zusammenfassen was *ich* hier mache.
Dieses GCHandle::Alloc(Object) reserviert einen Speicherbereich im managed-Speicher für ein Object und gibt ein Handle darauf zurück, und verhindert natürlich das es vom GC eingesammelt wird...
Und zu dieser Zeile:
safe_cast<CallbackFunc^>(static_cast<GCHandle>(mHandle).Target)(arr);
Dieses Target:
MSDN hat geschrieben:Ruft das Objekt ab, das von diesem Handle dargestellt wird, oder legt dieses fest.
Es wird also das Delegate aufgerufen und das managed Array übergeben...
:)

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 17.01.2017, 12:40
von xq
Mal ne ganz blöde frage: Warum wandelst du das GCHandle in einen IntPtr um und wieder zurück? Das könnte man auch noch weg lassen...

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 17.01.2017, 13:42
von joggel
Warum? Na damit du blöden fragen stellen kannst^^

Aber ja, stimmt. Ich kann auch gleich ein GCHandle als Member verwenden.
Habe es gerade geändert...

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 17.01.2017, 16:43
von biertrinker
Ich versteh gar nicht, wozu du überhaupt ein GCHandle brauchst. Wenn du das Delegate als Member speicherst, hat der Garbage Collector eine Referenz darauf, die so lange gültig ist wie die Lebensdauer der MyCallback Instanz.

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 20.01.2017, 07:55
von joggel
Sorry, ich war unpässlich die letzten 2 Tage...
biertrinker hat geschrieben:Ich versteh gar nicht, wozu du überhaupt ein GCHandle brauchst. Wenn du das Delegate als Member speicherst, hat der Garbage Collector eine Referenz darauf, die so lange gültig ist wie die Lebensdauer der MyCallback Instanz.
Ich kann das Delegate nicht als Member speichern. Zumindest sagt mir das der Compiler. Kannst es mal probieren....

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 20.01.2017, 09:09
von xq
Welche Fehlermeldung kommt?

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 20.01.2017, 09:14
von joggel
VC hat geschrieben:ein Member einer nicht-verwaltet-Klasse kann kein Handle sein

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 20.01.2017, 09:21
von xq
Kannst du die klasse nicht einfach verwaltet machen? Würde mit C++/CLI native Klassen soweit es geht vermeiden

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 20.01.2017, 09:33
von joggel
Nein, kann ich nicht. Ich übergebe ja eine Instanz davon einer C++-Bibliothek.
Aber ist ja auch nicht sooo schlimm. Ist halt nur ein kleiner Umweg so ;)

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 13.02.2017, 10:50
von joggel
Und wieder ein Problem vor dem ich stehe!!

Wenn ich mein Projekt, welches ja eine managed Bibliothek werden soll, mit Debug erstelle, funktioniert alles gut.
Wenn ich das Projekt aber als Releas erstellen will, sagt mir VS:
Fehler LNK2038 Konflikt ermittelt f³r "RuntimeLibrary": Der Wert "MT_StaticRelease" stimmt nicht mit dem Wert "MD_DynamicRelease"
Wieso funktioniert das im Debug-Mode, aber nicht im Release-Mode?
Die Bibliothek die ich verwenden möchte, wird für statisches Linken erstellt.
Ich würde sie ja auch dynamisch als DLL erstellen, nur wird mir NUR eine DLL ausgespuckt, und die kann ich ja nicht in meinem Projekt verwenden; zumindest braucht man doch da eine LIB-File, oder irre ich mich??

Re: [gelöst]C#-Callback-function in C++/Cli

Verfasst: 13.02.2017, 11:32
von joggel
Ehm...ich weiß nicht was ich da versucht habe zu erstellen, aber jetzt geht es.
Also letzter Post kann ignoriert werden^^