Callback und Interop



  • Hallo,

    ich bin am verzweifeln. Ich versuche in einer verwalteten Klasse "Form1" (für mein GUI) eine Callbackfunktion an eine nicht verwaltete Klasse zu übergeben. Das geht soweit auch, aber der Aufruf der Callbackfunktion in der nicht verwaltete Klasse führt zum Fehler:

    Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

    Alles an Quelltext hat als standard "calling convention" __cdecl eingestellt.

    Ich habe den Code nur von MSDN (dort lese ich seit Stunden) abgeschrieben und eingebettet (vielleicht ist hier das Problem). Hier im Forum hat mir noch kein Artikel geholfen.

    Hier eine paar Code-Ausschnitte (Kann man hier Anhänge an Artikel hängen? Dann könnte ich kurz das ganze VC-Projekt hochladen.):
    logger.h (alles nicht verwaltet)

    (...)
    
    // Callbacktyp definieren
    typedef void (__stdcall *logFunctionPointer)(std::string); // muss laut MSDN __stdcall sein
    (...)
    
    class LOGGER_API Logger {
    private:
    	logFunctionPointer logFunktion;
    
    public:
    	Logger(logFunctionPointer logfkt);
    
    	void status();
    	void tueNix();
    };
    

    logger.cpp (alles nicht verwaltet)

    (...)
    Logger::Logger(logFunctionPointer logfkt)
    {
    	logFunktion = logfkt;
    	return;
    }
    
    void Logger::status() {
    	(*logFunktion)("Hallo");    // hier der Fehler s.o.
    	return;
    }
    

    Form1.h (gemischt verwaltet)

    (...)
    #include "logger.h"
    
    namespace logTest {
    
    	using namespace System;
             (...)
    
    	public delegate void LogItDelegate(String^ msg);
    
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	private:
    		static int staticCounter = 0;
    		Logger* myLogger;
    
    	public:
    		LogItDelegate^ myLogItD;
    
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Konstruktorcode hier hinzufügen.
    			//
    
    			myLogItD = gcnew LogItDelegate(this, &Form1::logIt);
    			myLogger = new Logger(static_cast<logFunctionPointer>(Marshal::GetFunctionPointerForDelegate(myLogItD).ToPointer()));
    
    			logIt("logIt: gestartet");        // geht
    			myLogItD("myLogItD: gestartet");  // geht
    			myLogger->tueNix();               // geht
    			myLogger->status();               // Fehler in Funktion
    		}
    (...)
    
    	public:
    		void logIt(String^ msg) {
    			staticCounter++;
    			textBox1->AppendText(staticCounter + ": " + msg + Environment::NewLine);
    		}
    
    (...)
    

    Ich benutze Win XP 32 mit .NET 2.0 und das VS2005. Ich schreibe eigentlich in C++ und muss aber jetzt die GUI in .NET (also über C++/CLI) machen. Das ist Vorgabe des Projektes. Ich würde deshalb gern meinen Programmcode und die GUI komplett trennen und alles nur über Funktionsaufrufe in beiden Richtungen regeln. Deshalb der Callback. Übrigends scheint alles andere zu funktionieren.

    Danke schon mal.
    Gerrit



  • Hallo,

    hab leider nicht viel Zeit...
    Wahrscheinlich wird der callback mir __cdecl aufgerufen.
    Dann musst Du deiner delegate defninition ein Attribut spendieren

    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] 
    public delegate void LogItDelegate(String^ msg);
    

    Probier das mal!

    Die Zeile irritiert mich noch ein wenig.

    typedef void (__stdcall *logFunctionPointer)(std::string); // muss laut MSDN __stdcall sein
    


  • Danke, aber das war wohl nicht des Rätsels Lösung.

    Ich konnte das Problem ein wenig weiter eingrenzen. Zunächst habe ich eine zweite Callbackfunktion nach gleichem Schema erstellt. Diesmal jedoch ganz ohne Parameter: Funktioniert einwandfrei.
    Dann habe ich herausgefunden, dass der String "Hallo"

    (*logFunktion)("Hallo");
    

    beim Aufruf der Funktion Logger::status() erstellt aber nicht ordnungsgemäß übergeben wird. Besser gesagt: Der Parameter "msg" der Funktion logIt(String^ msg) hat einen undefinierten Wert. Ich glaube also, dass es beim Marshalling oder so irgend einen Fehler gibt.
    Das Kapitel über Marshallen von Strings aus dem MSDN hat mir noch keine Erleuchtung gebracht. Vielleicht weiß hier jemand Rat.

    Danke Gerrit



  • Ich habe nochwas rausgefunden:

    typedef void (__stdcall *logFunctionPointer)(std::string);
    

    Diese Zeile habe ich mal in

    typedef void (__stdcall *logFunctionPointer)(char*);
    

    geändert.

    Dann geht alles. Einfach so. Also muss es mit dem "std::string" zusammen hängen. Aber wieso und wie geht das richtig?


Anmelden zum Antworten