Autor Beitrag
asoechting
Hält's aus hier
Beiträge: 7



BeitragVerfasst: Di 27.07.10 18:58 
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):

ausblenden volle Höhe 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()
{
        //Hier wird das Callback in bei der unmanaged lib registriert, Deklaration CallbackType sieht so aus:
        // typedef void(__cdecl *  CallbackType )(IconBuffer *buffer, FrameGrabber *fg, void *userdata) 
        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)
        {
            //TODO
            Console.Writeln("Aufruf managed Callback.");
        }


    }


Weiterhin weiß ich noch nicht, wie ich passend für den in der unmanaged Bibliothek definierten Typ

ausblenden 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 user profile iconKha: C#-Tags hinzugefügt
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 27.07.10 19:18 
:welcome:

Wäre die falsche Aufrufkonvention denn nicht schon Grund genug für einen Laufzeitfehler?
msdn.microsoft.com/e...ointerattribute.aspx

_________________
>λ=
asoechting Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: 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:

ausblenden volle Höhe 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)
        {
            //TODO
            Console.WriteLine("Profil wird verarbeitet.");
        }

        #endregion

    }
}


Moderiert von user profile iconKha: C#-Tags hinzugefügt
asoechting Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: 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?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4807
Erhaltene Danke: 1061

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 28.07.10 16:52 
Hallo,

zu deiner letzten Frage kann ich dir allgemein auch nur dieselbe Antwort wie hier (www.c-sharp-forum.de....php?p=612446#612446) geben: Marshal-Klasse bzw. MarshalAs (u.a.) Attribute
asoechting Threadstarter
Hält's aus hier
Beiträge: 7



BeitragVerfasst: 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#:

ausblenden 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)
            {
                //Verarbeitung
                this.saveProfile(new double[MAX_RESOLUTION], profileBuffer, (uint)len);
                this.scanEvent.OnProfileReceived(thisnew ScannerEventArgs(this.profiles[this.profiles.Count - 1]));
            }
        }


C++:

ausblenden 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)
{

    //Auslesen der Daten und Speichern im Feld
    //...
}

Moderiert von user profile iconKha: C#-Tags hinzugefügt