Entwickler-Ecke
Basistechnologien - C# Delegate an C++ unmanaged Code als Callback übergeben
asoechting - Di 27.07.10 18:58
Titel: C# Delegate an C++ unmanaged Code als Callback übergeben
Hallo an alle Leser dieses Forums!
Ich versuche einen C# Delegate über eine managed C++ Wrapper-Klasse an eine unmanaged C++ Bibliothek zu übergeben, die den Delegaten als Callback Funktion aufruft. Leider kommt es beim Verlassen des Gültigkeitsbereiches meiner C# Callback-Funktion zu einem Laufzeitfehler (Win 32 Ausnahme). Kann hier trotz des noch gültigen GCHandle-Objektes eventuell der Funktionszeiger durch den Garbage Collector abgeräumt werden? Was könnte noch die Ursache für so einen Fehler sein?
Ausschnitt der Datei Ranger_D.h (managed):
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70:
| public ref class Ranger_D { public: void setCallbackPointer(IntPtr ptr);
protected: IntPtr callback_ptr; GCHandle gch;
}
Ausschnitt der Datei Ranger_D.cpp (managed):
void Ranger_D::setCallbackPointer(IntPtr ptr) { callback_ptr = ptr; }
int Ranger_D::initFrameGrabber() { FrameGrabber::CallbackType cb = static_cast<FrameGrabber::CallbackType>(this->callback_ptr.ToPointer()); prms->setCallback(cb);
}
Ausschnitt der Datei SickRangerD.cs:
namespace ScannerLibrary.Impl { public class SickRangerD : LaserScannerDevice {
#region attributes protected delegate void ProfileReceiveMethod(IntPtr iconBuffer, IntPtr frameGrabber, uint userData); protected ProfileReceiveMethod profile_handler = null; protected Ranger_D ranger; protected GCHandle gch;
#endregion
public SickRangerD() { profile_handler += new ProfileReceiveMethod(ProfileEvent); init(); }
private void init() { ranger = new Ranger_D(); ProfileReceiveMethod^ fp = gcnew ProfileDelegate(ProfileEvent); gch = GCHandle.Alloc(fp); ranger.setCallbackPointer(Marshal.GetFunctionPointerForDelegate(fp));
} protected void ProfileEvent(IntPtr iconBuffer, IntPtr frameGrabber, uint userData) { Console.Writeln("Aufruf managed Callback."); }
} |
Weiterhin weiß ich noch nicht, wie ich passend für den in der unmanaged Bibliothek definierten Typ
C#-Quelltext
1:
| typedef void(__cdecl * CallbackType )(IconBuffer *buffer, FrameGrabber *fg, void *userdata) |
die Argumente für meine C# Funktion definieren soll. Mit IntPtr lässt es sich erstmal compilieren, jedoch kann ich IntPtr nicht nach FrameGrabber* konvertieren, wollte ich irgendwelche Eigenschaften dises Parameters abfragen.
Gibt es außerdem noch eine Möglichkeit die Aufrufkonvention des C# Delegaten von __stdcall nach __cdecl zu ändern?
Vielen Dank im Vorraus für jede Hilfe!
Moderiert von
Kha: C#-Tags hinzugefügt
asoechting - Mi 28.07.10 14:55
Hallo,
ich habe das ganze in einem kleinen Beispielprojekt nachgebaut und die Situation mit der Aufrufkonvention getestet.
Hier kommt eine ganz andere (und konkrete) Fehlermeldung, weswegen ich vermute, dass es an etwas anderem liegt.
Da ich beim Schreiben der Nachricht nicht die ganz aktuelle Version des Codes gepostet habe, hier das ganze nochmal als aktuelle Version:
Ranger_D.h:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124:
| #pragma once
#include <iostream> #include <iomanip> #include <string>
#include "icon_api.h" #include "ethernetcamera.h" #include "framegrabber.h"
using namespace std; using namespace icon; using namespace System; using namespace System::Runtime::InteropServices;
namespace Sick{
public ref class Ranger_D { public: Ranger_D(void); ~Ranger_D(void);
public:
void setCallbackPointer(IntPtr functionPointer); int initFrameGrabber(); protected: IntPtr callback_ptr; }; }
Ranger_D.cpp:
Ranger_D::Ranger_D(void) { }
Ranger_D::~Ranger_D(void) { }
void Ranger_D::setCallbackPointer(IntPtr functionPointer) { this->callback_ptr = functionPointer; }
int Ranger_D::initFrameGrabber() { FrameGrabber::CallbackType cb = static_cast<FrameGrabber::CallbackType>(this->callback_ptr.ToPointer()); prms->setCallback(cb);
prms->setUserData(NULL);
return 0; }
SickRangerD.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices;
using Sick;
namespace ScannerLibrary.Impl { public class SickRangerD : LaserScannerDevice {
#region attributes protected delegate void ProfileReceiveMethod(IntPtr iconBuffer, IntPtr frameGrabber, uint userData); protected ProfileReceiveMethod profile_handler = null; protected Ranger_D ranger;
#endregion
#region constructors
public SickRangerD() { profile_handler += new ProfileReceiveMethod(ProfileEvent); init(); }
#endregion
#region protected and private methods
private void registerCallback() { ranger.setCallbackPointer(Marshal.GetFunctionPointerForDelegate(profile_handler)); }
private void init() { ranger = new Ranger_D(); this.registerCallback(); }
protected void ProfileEvent(IntPtr iconBuffer, IntPtr frameGrabber, uint userData) { Console.WriteLine("Profil wird verarbeitet."); }
#endregion
} } |
Moderiert von
Kha: C#-Tags hinzugefügt
asoechting - Mi 28.07.10 15:11
Hallo,
ich nehme alles zurück!!! Jubelschrei, es geht!!! Und es lag doch an der Aufrufkonvention. Ich hatte tagelang in Foren gesucht und einen Beitrag gefunden, der beschreibt, wie man die Aufrufkonvention über die Modifikation des MSIL Code ändert. Das hatte ich probiert und war nicht weitergekommen. Außerdem hatte ich bei meinem kleinen Testprojekt eben eine ganz andere und klare Fehlermeldung bekommen, als ich die Aufrufkonvention absichtlich verkehrt gesetzt habe. Vielen Dank für den Tipp!
Jetzt lautet mein nächstes Problem, wie kann ich die Parameter, die die Funktion ProfileEvent erhält nutzen? Eigentlich sind dies Zeiger auf Objekte. Ich kann die Parameter in C# aber nicht so deklarieren, dass als Parameter Zeiger dieser Objekttypen angegeben sind, da C# diese Datentypen gar nicht kennt. Gibt es irgendeine Möglichkeit, die IntPtr in Zeiger auf ein Objekt umzuwandeln oder bin ich da auf dem Holzweg?
asoechting - Do 29.07.10 09:53
Hallo,
ich habe eine ganz einfache Lösung gefunden. Ich rufe eine Funktion in meiner Wrapper-Klasse auf, die IntPtr als Argument annimmt und diesen dann in einen Zeiger des gewünschten Typs umwandelt. Damit rufe ich dann wiederum meine private Funktion auf:
C#:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| protected void ProfileEvent(IntPtr iconBuffer, IntPtr frameGrabber, uint userData) { Console.WriteLine("Profil wird verarbeitet.");
int ret = 0; int len; double[] profileBuffer = new double[MAX_RESOLUTION];
unsafe{ fixed (double* buffer_ptr = profileBuffer) { ret = ranger.fetchProfile(buffer_ptr, &len, iconBuffer.ToPointer()); } }
if (ret == 0) { this.saveProfile(new double[MAX_RESOLUTION], profileBuffer, (uint)len); this.scanEvent.OnProfileReceived(this, new ScannerEventArgs(this.profiles[this.profiles.Count - 1])); } } |
C++:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| int Ranger_D::fetchProfile(double* profile, int &len, void* objPtr) { IconBuffer* iconBufPtr = static_cast<IconBuffer*>(objPtr);
return getProfileData(profile,len,iconBufPtr); }
int Ranger_D::getProfileData(double* profile, int &len, IconBuffer* &bufferPtr) {
} |
Moderiert von
Kha: C#-Tags hinzugefügt
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!