Native C++-DLL samt Pointer in C# verwenden



  • Hallo Leute,

    ich habe eine in C++ geschriebene DLL, die ich aus C# heraus ansprechen möchte.

    Hier die C++ Funktion:

    bool OpenSDTFile(char *sFileName, char *sFileInfoString)
    	{
    		pSDTIO = new SDTIO();
    
    		bSDTFileOpened = false;
    		if (!pSDTIO->ReadInfo(sFileName, &pSDTFileInfo))
    			return false;
    
    		// any scan in image found?
    		for (int iInd=0; iInd<pSDTFileInfo->iNumSPCDataBlocks; iInd++)
    		{
    			if (pSDTFileInfo->pSPCData[pSDTFileInfo->pSPCDataBlockInfo[iInd].iSPCDataIndex].mode == 2)
    			{
    				bSDTFileOpened = true;
    				break;
    			}
    		}
    
    		sFileInfoString = pSDTFileInfo->sInfoString;
    		// MessageBox(NULL, sFileInfoString, "TEST", NULL);
    		return true;
    	}
    
    	char * TestDll()
    	{
    // 		char* buffer = new char[1024];
    // 		sprintf(buffer, "DLL is answering");
    		return "DLL is answering";
    	}
    

    Wie zu sehen ist, gibt es einen Rückgabewert, einen übergebenen Dateinamen (aus C# heraus) und eine Variable (Pointer), die übergeben und befüllt wird (sFileInfoString)

    So sieht meint C#-Teil bislang aus:

    [DllImport("SDTProcessingDll.dll")]
            unsafe public static extern bool OpenSDTFile(void* sFileName, void* sFileInfoString);
    
            public static IntPtr OpenSDTFile(string _sFileName)
            {
                unsafe
                {
                    IntPtr refPtr = IntPtr.Zero;
                    IntPtr filePtr = IntPtr.Zero;
    
                    try
                    {
                        //convert from string to pointers
                        filePtr = Marshal.StringToHGlobalAnsi(_sFileName);
    
                        //alloc mem for system pointer
                        refPtr = Marshal.AllocHGlobal(IntPtr.Size);
    
                        //call external method
                        bool bSuccess = OpenSDTFile(filePtr.ToPointer(), refPtr.ToPointer());
    
                        //check for errors
                        if (!bSuccess)
                        {
                            throw new Exception("FlioAnalyzer: Error happened while using Dll");
                        }
    
                        //unreference system pointer
                        return Marshal.ReadIntPtr(refPtr);
                    }
                    finally
                    {
                        //clean up memory
                        if (refPtr != IntPtr.Zero)
                            Marshal.FreeHGlobal(refPtr);
                        if (filePtr != IntPtr.Zero)
                            Marshal.FreeHGlobal(filePtr);
                    }
                }
            }
    
            [DllImport("SDTProcessingDll.dll")]
            public static extern string TestDll();  
    
            private void button4_Click(object sender, EventArgs e)
            {
                System.Console.WriteLine(TestDll()); 
    
                string filepath = "G:\\Taumap\\3_Zenger_Anita\\01122012_11-23-58\\Measurement.sdt";
    
                try
                {
                    MessageBox.Show(Marshal.PtrToStringAnsi(OpenSDTFile(filepath)));
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
    
            }
    

    Es ist nun so: Das Übergeben des Dateinamens funktioniert einwandfrei, habe ich getestet, indem ich den String sFileInfoString aus der DLL heraus einmal habe anzeigen lassen. Es gelingt mir nun aber nicht, den String in C# zu verarbeiten, ich bekomme nur kryptische Zeichen.

    Ich bin für jede Hilfe und Fragen dankbar 🙂

    Zusammengebaut habe ich das Ganze anhand dieses Artikels:
    http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx

    Vielen Dank Leute!!!



  • Edit: Leider Blödsinn geschrieben 😉



  • das ist natürlich eine große Hilfe.



  • Neuer Versuch:

    [DllImport("SDTProcessingDll.dll",CharSet=Charset.Ansi)] 
    public static extern bool OpenSDTFile(string sFileName, IntPtr sFileInfoString);
    
    IntPtr sFileInfoStringPtr=IntPtr.Zero;
    OpenSDTFile("bla.dat",sFileInfoStringPtr);
    string sFileInfoString=Marshal.PtrToStringAnsi(sFileInfoStringPtr);
    

    Man könnte die C-Funktion auch ändern, so dass Pointer gar nicht direkt benötigt werden:

    bool OpenSDTFile(char *sFileName, char *sFileInfoString)
    // ändern zu:
    bool OpenSDTFile(char *sFileName, char *sFileInfoString, DWORD *sFileInfoStringLength)
    
    // ...
    
    if (lstrlen(pSDTFileInfo->sInfoString)>&sFileInfoStringLength)
    {
       // Übergebener Puffer zu klein,
       // benötigte Größe setzen:
       &sFileInfoStringLength=lstrlen(pSDTFileInfo->sInfoString);
       return false;
    }
    else
    {
       // Puffer groß genug, String kopieren:
       StringCchCopy(sFileInfoString , pSDTFileInfo->sInfoString, &sFileInfoStringLength);
    }
    

    und:

    [DllImport("SDTProcessingDll.dll",CharSet=Charset.Ansi)] 
    public static extern bool OpenSDTFile(string sFileName, StringBuilder sFileInfoString, ref int sFileInfoStringLength);
    
    // ...
    
    StringBuilder sFileInfoString=new StringBuilder(1024);
    int sFileInfoStringLength=sFileInfoString.Capacity;
    if (OpenSDTFile("filename",sFileInfoString,ref sFileInfoStringLength))
    {
       // Alles klar, mit sFileInfoString.ToString() ist man schon fertig 
    }
    else
    {
       // Capacity von sFileInfoString war evtl. zu klein
       // benötigte Größe steht nun in sFileInfoStringLength
       // Capacity von sFileInfoString könnte nun vergrößert werden und 
       // OpenSDTFile neu aufgerufen werden
    }
    


  • OKay, schonmal vielen Dank, werde das ausprobieren.



  • Unabhängig davon fände ich es aber auch interessant, wie man das mit Pointern grundsätzlich so hinbekommen kann.

    Vor diesem Problem standen sicherlich schon einige, die mit C# und .NET Dinge einbinden wollten, die man mit C# nicht ohne weiteres direkt hinbekommt, mit C++ hingegen schon.

    Vielen Dank für Eure Hilfe und Hinweise!!!

    Kennt vielleicht jemand auch ein ausführliches Tutorial?



  • Hallo Shiny86,

    Interop sollte man nur verwenden, wenn man beide Sprachen gut genug beherrscht.
    Dein C++ Code ist überarbeitungsbedürftig, z.B. bezüglich const-correctness.
    Außerdem macht die Zeile

    sFileInfoString = pSDTFileInfo->sInfoString;
    

    nicht das, was du dir anscheinend erhoffst (man kann sie nämlich auch weglassen, hat denselben Effekt ;-).
    Und solange diese Fehler nicht behoben sind, brauchst du von C# erst gar nicht darauf zugreifen.



  • Wieso nicht?
    Ich habe den Pointer auf eine Adresse und möchte von dort auslesen und kann es eben nicht. Wäre ich Vollprofi und allwissend müsste ich ja nicht fragen 😉


Log in to reply