String an C-Bibliothek übergeben



  • nn schrieb:

    Du benutzt C++ Klassen falsch. Wenn ein C++ Objekt angelegt wird, wird erstmal der Speicher angelegt und dann der Konstruktor ausgeführt. Beim Abräumen wird erstmal der Destruktor (bzw. die Destruktoren bei Arrays) ausgeführt, dann der Speicher freigegeben. Das ist was new und delete bzw. Smartpointer machen. Dein calloc legt nur Speicher an, was bei einfachen C++ Klassen zufällig funktioniert, weil das Speicherlayout von C++ Klassen in diesem Falle so aussieht.

    Danke für die Aussage. Aber das kann man ja korrigieren.

    nn schrieb:

    Den Fehler 1 überträgst du jetzt auch noch auf C#. Du legst eine Klasse CElement in C++ an und eine in C#. Dann glaubst du das wäre das gleiche und schiebst da Zeiger hin und her. Das Problem ist nur, dass die C# Klasse ganz anders aufgebaut ist, außerdem erbt sie implizit von System.Object. Sie liegt in einem ganz anderen, verwalteten, Speicherbereich, wo der Garbagecollector sie beim Aufräumen auch noch verschieben darf.

    Du mischt also verwaltete und nicht verwaltete Objekte und machst darauf mit deinem calloc rum. Damit überschreibst du irgendwo Speicher. Wo irgendwo ist, hängt wahrscheinlich von Compiler- und Netframeworkversion ab.

    Aber genau das war ja die Frage. Kann ich die Klasse einmal schreiben und sowohl in C# und C++ nutzen? Wenn nein, gibt es überhaupt die Möglichkeit in C++ die Klasse anzulegen und an C# zu übergeben, ohne dass das im Speicher kopiert wird? Die Aussage "Nein, das geht nur mit struct" wäre zwar schade, aber dann kann ich ja z.B. in C# erst die Klasse erstellen und in dieser auf das Struct verweisen?



  • Jetzt funktioniert es zumindest schon teilweise... Die Anzahl nElement wird korrekt zurückgegeben.

    CElement.h und CElement.cs

    #ifndef CELEMENT_H
    #define CELEMENT_H
    
    struct TPoint{
    	double x;
    	double y;
    };
    
    class CElement{
    	public:
    		TPoint *P;
    
    		CElement();
    		~CElement();
    
    		void SetExternalNumber(unsigned long n);
    
    	private:
    		unsigned long ExternalNumber;
    };
    #endif
    

    Die CElement.cpp

    #include "CElement.h"
    
    CElement::CElement(){
    	this->ExternalNumber = 0;
    };
    
    CElement::~CElement(){};
    
    void CElement::SetExternalNumber(unsigned long n){
    	this->ExternalNumber = n;
    }
    

    Die MyDLL.cpp

    #include "CElement.h"
    #include <stdio.h>
    #include <string.h>
    
    extern "C" __declspec(dllexport) int RunMyDLL(char* filename, CElement** pElement, unsigned long *nElement){
    	unsigned long fnElement = 5;
    	CElement* fpElement = new CElement[fnElement];
    
    	for (int i = 0; i < fnElement; i++){
    		fpElement[i].SetExternalNumber(i);
    	}
    
    	*nElement = fnElement;
    	*pElement = fpElement; // Fehler, wenn nicht auskommentiert "System.NullReferenceException
    
    	return 0;
    }
    

    Und das C# Executable

    [DllImport("D:/C_Programme/C_Sharp_vs_CPP_01/MyDLL/MyDLL/x64/Debug/MyDLL.dll", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            static extern unsafe int RunMyDLL(string filename, CElement[] pElement, ulong* nElement);
    
            private void button1_Click(object sender, EventArgs e)
            {
                CElement[] pElement=null;
                ulong nElement = 0;
                RunMyDLL("C:/Test.dat", pElement, &nElement);
    
                Console.WriteLine("Value of nElement: " + nElement);
            }
    

    Hat man den Rückgabebefehl für pElement in MyDLL auskommentiert, wird beim Ausführen auf der Console wie erwartet "Value of nElement: 5" ausgegeben.

    Dass das so mit der Übergabe der Klasse nicht funktioniert ist mir klar, aber das ist ja die eigentliche Frage.



  • CJens (extern) schrieb:

    Aber genau das war ja die Frage. Kann ich die Klasse einmal schreiben und sowohl in C# und C++ nutzen?

    Im allgemeinen Fall nein. Im Sonderfall Windows Apps ja. Im normalen Windows kann man in C++ COM-Objekte erstellen und in C# benutzen, da werden aber intern auch noch irgendwelche Wrapper erstellt.

    CJens (extern) schrieb:

    Die Aussage "Nein, das geht nur mit struct" wäre zwar schade,

    Mit structs geht es so, wie oben von Dravere sehr schön beschrieben. Kein unsafe und keine Pointer auf C# Seite nötig, das ist falsch.

    Bleibt weiter die Frage, warum die Oberfläche alle diese Elemente kennen soll ? Warum reicht es nicht, aus der DLL Funktionen in der Form "GetFirstElement" und "GetNextElement" zu exportieren ? Die Daten (und die Zeiger) bleiben dann in der DLL und die Oberfläche kann sie durchlaufen z.B. um sie anzuzeigen.



  • nn schrieb:

    Mit structs geht es so, wie oben von Dravere sehr schön beschrieben. Kein unsafe und keine Pointer auf C# Seite nötig, das ist falsch.

    Ja, ich glaube ich werde bei structs bleiben.

    nn schrieb:

    Bleibt weiter die Frage, warum die Oberfläche alle diese Elemente kennen soll ? Warum reicht es nicht, aus der DLL Funktionen in der Form "GetFirstElement" und "GetNextElement" zu exportieren ? Die Daten (und die Zeiger) bleiben dann in der DLL und die Oberfläche kann sie durchlaufen z.B. um sie anzuzeigen.

    Das klingt interessant. Eigentlich genügt es, wenn die Routine die Oberflächen des Netzes, das sie einliest, zurückgibt. Das sind Informationen in Struct Format. Der Rest wird später für eine andere C++ DLL verwendet, darf aber nicht verloren gehen. Kann ich die Daten im Speicher parken um sie später in anderen Funktionen in der selben oder anderen DLLs weiter zu verarbeiten?



  • CJens schrieb:

    Kann ich die Daten im Speicher parken um sie später in anderen Funktionen in der selben oder anderen DLLs weiter zu verarbeiten?

    Das tust du doch schon. Was du mit calloc anlegst, bleibt bis zum Löschen mit free oder dem Ende des Programms erhalten. Du verlierst nur den Zugriff darauf, weil deine Zeiger lokale Variablen der Funktionen sind.


Anmelden zum Antworten